sábado, 30 de enero de 2021

Implementar los escaneos de puertos UDP y SYN Stealth (TCP) | Scapy @Ubuntu 18.04

Todos los resultados obtenidos por los scripts de escaneo de puertos que se mostrarán en lo que sigue serán comparados con los de Nmap para el mismo tipo de escaneos.

Antes de nada, mencionar que es necesario instalar el software Scapy. Para ello, se precisa usar $ sudo apt install python-scapy. Una vez instalado puede lanzarse ejecutando $ sudo scapy, aunque no trabajaremos de ese modo en esta ocasión.

Escaneos de puertos UDP y SYN Stealth

Como se menciona en el título, se desarrollarán dos scripts de escaneo. Uno basado en el envío de paquetes UDP, y otro en el envío de paquetes TCP con host discovering (Ping ICMP).

Escaneo UDP (sin retransmisiones)

Este tipo de escaneos se basa en el envío de paquetes del protocolo UDP para deducir si el host destino está disponible y cuál es el estado de los puertos a escanear. Normalmente, estos paquetes carecerán de datos y únicamente transportarán unos pocos bytes de cabecera. Sin embargo, existen algunos puertos comunes para UDP para los que sí que se envían ciertos datos específicos.

Estados del puerto según la respuesta - UDP Scan

La información necesaria para conocer el estado del host destino y sus puertos reside en el tipo de respuesta y en determinados campos de la cabecera ICMP (si es el caso). A continuación, se muestra una tabla extraída de la documentación de Nmap que relaciona el contenido de dichas respuestas con el estado de un puerto:

Figura 1. Estados según la respuesta del sondeo UDP

El estado abierto se asigna a los puertos cuyo sondeo ha recibido una respuesta UDP. Un puerto estará cerrado si la respuesta recibida es un paquete ICMP de tipo 3 y código 3 (error de puerto inalcanzable) o filtrado si es de tipo 3 y alguno de los códigos 1, 2, 9, 10 ó 13. Puede ver más sobre la relación de tipos y códigos ICMP en https://rlworkman.net/howtos/iptables/spanish/chunkyhtml/a4189.html.

Por último, aquellos puertos que no responden de ningún modo, incluso tras el envío de varias retransmisiones, son clasificados como abiertos o cerrados. Muchos firewalls descartan paquetes sin responder, por lo que no se puede determinar si el puerto destino está abierto o filtrado.

Imaginemos que se envía un paquete UDP al puerto 53 de un host y este lo descarta. Entonces no habrá respuesta de ningún tipo (UDP o ICMP), así que no sabremos con certeza si se encuentra abierto o filtrado.

Es un caso particularmente difícil de comprender, así que veámoslo del siguiente modo:
  1. Si no se recibe respuesta ICMP ni UDP -> el puerto ha recibido el paquete pero lo ha descartado, pero no sabemos por qué razón...
  2. Esta ausencia de respuesta nos impide determinar si el puerto está abierto, pero ignorando nuestras emisiones (porque rara vez responden a paquetes vacíos), o filtrado.
Una buena manera de solucionar este problema de determinación sería enviar sondas con datos específicos para el servicio UDP concreto que se aloje en el puerto destino que ya haya sido marcado como abierto o filtrado. De este modo esos puertos podrían interpretar el paquete como válido y, a no ser que estén filtrados, responderán, actualizando su estado a abierto.

Código en Python - udp-scan.py

Comprendido lo anterior, es momento de pasarlo a código:

Figura 2. Código Python del escaneo UDP

Ejemplo de uso - udp-scan.py

Para poder ejecutar este script es necesario ubicarlo dentro del directorio /usr/lib/python2.7/dist-packages/scapy, en el cual se encuentran todos los archivos .py de Scapy que necesitamos para poder realizar las importaciones pertinentes. Para lanzarlo ejecutamos el comando python upd-scan.py.

Figura 3 - Prueba de sondeo UDP en Scapy (Linux)

Tras ejecutar udp-scan.py, indicándole ip de destino y puerto/s de destino, se muestra un breve informe sobre el estado de cada uno de los puertos. Para el caso, el estado de los puertos es abierto/filtrado, es decir, no ha habido respuesta y no se puede determinar (caso 2 en la tabla de la figura 1).

Para asegurar el correcto funcionamiento de esta sonda se ha realizado la misma prueba desde la herramienta Nmap: nmap -sU -p 53 193.136.28.31.

Figura 4. Prueba de sondeo UDP en Nmap

Nuevamente, el estado del puerto es abierto/filtrado, por lo que justificamos el buen funcionamiento de nuestro script. En este caso se indica también que "1 host up", es decir, ha detectado que el equipo en esa dirección está disponible. Esto es porque Nmap ejecuta una fase de 'host discovering' antes del sondeo UDP. Nuestro script no hace eso. Puede deshabilitarse escribiendo la opción -sP.

Escaneo TCP - SYN Stealth (con host discovering)

Este sondeo consiste, primero, en realizar un descubrimiento de host mediante ICMP. De este modo, si recibimos una respuesta ICMP deduciremos que el host está activo. Tras esta pequeña comprobación de la disponibilidad del equipo destino, se procede a enviar un paquete TCP sin datos. 

Estados del puerto según la respuesta - TCP SYN Stealth

Este tendrá activa la bandera SYN en su cabecera, por lo que su función es intentar que el host destino responda a dicho "inicio de establecimiento de conexión" con un SYN-ACK. Análogamente a lo descrito en el sondeo UDP: 
  • Si no se recibe respuesta -> Puerto filtrado.
  • Si se recibe respuesta (observamos los flags de su cabecera):
    • Si están activos los flags en 0x14 -> Puerto cerrado.
    • Si están activos los flags en 0x12 -> Puerto abierto.
    • Si la respuesta es ICMP type=3, code=1,2,3,9,10,13 -> Puerto filtrado.
Los flags anteriores están expresados en base hexadecimal. En binario, 0x12 sería 0001 (para el 1) y 0010 (para el 2), por tanto 0x12 = 0001 0010. Cada uno de estos dos bloques corresponde a un flag, concretamente, 0001 es ACK y 0010 es SYN. Por lo que 0x12 indica ACK-SYN, esto es, la respuesta que determina que el puerto i-ésimo está abierto.

Análogamente, 0x14 se corresponde con 0001 0100, que son las banderas ACK-RESET, lo cual indica que el puerto está cerrado.

Si quieres saber más sobre los flags o banderas de la cabecera de un paquete TCP dejo enlace al siguiente documento: https://sites.google.com/site/customconfusion/summary-sheets/tcp-flags.

Código en Python - syn-stealth.py

Zanjando el estudio teórico anterior, dejo el código equivalente en Python:

Figura 5. Código Python del escaneo TCP SYN Stealth

Ejemplo de uso - syn-stealth.py

Este script (syn-stealth.py) se ejecuta de manera similar al anterior. Dentro del directorio /usr/lib/python2.7/dist-packages/scapy y ejecutando el comando python syn-stealth.py.

Figura 6. Resultados de syn-stealth.py en Scapy (Linux)

Lanzamos el sondeo contra la misma ip que en udp-scan.py, pero esta vez visitando puertos que alojan procesos del protocolo TCP: HTTP (80), HTTPS (443), etc.

Según se observa en la figura 5, los puertos 80 y 443 están abiertos (ACK-SYN ó 0x12) y los puertos 21 y 22 están filtrados (respuesta ICMP descrita anteriormente). Hacemos la misma prueba desde Nmap con nmap -sS -p 21-443 193.136.28.31.

Figura 7. Prueba TCP SYN Stealth en Nmap

Del mismo modo, los puertos 80 y 443 se encuentran abiertos y, por descarte, 21 y 22 filtrados (entre muchos otros del intervalo 21-443).

La descarga de estos scripts está disponible en el apartado Descargas. Se agradece todo tipo de comentarios y sugerencias.

No hay comentarios:

Publicar un comentario