🔐Ciberseguridad

Nmap desde cero: cómo descubrir servicios expuestos y reducir la superficie de ataque

Felipe••12 min de lectura
Aprende a interpretar Nmap con un laboratorio real en Kali: comprueba puertos con ss y curl, y compara escuchar en 0.0.0.0 frente a 127.0.0.1.

Una API de desarrollo funciona en tu equipo. El navegador abre la página y las pruebas pasan. Pero hay otra pregunta que esas comprobaciones no responden: ¿desde dónde se puede conectar alguien a ese servicio?

Un proceso puede estar disponible únicamente para aplicaciones de la misma máquina o aceptar conexiones por sus interfaces de red. Cambiar esa decisión puede reducir exposición sin apagar la aplicación. Nmap ayuda a observar el resultado; para interpretarlo necesitamos combinar sus hallazgos con la configuración del servicio y el punto desde el que hacemos la prueba.

Este artículo parte de un laboratorio ejecutado en Kali Linux ARM64, dentro de UTM en un Mac. Creamos un servidor HTTP con datos ficticios, comprobamos su acceso desde Kali y desde el Mac, restringimos su dirección de escucha y repetimos las pruebas. No se escaneó la red completa ni se utilizaron objetivos públicos.

Superficie de ataque, servicios y puertos

La superficie de ataque reúne los puntos por los que un sistema recibe interacciones potencialmente hostiles: endpoints, interfaces de administración, entradas de archivos o servicios de red, entre otros. Aquí estudiaremos una parte concreta: un servicio HTTP sobre TCP.

Un servicio de red es un proceso que ofrece una función a otros procesos mediante comunicaciones de red. Para aceptar conexiones TCP, un servidor prepara un socket y escucha en una combinación de dirección y puerto. La dirección determina dónde recibe conexiones; el puerto permite distinguir ese servicio de otros que utilizan el mismo equipo.

Que 8080/tcp esté abierto no prueba una vulnerabilidad. Puede ser exactamente lo que necesita una aplicación. La decisión de seguridad requiere más contexto: quién necesita conectarse, qué autenticación existe, qué datos procesa el servicio y cómo se mantiene. Un servicio innecesario o accesible desde más lugares de los previstos sí merece revisión.

Este enfoque complementa la metodología de pentesting web: primero delimitamos y observamos; después evaluamos el significado de la evidencia.

Qué puede decir Nmap sobre un puerto

Nmap es una herramienta de descubrimiento y reconocimiento de redes. Puede examinar puertos y realizar sondeos para identificar servicios. No convierte automáticamente cada respuesta en una vulnerabilidad confirmada.

Para este laboratorio nos interesan tres estados:

EstadoInterpretación en un escaneo TCP
openHay una aplicación aceptando conexiones en el destino observado
closedEl destino responde, pero no hay una aplicación escuchando en ese puerto y dirección
filteredNmap no consigue determinar si está abierto porque el filtrado impide obtener una respuesta concluyente

El estado depende del origen, la ruta y el tipo de prueba. Un mismo servicio puede verse abierto desde un punto y filtrado desde otro. Nmap distingue también otros estados; esta tabla no pretende cubrirlos todos. Referencia oficial: estados de puertos.

No observamos filtered en este laboratorio. Lo explicamos para interpretar futuros resultados; no cambiamos reglas de firewall para fabricar ese escenario.

Preparar un laboratorio limitado y verificable

El laboratorio se realizó el 8 de noviembre de 2026 en una VM con Kali Linux ARM64, alojada en UTM sobre macOS. Se utilizaron Nmap 7.991, Python 3.14.8, curl 8.22.0 e iproute2 7.2.0. Estas versiones documentan el entorno de la prueba; no son requisitos exactos para repetir el ejercicio.

Comprobar herramientas, red y puerto

En una terminal de Kali:

uname -m
command -v nmap python3 curl ss

nmap --version
python3 --version
curl --version
ss -V

ip -brief -4 address
ss -ltn 'sport = :8080'

La arquitectura observada fue aarch64. Anota la IPv4 de la interfaz de la VM si después vas a realizar la prueba desde el Mac. No confundas esa dirección con la del host ni copies rangos de red como objetivos de escaneo.

En la última orden, -l selecciona sockets en escucha, -t TCP y -n evita resolver nombres. El filtro limita la consulta al puerto elegido. Manual de ss.

Antes de iniciar el servidor, ss mostró únicamente la cabecera: no había un socket TCP escuchando en 8080. Si en tu entorno aparece una fila, no detengas un servicio desconocido. Elige otro puerto libre y úsalo consistentemente en todo el ejercicio.

Comprobamos también el estado inicial con Nmap:

nmap -sT -Pn -n -p 8080 --reason 127.0.0.1

El resultado inicial fue closed, con motivo conn-refused. Veamos por qué el comando está deliberadamente acotado:

  • -sT pide un escaneo TCP connect: utiliza conexiones del sistema operativo y no requiere paquetes raw ni sudo en este entorno. Puede generar registros en el servicio.
  • -p 8080 limita los puertos examinados a uno.
  • -n evita consultas DNS de resolución de nombres.
  • -Pn omite el descubrimiento previo de hosts. Lo usamos porque el único objetivo es nuestro localhost conocido.
  • --reason añade la razón del estado informado.

-Pn no demuestra que un equipo esté activo. La línea Host is up puede reflejar esa suposición; hay que examinar las respuestas posteriores. Técnicas TCP, descubrimiento de hosts.

Crear únicamente contenido ficticio

En la misma terminal:

LAB_DIR=$(mktemp -d /tmp/nmap-lab.XXXXXXXX)
mkdir "$LAB_DIR/site"

cat > "$LAB_DIR/site/index.html" <<'HTML'
<h1>Laboratorio Nmap</h1>
<p>Solo datos ficticios.</p>
HTML

printf 'Directorio del laboratorio: %s\n' "$LAB_DIR"

Conserva esa terminal: allí seguirá definida LAB_DIR. Servimos exclusivamente site, no tu carpeta personal ni el repositorio de un proyecto. No coloques archivos .env, claves ni enlaces simbólicos dentro de ella.

El módulo http.server es apropiado para esta demostración, pero Python no lo recomienda para producción. El propósito es observar la escucha y las respuestas, no construir un servidor endurecido. Documentación oficial.

Escenario A: escuchar en todas las interfaces IPv4

Inicia el servidor en la primera terminal y déjalo ejecutándose:

python3 -m http.server 8080 \
  --bind 0.0.0.0 \
  --directory "$LAB_DIR/site"

--bind selecciona la dirección de escucha y --directory el directorio servido. Puedes comprobar las opciones disponibles con python3 -m http.server --help. Interfaz de línea de comandos de Python.

En una segunda terminal de Kali:

ss -ltn 'sport = :8080'

curl --noproxy '*' --include --silent --show-error \
  --max-time 3 http://127.0.0.1:8080/

La fila relevante de ss fue:

State  Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0      5            0.0.0.0:8080      0.0.0.0:*

Mira Local Address:Port. 0.0.0.0 en un binding IPv4 significa aceptar conexiones dirigidas a las direcciones locales de las interfaces; no es una lista de clientes autorizados ni significa automáticamente «publicado en Internet». La ruta y los filtros siguen importando. La columna Peer Address de un socket en escucha no demuestra que haya clientes conectados. Linux ip(7).

curl recibió HTTP 200 y el HTML ficticio. Este extracto omite cabeceras de fecha y longitud:

HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.14.8
Content-type: text/html

<h1>Laboratorio Nmap</h1>
<p>Solo datos ficticios.</p>

--noproxy '*' evita que la comprobación use un proxy configurado en el entorno; consultamos directamente el destino del laboratorio. Manual de curl. Nmap nos dirá si se puede conectar por TCP; curl añade una comprobación concreta del protocolo HTTP y del recurso /.

Escenario B: reconocer el servicio, no adivinarlo por el número

Repite el escaneo limitado:

nmap -sT -Pn -n -p 8080 --reason 127.0.0.1

Resultado observado:

PORT     STATE SERVICE    REASON
8080/tcp open  http-proxy syn-ack

¿Instalamos un proxy? No. Sin detección de versiones, la columna SERVICE puede proceder de la asociación del número de puerto con el catálogo de Nmap. El nombre no demuestra qué proceso está ejecutándose.

Ahora solicita detección del servicio:

nmap -sT -Pn -n -sV --version-light -p 8080 127.0.0.1

En esta ejecución obtuvimos:

PORT     STATE SERVICE VERSION
8080/tcp open  http    SimpleHTTPServer 0.6 (Python 3.14.8)

-sV envía sondeos para reconocer el protocolo y la aplicación a partir de sus respuestas. --version-light reduce la intensidad de esos sondeos; no convierte la operación en pasiva ni garantiza identificar todos los servicios. Detección de servicios y versiones de Nmap.

Aquí la identificación coincide con el servidor que iniciamos. En otro sistema, una cabecera puede estar modificada, un intermediario puede responder o una versión puede incorporar parches sin cambiar el identificador visible. Para evaluar una vulnerabilidad, contrasta producto, configuración, paquetes y aviso del fabricante; no derives un CVE únicamente de esta línea.

Terminal de Kali con ss mostrando 0.0.0.0:8080 y Nmap identificando el servidor HTTP de Python

Captura auténtica de la terminal de Kali mostrando las salidas guardadas durante el laboratorio. El servidor ya estaba detenido al tomar la captura; se conserva la fecha original del escaneo.

Escenario C: mantener el servicio, reducir su alcance

Vuelve a la primera terminal y detén únicamente el servidor de prueba con Ctrl+C. Inícialo de nuevo, cambiando solo el binding:

python3 -m http.server 8080 \
  --bind 127.0.0.1 \
  --directory "$LAB_DIR/site"

Repite desde Kali:

ss -ltn 'sport = :8080'

curl --noproxy '*' --include --silent --show-error \
  --max-time 3 http://127.0.0.1:8080/

nmap -sT -Pn -n -p 8080 --reason 127.0.0.1

La fila de escucha cambió:

LISTEN 0      5          127.0.0.1:8080      0.0.0.0:*

Y el escaneo de localhost siguió mostrando:

PORT     STATE SERVICE    REASON
8080/tcp open  http-proxy syn-ack

Ese open es el resultado correcto. El proceso continúa escuchando en loopback y curl dentro de Kali volvió a recibir HTTP 200. También repetimos -sV --version-light: identificó el mismo servidor HTTP de Python.

La comprobación desde el Mac hacia la IP privada de Kali pasó de HTTP 200 con el binding 0.0.0.0 a un fallo de conexión con 127.0.0.1. curl terminó con código de salida 7, sin respuesta HTTP. Ese error no equivale a una clasificación filtered de Nmap.

Comparar sin confundir alcance con disponibilidad

ComprobaciónBinding 0.0.0.0Binding 127.0.0.1
ss, dirección local0.0.0.0:8080127.0.0.1:8080
Nmap contra 127.0.0.1 desde Kaliopenopen
curl a localhost desde KaliHTTP 200HTTP 200
Detección -sV desde KaliHTTP, servidor de PythonHTTP, servidor de Python
curl desde el Mac hacia la IP privada de KaliHTTP 200Fallo de conexión, salida 7

Todos los resultados de esta tabla fueron observados. El cambio redujo el alcance del socket sin apagar la funcionalidad local. No modificamos autenticación, contenido ni firewall.

El experimento cubre IPv4. :: y ::1 pertenecen a IPv6; un despliegue real debe revisar ambas familias y sus ajustes. Tampoco extrapoles directamente a contenedores: su localhost pertenece a su espacio de red. Si trabajas con ellos, revisa además redes y publicación de puertos en Docker Compose.

Binding, firewall y segmentación resuelven problemas distintos

El binding elige las direcciones locales donde escucha el proceso. Un firewall permite o bloquea tráfico según su política. La segmentación separa zonas y ayuda a controlar qué comunicaciones pueden cruzarlas. Una VLAN por sí sola no expresa toda la política de autorización entre zonas: revisa también el enrutamiento y las reglas que lo acompañan.

Para aplicar lo aprendido, usaría este orden de revisión:

  1. Necesidad: elimina o detén servicios que no tengan una función justificada.
  2. Alcance: utiliza loopback cuando el único consumidor sea local. Si hay consumidores remotos, identifica interfaces y orígenes necesarios.
  3. Acceso: establece reglas de red acordes con esos consumidores y comprueba el acceso desde los puntos pertinentes.
  4. Privilegios: ejecuta el servicio con una identidad que solo pueda leer y modificar lo que necesita. No uses root por comodidad.
  5. Aplicación: aplica autenticación, autorización, actualizaciones y protección del transporte según el caso.
  6. Operación: registra responsable, finalidad, exposición prevista y fecha de revisión; repite la comprobación tras cambios relevantes.

Es una propuesta de trabajo, no una certificación de seguridad. Restringir el binding no evita que otro proceso local acceda al servicio, ni neutraliza por sí solo una SSRF, un túnel o un proxy que lo haga alcanzable por otra vía.

Para una API de producción, continúa con los controles esenciales de seguridad en Node.js. La protección de interfaces administrativas se desarrolla también en seguridad de switches y redes de gestión.

Cerrar el laboratorio y conservar lo que demuestra

En la primera terminal, detén el servidor con Ctrl+C. En la segunda, comprueba:

ss -ltn 'sport = :8080'
nmap -sT -Pn -n -p 8080 --reason 127.0.0.1

Al finalizar nuestra prueba, ss no mostró sockets en escucha en ese puerto y Nmap informó:

PORT     STATE  SERVICE    REASON
8080/tcp closed http-proxy conn-refused

Ahora sí dejó de existir la escucha local. No es lo mismo que restringirla a localhost.

Terminal de Kali con escucha en 127.0.0.1, puerto abierto desde localhost y resultado closed tras detener el servidor

La segunda captura reúne las salidas reales guardadas: primero el servicio restringido a loopback y, debajo, el resultado tras detenerlo. No son estados simultáneos.

En la primera terminal puedes retirar los archivos ficticios que creaste, conservando por separado las evidencias:

rm -- "$LAB_DIR/site/index.html"
rmdir -- "$LAB_DIR/site"
rmdir -- "$LAB_DIR"

Se eliminan archivos concretos y directorios vacíos, sin búsquedas amplias ni borrado de otros proyectos. En nuestra ejecución, los procesos de prueba quedaron detenidos, se retiró el directorio servido y se conservaron las salidas para revisión. La VM continuó encendida.

Lo importante es desde dónde responde

Un inventario de puertos cobra valor cuando puedes explicar cada resultado: qué proceso lo abre, quién necesita utilizarlo y desde qué lugar responde. En este laboratorio, localhost permaneció abierto después de reducir la exposición. Buscar simplemente «cero puertos abiertos» habría llevado a una interpretación equivocada.

La próxima vez que levantes un servicio, combina tres perspectivas: su socket con ss, su accesibilidad TCP con Nmap y una petición de aplicación con curl. Después comprueba el acceso desde el origen que realmente te interesa. Esa cadena de evidencias es más útil que convertir una etiqueta de escáner en un diagnóstico automático.

Referencias

#Nmap#Kali Linux#superficie de ataque#puertos TCP#binding#seguridad de redes

Artículos relacionados