Redes · Lección 01

TCP/IP, puertos y qué escucha de verdad

Casi todos los inventarios de superficie de ataque que he visto estaban mal por la misma razón: alguien listó los puertos que creía haber publicado en vez de leer los que la máquina tiene abiertos. Son cosas distintas, y la diferencia se mide en incidentes. Esta lección enseña a leer una tabla de sockets, a distinguir escuchar de ser alcanzable, y a saber qué deja cada tipo de conexión en el otro extremo. Al final vas a inventariar un laboratorio entero desde dentro y desde fuera, y a explicar cada diferencia.

Al terminar sabrás

  • Nombrar qué hace cada capa del modelo TCP/IP y en cuál actúa cada control de red.
  • Explicar la diferencia real entre TCP y UDP, y qué implica para un atacante.
  • Leer ss -ltnp y decir exactamente qué expone una máquina.
  • Separar escuchar, ser alcanzable y estar publicado, que casi nunca coinciden.
  • Distinguir un puerto cerrado de uno filtrado por la respuesta que da.
  • Entender qué hacen NAT y el encaminamiento con tu inventario de exposición.
Antes de empezar · qué sabes ya

Tres preguntas rápidas. No puntúan para completar la lección: sirven para que la explicación se ajuste a lo que ya traes.

¿Sabes qué diferencia hay entre TCP y UDP?

Ninguno de los dos cifra nada. La diferencia es el estado: TCP mantiene una conversación con acuse y reintento; UDP dispara y se olvida.

Un proceso escucha en 0.0.0.0:8080 dentro de un contenedor que no declara ports. ¿Llega tu navegador?

Escuchar en todas las interfaces es una afirmación relativa a la pila de red en la que vives. La del contenedor no es la tuya.

¿Qué identifica un socket?

Por eso mil clientes pueden hablar con el mismo puerto 443 sin chocar: lo que los distingue es la tupla completa, no el puerto.

0 / 3

1Por qué importa

La superficie de ataque de red de un sistema no es lo que dice el diagrama de arquitectura. Es la lista de sockets en estado de escucha, cruzada con quién puede llegar a cada uno. Esa lista se obtiene con un comando, cambia con cada despliegue y casi nunca coincide con lo que el equipo cree.

Los tres desajustes que más veces he visto son siempre los mismos. Un servicio de depuración que alguien levantó para una tarde y quedó escuchando en todas las interfaces. Una base de datos con el puerto publicado “para poder mirarla desde el portátil” que nadie retiró. Y un contenedor que expone un puerto sin que su servicio esté pensado para recibir tráfico de nadie. Ninguno de los tres aparece en un diagrama; los tres aparecen en la tabla de sockets.

Hay un motivo más profundo para empezar la fase aquí. Todo lo que viene después —DNS, HTTP, TLS, firewalls— son capas construidas encima de esto. Si no tienes claro qué es una conexión, qué la establece y qué la corta, las lecciones siguientes se convierten en recetas. Con el modelo claro, se convierten en consecuencias.

El objetivo real

El entregable de esta lección es un inventario: una tabla con protocolo, dirección de escucha, puerto, proceso y desde dónde es alcanzable. Esa última columna es la que convierte un listado técnico en una decisión de seguridad.

2Conceptos

El vocabulario mínimo
  • Capa de enlace: mueve tramas entre equipos del mismo segmento, por dirección MAC.
  • Capa de red (IP): mueve paquetes entre redes distintas; aquí vive el encaminamiento.
  • Capa de transporte: TCP y UDP; aquí aparecen los puertos y, en TCP, el estado de la conexión.
  • Capa de aplicación: HTTP, DNS, Postgres; el contenido que a la red le da igual.
  • Puerto: un número de 16 bits que multiplexa varias conversaciones sobre una misma dirección.
  • Socket: la tupla completa (protocolo, dirección local, puerto local, dirección remota, puerto remoto).
  • Escuchar: tener un socket en estado LISTEN esperando conexiones entrantes.
  • NAT: traducir direcciones y puertos al cruzar una frontera; el motivo de que “abierto” dependa de dónde preguntes.

3Explicación profunda

El modelo TCP/IP tiene cuatro capas y la utilidad de recordarlas no es académica: cada control de seguridad de red actúa en una capa concreta y es ciego a las demás. Un filtro que decide por dirección y puerto no sabe si dentro va HTTP o Postgres. Un proxy que entiende rutas HTTP no ve el encaminamiento. Cuando alguien dice “el firewall lo cubre”, la primera pregunta útil es en qué capa mira.

En la capa de transporte está la decisión que más consecuencias tiene. TCP establece una conexión con un intercambio de tres pasos: el cliente manda SYN, el servidor responde SYN-ACK y el cliente confirma con ACK. A partir de ahí hay una conversación con números de secuencia, acuse de recibo y retransmisión. UDP no tiene nada de eso: manda un datagrama y termina su trabajo.

Esa diferencia tiene tres consecuencias de seguridad que conviene tener memorizadas. La primera: en TCP, quien recibe sabe con quién habla, porque completar el intercambio de tres pasos exige recibir de vuelta el número de secuencia del servidor. En UDP no, y por eso la falsificación de origen es trivial y por eso los ataques de amplificación viven casi siempre ahí. La segunda: un filtro stateful puede seguir una conexión TCP y permitir sus respuestas sin abrir un rango entero; con UDP tiene que aproximar con tiempos de espera. La tercera: los escaneos se comportan distinto. Un puerto TCP cerrado contesta RST de inmediato, así que se distingue de uno filtrado, que no contesta nada. Un puerto UDP cerrado responde con un mensaje de control, y muchos sistemas limitan la tasa de esos mensajes, con lo que un escaneo UDP es lento y ambiguo.

El socket es el otro concepto que hay que tener sin nieblas. No es el puerto: es la tupla de cinco elementos. Un servidor con un solo socket en LISTEN sobre el puerto 443 mantiene simultáneamente miles de sockets establecidos, uno por cliente, todos con el mismo puerto local. Cuando miras una tabla de sockets tienes que separar mentalmente las dos poblaciones: las líneas LISTEN son tu superficie de ataque; las líneas ESTAB son tu tráfico.

Queda la parte que más confusión genera: la dirección de escucha. Un proceso puede escuchar en la dirección de loopback, en una interfaz concreta o en todas. Escuchar en loopback significa que solo los procesos de esa misma pila de red llegan; es la diferencia entre un servicio de administración razonable y un incidente. Escuchar en todas las interfaces significa todas las de esa pila de red, que en un contenedor no son las de tu máquina. Por eso publicar un puerto es un acto explícito y separado: sin ports en el compose, el proceso escucha y no lo alcanza nadie de fuera.

NAT y el encaminamiento completan el cuadro. Cuando publicas un puerto, Docker escribe una regla de traducción: lo que llega a una dirección y puerto del host se reescribe hacia la dirección y el puerto del contenedor. La consecuencia práctica es que abierto es una propiedad relativa al punto desde el que preguntas, y por eso el inventario tiene que hacerse desde los dos lados. Es también la razón de que NAT no sea una medida de seguridad: no filtra nada, solo cambia direcciones. Que durante veinte años haya funcionado como cortafuegos accidental no lo convierte en uno.

4Ejemplo sencillo

# Un edificio de oficinas

direccion IP     la calle y el numero del edificio
puerto           el numero de despacho dentro del edificio
socket           "despacho 443 del edificio X hablando con el despacho 51844 del edificio Y"

# Y los dos transportes

TCP   llamas al telefono: alguien descuelga, dice "si?", y sabes que
      te oye. Si se corta, los dos os enterais.
UDP   metes una carta por debajo de la puerta y te vas. Puede que
      llegue, puede que no, y puede que llegue despues de la siguiente.

# Tres estados que la gente confunde

ESCUCHA          hay alguien en el despacho esperando visitas
ALCANZABLE       ademas existe un pasillo desde donde tu estas
PUBLICADO        ademas hay un cartel en la puerta de la calle
Los tres estados de la última tabla son independientes. Un inventario que solo mira uno de ellos siempre miente en alguna dirección.

5Ejemplo técnico

Así se lee una tabla de sockets de verdad. Esta salida es del contenedor app del laboratorio de esta lección, y cada columna dice algo que importa:

qué escucha dentro del contenedor
ss -ltnp State Recv-Q Send-Q Local Address:Port Peer Address:Port Process LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1,fd=6)) LISTEN 0 511 [::]:80 [::]:* users:(("nginx",pid=1,fd=6)) ss -ltnp | wc -l 3

Dos líneas para un solo servicio, porque nginx abre un socket para cada familia de direcciones. La dirección local es la parte que decide la exposición: aquí es “todas las interfaces del contenedor”. Y la columna del proceso es la que convierte el hallazgo en una tarea asignable: sin ella tienes un puerto abierto y ningún responsable.

ComandoQué respondeQué NO responde
ss -ltnpQué procesos escuchan en esta pila de red y en qué direcciónQuién puede llegar hasta ellos
ss -tnpConexiones establecidas: con quién habla la máquina ahoraQué podría escuchar y no lo hace
docker compose psQué puertos están publicados hacia el hostQué escucha dentro sin publicar
nc -z destino puertoSi desde aquí se alcanza aquel socketSi se alcanza desde otro sitio
ip routePor dónde saldría un paquete hacia cada redSi algo lo va a filtrar por el camino

La tabla resume el método: ningún comando responde la pregunta completa. El inventario correcto se hace desde dentro del sistema y desde fuera, y las discrepancias entre las dos vistas son precisamente los hallazgos.

6Diagrama

Las cuatro capas TCP/IP y en cuál actúa cada control: filtrado por dirección en red, filtrado por puerto y estado en transporte, e inspección de contenido en aplicación aplicacion · HTTP, DNS, Postgres transporte · TCP, UDP, puertos red · IP, encaminamiento, NAT enlace · MAC, mismo segmento proxy: ve la ruta y la cookie firewall L4: ve puerto y estado segmentacion: ve o no ve la ruta ARP: aqui no hay autenticacion cada control es ciego a las capas de arriba
Lo que demuestra: no existe “el firewall lo cubre”. Cada control ve una capa y no puede opinar sobre lo que ocurre por encima de ella.
Ver el mismo diagrama como código Mermaid (editable)
flowchart LR
    A[aplicacion] --> PA[proxy: ruta y cookie]
    T[transporte] --> PT[firewall L4: puerto y estado]
    R[red] --> PR[segmentacion: hay ruta o no]
    E[enlace] --> PE[ARP: sin autenticacion]
    A --- T --- R --- E

7Código

Un inventario de exposición cabe en un script y en una tabla. Este recorre las dos vistas —desde dentro y desde fuera— y deja las discrepancias a la vista, que es lo único que hay que revisar a mano:

#!/bin/sh
# inventario-sockets.sh — que escucha, y desde donde se alcanza.
# Se ejecuta contra el laboratorio propio. Nada de esto apunta fuera.
set -eu

SERVICIOS="entrada app db cliente"

echo "== vista de dentro: sockets en escucha =="
for s in $SERVICIOS; do
  echo "--- $s"
  # -l solo LISTEN, -t TCP, -u UDP, -n sin resolver, -p con proceso
  docker compose exec -T "$s" ss -ltunp 2>/dev/null || echo "sin ss en la imagen"
done

echo
echo "== vista de fuera: lo que el host publica =="
docker compose ps --format "table {{.Service}}\t{{.Ports}}"

echo
echo "== alcanzabilidad real desde el puesto de trabajo =="
for destino in "app 80" "db 5432" "app 8080"; do
  set -- $destino
  if docker compose exec -T cliente nc -z -w 2 "$1" "$2" 2>/dev/null; then
    echo "ALCANZABLE  $1:$2"
  else
    echo "no          $1:$2"
  fi
done

Lo valioso del script no es ninguno de los tres bloques por separado: es el contraste. Un socket que aparece en la vista de dentro y no en la de fuera está correctamente contenido. Uno que aparece en las dos es superficie de ataque declarada. Y uno que resulta alcanzable desde un contenedor que no debería hablar con él es un hallazgo de segmentación, que es justo lo que la última lección de esta fase va a arreglar.

8Qué puede salir mal

Los cuatro errores clásicos

Publicar sin prefijo de interfaz. Escribir 8080:80 en lugar de 127.0.0.1:8080:80 publica el puerto en todas las interfaces del host. En una cafetería, tu laboratorio deliberadamente vulnerable acaba de ser el servicio más interesante de la red.

Confundir “no lo uso” con “no escucha”. Un servicio de depuración arrancado por una variable de entorno olvidada sigue en LISTEN aunque nadie lo llame. El inventario no pregunta por intenciones.

Creer que NAT protege. NAT traduce, no filtra. En cuanto existe una regla de publicación, o una conexión saliente que abre el camino de vuelta, el servicio está expuesto. Toda la sensación de seguridad de las redes domésticas viene de un efecto secundario, no de un control.

Inventariar solo TCP. ss -ltn omite UDP por completo. Un resolutor mal configurado o un servicio de métricas en UDP no aparecen, y son exactamente el tipo de cosa que se usa para amplificar.

9Cómo lo abusaría un atacante

Vista desde el otro lado

Lo primero que hace alguien que llega a una red no es explotar nada: es construir el mismo inventario que acabas de aprender a construir, pero desde fuera. Barre puertos, anota qué responde y clasifica cada respuesta. Un RST le dice “aquí hay una máquina y ese puerto está cerrado”, que ya es información: confirma el equipo. El silencio le dice “hay un filtro”, que también lo es.

La segunda cosa que busca es la asimetría entre vistas, porque es donde vive el descuido. Un servicio que no está publicado hacia internet pero sí es alcanzable desde cualquier contenedor del mismo segmento es el camino estándar del movimiento lateral. No hace falta romper nada: basta con haber llegado a un sitio cualquiera de esa red.

Y la tercera es el orden de los paquetes. Un escaneo que completa el intercambio de tres pasos deja una conexión aceptada y, casi siempre, una línea en el log de la aplicación. Uno que manda solo el SYN y abandona no llega a la aplicación y, en muchos sistemas, no deja rastro salvo que alguien esté mirando la capa de transporte. La lección defensiva está clara: si tu única fuente de detección son los logs de aplicación, eres ciego a la mitad del reconocimiento.

10Cómo defenderlo

Qué hacer con esto mañana
  • Escucha en loopback por defecto. Un servicio se ata a todas las interfaces cuando alguien escribe por qué en el ticket, no antes.
  • Publica con interfaz explícita. 127.0.0.1:puerto:puerto en todo lo que no sea deliberadamente público. Es una línea y evita una clase entera de incidentes.
  • Inventaría las dos vistas y guarda el resultado en el repositorio. Un cambio en esa tabla es un cambio de superficie de ataque y merece revisión.
  • Incluye UDP en cada inventario. Cuesta una letra en el comando y cubre la mitad que todo el mundo olvida.
  • Separa segmentos por función. Que el puesto de trabajo alcance la base de datos no es una comodidad, es el camino del movimiento lateral.
  • Registra a nivel de conexión, no solo de aplicación, si quieres ver el reconocimiento antes que la explotación.

11Laboratorio

Estado: laboratorio local lab-net-captura
Targethttp://localhost:8091
Autorizaciónsistema local creado por ti para entrenamiento
Escanear y capturar solo se hace aquí dentro. Un barrido de puertos contra una máquina que no es tuya es un delito en casi cualquier jurisdicción, y este laboratorio existe para que no te haga falta.

Levanta el laboratorio. La primera vez construye la imagen de herramientas:

cd cursos/seguridad/labs/lab-net-captura docker compose up -d --build docker compose ps

Ahora las dos vistas. Primero desde dentro de la aplicación, después desde el host, y fíjate en que no dicen lo mismo:

dentro contra fuera
docker compose exec sniffer ss -ltnp LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1,fd=6)) # el sniffer comparte la pila de red de app: esto son los sockets de app docker compose exec cliente ss -ltnp # vacio: el cliente no escucha nada, solo habla docker compose exec cliente nc -z -w 2 app 80; echo alcanzable=$? alcanzable=0 curl -m 3 -sS http://localhost:80/ curl: (7) Failed to connect to localhost port 80 # correcto: app escucha en el 80 de SU pila, no del host

Comprueba ahora la diferencia entre cerrado y filtrado, que es la que un atacante usa para dibujar el mapa. El puerto 5432 de db está abierto; el 3306 no existe en ninguna parte del laboratorio:

docker compose exec cliente nc -z -v -w 2 db 5432 docker compose exec cliente nc -z -v -w 2 db 3306 docker compose exec cliente ss -tnp

Genera tráfico contra el borde y mira la tabla de conexiones establecidas mientras ocurre. Cuando termines, destruye el laboratorio:

curl -s http://localhost:8091/ > /dev/null docker compose logs --tail 5 entrada docker compose down -v

12Ejercicios

Clasifica cinco líneas de socket

Para cada una, di si es superficie de ataque hacia el host, superficie interna o tráfico: (1) LISTEN 0.0.0.0:80 en un contenedor sin ports; (2) LISTEN 127.0.0.1:5432 en tu portátil; (3) ESTAB hacia el puerto 443 de otra máquina; (4) LISTEN 0.0.0.0:9229 en un contenedor con 9229:9229 publicado; (5) LISTEN [::]:53 en UDP.

Separa primero las líneas LISTEN de las ESTAB: solo las primeras son superficie.
Para cada LISTEN, pregunta desde qué pila de red se llega a esa dirección.
El caso (4) tiene dos cosas a la vez: una dirección de escucha permisiva y una publicación sin interfaz.
Solución

(1) superficie interna: escucha en todas las interfaces del contenedor, pero sin publicación no llega el host. (2) superficie muy reducida: solo procesos locales. (3) tráfico, no superficie. (4) superficie hacia el host y hacia la red local, y además es un puerto de depuración, que suele significar ejecución de código. (5) superficie interna en UDP, y es la línea que un inventario con ss -ltn no habría visto.

Clave: interna · local · tráfico · externa y grave · interna en UDP. El ejercicio está bien resuelto si en el (4) mencionas las dos cosas.

Publica la base de datos y mide el daño

En el lab-net-captura, añade a db una publicación de puerto en la interfaz de loopback. Comprueba que ahora se alcanza desde el host. Después cámbiala por la forma sin interfaz y explica, con un comando que lo demuestre, qué cambió. Revierte las dos cosas.

El servicio db del compose no tiene bloque ports. Añádelo y vuelve a levantar con up -d.
Para ver la diferencia entre las dos formas no mires el contenedor: mira la tabla de sockets del host.
En el host, ss -ltn muestra la dirección en la que Docker ha atado el puerto publicado. Compárala antes y después.
Solución
# forma correcta: solo tu maquina
  db:
    ports:
      - "127.0.0.1:5432:5432"

# forma peligrosa: toda la red local (NO dejarlo asi)
  db:
    ports:
      - "5432:5432"

Con la primera forma, la tabla de sockets del host muestra el puerto atado a la dirección de loopback y nadie más de la red llega. Con la segunda aparece atado a todas las interfaces: cualquier equipo del mismo wifi puede intentar autenticarse contra tu Postgres, que además tiene la contraseña escrita en claro en el compose.

Clave: la demostración vale más que la explicación. Se pide un ss -ltn del host antes y después, y que revirtieras ambos cambios.

Reconstruye el intercambio de tres pasos

Captura una conexión completa contra app desde el contenedor sniffer e identifica, en la salida de tcpdump, los tres paquetes del establecimiento y los que cierran la conexión. Después responde: ¿por qué a veces ves cuatro paquetes de cierre y a veces tres? Necesitarás mirar documentación fuera de esta lección.

tcpdump imprime las banderas entre corchetes. Busca [S], [S.] y [.].
El cierre usa FIN y RST, y no todos los clientes cierran igual: HTTP con conexiones persistentes cambia el final.
Empieza por docker compose exec sniffer tcpdump -i any -c 20 tcp port 80 y lanza el curl desde otra terminal.
Solución

El establecimiento son siempre tres: SYN, SYN-ACK, ACK. El cierre limpio son cuatro, porque cada lado cierra su mitad por separado: FIN y ACK en un sentido, y lo mismo en el otro. Se ven tres cuando el ACK del primero viaja junto al FIN del segundo, y se ve un cierre abrupto con RST cuando alguien corta sin negociar.

La consecuencia de seguridad es que el número de paquetes no es un detalle: un filtro con estado tiene que decidir cuánto tiempo recuerda una conexión medio cerrada, y ese temporizador es un recurso finito que se puede agotar a propósito.

Clave: tres para abrir, tres o cuatro para cerrar según se combinen las banderas, y uno solo si hay RST. La parte evaluable es que expliques por qué el cierre es bidireccional.

Red Team · dibuja el mapa desde dentro

Ponte en el papel de alguien que acaba de conseguir ejecución en el contenedor cliente y no sabe nada más. Solo dentro del lab-net-captura: enumera qué otros equipos existen en su red, qué puertos tienen abiertos y qué servicio parece correr en cada uno. Escribe el mapa resultante y señala cuál sería tu siguiente objetivo y por qué.

Sin lista de objetivos, lo primero es averiguar qué nombres existen. El compose te los da, pero el atacante no lo tiene: prueba a resolver nombres de servicio.
Para los puertos basta con nc -z en un bucle; no hace falta ninguna herramienta especializada.
Empieza por docker compose exec cliente sh -c 'for p in 22 53 80 443 5432 8080; do nc -z -w 1 db $p && echo abierto $p; done' y repítelo por cada nombre.
Solución

El mapa que sale es: entrada y app con el 80 abierto, db con el 5432, y nada más. El siguiente objetivo razonable es db, y no por ser una base de datos sino por dos motivos concretos: es el único servicio con estado persistente, y su credencial suele estar escrita en el entorno de otro contenedor al que quizá ya tienes acceso.

Fíjate en lo que no hizo falta: ninguna vulnerabilidad. Todo el reconocimiento se hace con permisos legítimos de red, y por eso la defensa contra esta fase no es parchear, es segmentar.

Clave: el entregable es el mapa más la justificación del siguiente salto. Guárdalo: en f02-firewall vas a cortar exactamente estos caminos.

Blue Team · qué dejó el barrido en los logs

Repite el barrido del ejercicio anterior con el laboratorio levantado y después busca rastro en docker compose logs. Responde: ¿cuáles de tus intentos dejaron una línea y cuáles no? ¿Qué fuente de datos necesitarías para ver los que faltan?

Los logs de nginx registran peticiones HTTP, no conexiones TCP. Piensa qué pasa cuando abres y cierras sin enviar nada.
Postgres sí escribe algo cuando una conexión se corta antes de completar el saludo del protocolo. Compáralo con nginx.
La fuente que falta está una capa más abajo: mira lo que ve el sniffer mientras haces el barrido.
Solución

Un nc -z contra nginx abre y cierra sin enviar una petición, así que el log de acceso no registra nada; a lo sumo aparece una línea en el log de error. Postgres es más ruidoso y suele dejar constancia de una conexión incompleta. Los intentos contra puertos donde no escucha nadie no dejan absolutamente nada, porque no hay proceso que pueda escribirlo.

La fuente que falta es la capa de transporte: registro a nivel de conexión, sea del filtro, del nodo o de una captura. Sin ella, todo un barrido de reconocimiento es invisible para tu pila de observabilidad.

Clave: la respuesta correcta es “casi nada, y esa es la noticia”. El entregable es la lista de fuentes que harían visible el reconocimiento.

Architecture Challenge · el inventario de exposición como control

Diseña cómo integrarías el inventario de sockets en un sistema real para que detecte cambios de superficie de ataque. Define: dónde se ejecuta, con qué frecuencia, contra qué línea base se compara, qué diferencia bloquea un despliegue y cuál solo avisa, y cómo evitas que el ruido lo vuelva inútil en tres semanas.

Un inventario que nadie compara con nada es una foto bonita. Lo que aporta valor es la diferencia contra un estado esperado.
Piensa qué cambios son sospechosos siempre y cuáles son normales en despliegues legítimos: un puerto nuevo no es lo mismo que un puerto que cambia de dirección de escucha.
Si la línea base vive en el repositorio, la revisión de un cambio de exposición ocurre en la revisión de código, que es donde hay contexto.
Solución

Una propuesta razonable: la línea base es un archivo versionado con la tabla esperada de sockets por servicio. El inventario se ejecuta en el pipeline contra el entorno recién levantado y también de forma periódica en producción. Bloquea cuando aparece un socket que no está en la línea base, o cuando uno pasa de loopback a todas las interfaces, porque ambas cosas son cambios de exposición. Avisa cuando desaparece un socket esperado, que suele ser un servicio caído y no un problema de seguridad.

Contra el ruido, dos decisiones de diseño: normalizar puertos efímeros y conexiones establecidas fuera del inventario, y exigir que cualquier entrada nueva en la línea base lleve un campo con el motivo. Ese campo es lo que hace que la revisión sea una conversación y no un sello.

Clave: no hay una única respuesta. Se evalúa que distingas bloqueo de aviso, que la línea base viva junto al código y que hayas pensado en el ruido antes de que aparezca.

13Preguntas de comprensión

Comprobación

¿Qué te dice ss -ltnp dentro de un contenedor que no te dice docker compose ps?

El compose describe la intención declarada; la tabla de sockets describe el estado real. Las discrepancias entre ambas son el hallazgo.

Intentas conectar a un puerto TCP y recibes un RST inmediato. ¿Cómo está ese puerto?

El RST confirma que la máquina existe. El silencio es lo que indica un filtro, y por eso descartar revela menos que rechazar.

¿Por qué el curso escribe siempre 127.0.0.1:8091:80 y nunca 8091:80?

La forma corta expone el laboratorio a toda la red local. Es una diferencia de trece caracteres entre practicar y ser un problema para terceros.

Un escaneo que completa el intercambio de tres pasos deja en el objetivo…

Por eso existen los escaneos que no completan la conexión: dejan menos rastro justo en la fuente que casi todo el mundo monitoriza.

0 / 4

14Reto adicional

Inventaría tu propia máquina de desarrollo

Ejecuta el inventario de sockets contra tu equipo de trabajo, no contra el laboratorio. Lista todo lo que escucha en direcciones que no son de loopback, identifica el proceso responsable de cada línea y decide, para cada una, si debería seguir ahí. No cambies nada todavía: escribe la tabla y la decisión.

Necesitas permisos elevados para ver la columna de proceso en los sockets que no son tuyos.
Incluye UDP. La mitad de las sorpresas de este ejercicio están ahí.
Los sospechosos habituales son servidores de desarrollo con recarga automática, demonios de sincronización y herramientas de contenedores.
Solución

No hay una respuesta única, pero sí un patrón que se repite: la mayoría de las máquinas de desarrollo tienen entre tres y diez servicios escuchando en todas las interfaces sin que nadie lo haya decidido. Los más comunes son servidores de desarrollo que se atan a todas las interfaces por comodidad, demonios de compartición de archivos y puertos de depuración de intérpretes.

Lo interesante del ejercicio no es la lista, es la pregunta que la acompaña: cuando trabajas desde una red que no controlas, esa lista es exactamente lo que ofreces. Un servidor de desarrollo con recarga automática suele leer y escribir archivos de tu proyecto sin autenticación de ningún tipo.

Clave: el entregable es la tabla con la columna de decisión. Si en alguna fila no sabes qué proceso es, esa fila es el hallazgo.

15Resumen

Puntos clave
  • Cada control de red actúa en una capa y es ciego a las de arriba. “El firewall lo cubre” no significa nada sin decir en qué capa mira.
  • TCP mantiene estado y confirma el origen; UDP no. De ahí salen la falsificación de origen, la amplificación y la dificultad de filtrar UDP con precisión.
  • Un socket es la tupla completa, no el puerto. Las líneas LISTEN son superficie de ataque; las ESTAB son tráfico.
  • Escuchar, ser alcanzable y estar publicado son tres cosas distintas, y el inventario hay que hacerlo desde dentro y desde fuera.
  • Un puerto cerrado responde RST; uno filtrado calla. La respuesta que das es información que regalas.
  • NAT traduce, no filtra. La sensación de protección que da es un efecto secundario, no un control.
¿Qué distingue dos conexiones simultáneas al mismo puerto 443 de un servidor?
La tupla completa del socket: protocolo, dirección y puerto de los dos extremos. El puerto local es el mismo en las dos.
Un servicio escucha en todas las interfaces dentro de un contenedor sin ports. ¿Está expuesto al host?
No. “Todas las interfaces” son las de la pila de red del contenedor. Publicar es un acto explícito y separado.
¿Por qué la falsificación de origen es fácil en UDP y difícil en TCP?
Porque completar el intercambio de tres pasos de TCP exige recibir de vuelta el número de secuencia del servidor, y quien falsifica el origen no lo recibe.
¿Qué diferencia hay entre descartar y rechazar un paquete?
Rechazar responde y confirma que la máquina existe; descartar calla y solo revela que hay un filtro. Cuesta tiempo de espera al que escanea.

16Mis notas

Cierre de la lección

Estas notas se guardan solo en este navegador. No se envían a ningún servidor. Expórtalas desde el panel de progreso si cambias de máquina.

17Recursos

Los comentarios se cargan solo si los pides, desde GitHub Discussions. No se carga nada de terceros mientras no pulses.