Optimizando Nmap

Nmap es una archiconocida herramienta de escaneo de red. Seguramente todos los que estéis leyendo esto la habréis utilizado alguna vez. Permite de forma muy sencilla hacer una enumeración de los equipos que hay conectados a una red, o descubrir los servicios disponibles en una máquina. Cuando hacemos esto de forma ocasional, y para un escaneo de una red pequeña, o para un solo equipo, no nos paramos a pensar en afinar la configuración, y escribimos la ristra de parámetros que nos sabemos de memoria. En mi caso suelo usar:

# nmap -T4 -A ip-host

Esta configuración hace un escaneo TCP de tipo SYN scan, que es el tipo por defecto si no le especificas de otro tipo. Utiliza una plantilla de tiempo bastante agresiva (-T4), y además ejecuta detección de sistema operativo, de versión de servicios, uso de scripts, y traceroute (-A) Todo esto, como ya hemos comentado, puede ser adecuado si el escaneo se lo realizamos a una única máquina. Pero cuando estamos hablando por ejemplo de hacer un escaneo a una clase B entera, se hace imprescindible optimizar el máximo posible la configuración del escaneo, ajustar al máximo los tiempos y lanzar únicamente las sondas que sean necesarias. Veamos como ajustar los rtt con valores personalizados.

Lo primero que necesitamos saber es que latencia tiene el tráfico que vamos a escanear. Afinar este dato mejora considerablemente el rendimiento de Nmap, puesto que se perderá menos tiempo en esperar a que haya una respuesta en cada sonda que se envía. Pensad que no es lo mismo escanear una red local que un rango con equipos que se encuentren al otro lado del globo.

Hagamos un ejemplo con una red local. Averiguaremos la latencia de los equipos respecto a la nuestra lanzándole un ping a una máquina cualquiera del rango:

$ ping 192.168.1.1 
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data. 
64 bytes from 192.168.1.1: icmp_req=1 ttl=64 time=0.253 ms 
64 bytes from 192.168.1.1: icmp_req=2 ttl=64 time=0.165 ms 
64 bytes from 192.168.1.1: icmp_req=3 ttl=64 time=0.173 ms 
64 bytes from 192.168.1.1: icmp_req=4 ttl=64 time=0.182 ms 
64 bytes from 192.168.1.1: icmp_req=5 ttl=64 time=0.176 ms 
^C 
--- 192.168.1.1 ping statistics --- 
5 packets transmitted, 5 received, 0% packet loss, time 3999ms 
rtt min/avg/max/mdev = 0.165/0.189/0.253/0.036 ms 

Podemos ver como tenemos un rtt medio de 0.189 ms y un máximo de 0.253ms. Si la red tiene tráfico ICMP filtrado, podemos usar hping3 para hacer un ping TCP a un puerto de una máquina que sepamos está disponible:

# hping3 --syn -p 22 192.168.1.1
HPING 192.168.1.1 (eth0 192.168.1.1): S set, 40 headers + 0 data bytes 
len=46 ip= 192.168.1.1 ttl=64 DF id=0 sport=22 flags=SA seq=0 win=14600 rtt=0.3 ms 
len=46 ip= 192.168.1.1 ttl=64 DF id=0 sport=22 flags=SA seq=1 win=14600 rtt=0.3 ms 
len=46 ip= 192.168.1.1 ttl=64 DF id=0 sport=22 flags=SA seq=2 win=14600 rtt=0.3 ms 
len=46 ip= 192.168.1.1 ttl=64 DF id=0 sport=22 flags=SA seq=3 win=14600 rtt=0.3 ms 
^C 
--- 192.168.1.1 hping statistic --- 
4 packets transmitted, 4 packets received, 0% packet loss 
round-trip min/avg/max = 0.3/0.3/0.3 ms 

Una vez tengamos una idea de los valores de las latencias de nuestra red a escanear, podemos confeccionar una configuración con parámetros personalizados. El manual recomienda poner como initial-rtt-timeout el doble de la latencia máxima, y el cuádruple para max-rtt-timeout, aunque esto ya es a gusto del consumidor ;)

# nmap -T4 --initial-rtt-timeout 4 --max-rtt-timeout 8 --max-retries 2 --min-hostgroup 255 192.68.1.0/24 
[..]
Nmap done: 256 IP addresses (23 hosts up) scanned in 127.98 seconds

Sabiendo que para -T4 el valor de initial-rtt-timeout es 500 y max-rtt-timeout 1250, el ahorro sobre el papel es bastante significativo.

Se ha personalizado max-retries a 2. En una red local que no esté saturada, no es usual perder un paquete, así que poniendo 2 nos aseguramos de no perder eficacia y que se nos pueda escapar algún puerto perezoso. Teniendo en cuenta que el valor de max-retries es 6 con -T4, estamos ahorrando 4 intentos de descubrir un puerto hasta que lo descarta. Multiplicado por el número de puertos por máquina y por el número de máquinas a escanear, es un ahorro considerable.

Probemos ahora con la configuración por defecto:

# nmap -T4 192.168.1.0/24
[..]
Nmap done: 256 IP addresses (23 hosts up) scanned in 324.67 seconds 

Podemos ver que la mejora en tiempo es notable.

En redes donde hayan dispositivos de seguridad de por medio y filtren tráfico debemos llevar cuidado e hilar con hilo especialmente fino. Por defecto, Nmap determina si un host está operativo lanzándole un ping y una sonda ACK al puerto 80 TCP a la espera de contestación. Si no obtiene respuesta, considera que no hay nadie a la escucha y descarta la ip. Se corre el peligro de conseguir resultados incompletos. Una posible solución es realizar una primera búsqueda rápida pero eficaz que nos devuelva únicamente los hosts activos y “grepear” el resultado para pasárselo como entrada a un segundo escaneo más completo.

nmap -sP -PE -PS21,22,23,25,80,113,3306 -PA80,113,443,8080 -oG – 192.168.1.0/24 | awk '{print $2}' | grep -v "Nmap" > ips

# nmap -T4 -PN --initial-rtt-timeout 4 --max-rtt-timeout 8 --max-retries 2 –min-hostgroup -iL ips

Además de lanzar un paquete ICMP echo request con -PE, se mandan paquetes TCP vacíos con el flag SYN (-PS) y ACK (-PA) a los puertos más comunes. Una vez se haya completado el descubrimiento de host, ya se puede lanzar un segundo Nmap más completo, utilizando la salida del primero como entrada del segundo.

En escaneos UDP el asunto se complica. Al no ser un protocolo orientado a conexión, un puerto abierto no devuelve respuesta si el paquete no está bien formado y concuerda con el protocolo concreto, por lo que Nmap no es capaz de diferenciar si el puerto está abierto, o hay un dispositivo intermedio que está filtrando la sonda, o simplemente que se ha perdido por la jungla de la red. La situación se agrava cuando el equipo a escanear es un Solaris o Linux, ya que por defecto tienen las respuestas ICMP limitadas a una por segundo. Este valor se puede ver en /proc/sys/net/ipv4/icmp_ratelimit (recordemos que si un equipo recibe una sonda sobre un puerto TCP cerrado, envía un RST, mientras que si es UDP, responde con un paquete ICMP destination unreachable)

Indicaciones:

  • Hay que llevar mucho cuidado configurando los timeouts puesto que si superan el segundo, probablemente perderemos alguna respuesta ICMP y Nmap considerará un puerto abierto/filtrado cuando en realidad está cerrado. Aunque Nmap es capaz de detectar esta limitación y aumentar progresivamente el rtt-timeout, es recomendable utilizar el esquema de tiempo -T3.
  • También puede interesar usar el modificador -F que escanea los 100 puertos más usuales en vez 1000 que hace por defecto. En UDP se usan muy pocos tipos de puertos en comparación con TCP.
  • Ya que no se puede hacer nada contra la limitación ICMP, la forma de agilizar los escaneos UDP con grandes redes es aumentar el paralelismo de escaneo de equipos a la vez, con –min-hostgroup.

Otras recomendaciones generales:

  • Realizad escaneos TCP y UDP de forma separada. Ya hemos visto que funcionan a ritmos diferentes por lo que para agilizar la tarea cada uno necesita unos parámetros distintos.
  • Evitar usar la detección de sistema operativo, resolución DNS, traceroute o detección de versión -sV salvo que se necesite esa información precisa, puesto que ralentizan mucho el escaneo de cada host.
  • Utiliza el modificador exclude para ignorar rangos de ips, si no necesitas que sean escaneados, o si sabes que en esos rangos no hay equipos levantados. Le ahorrarás a Nmap el tiempo de analizarlos.
  • Lanza Nmap en franjas horarias que consideres que haya poco tráfico en la red que vas a escanear. Una red saturada afecta considerablemente al rendimiento por estar continuamente reenviando sondas perdidas.
  • A pesar de que pueda ser obvio, ¡actualiza Nmap! Cada nueva versión se va puliendo el rendimiento añadiendo mejoras en los algoritmos.

Comments

  1. Muy buen artículo. Sólo indicar que, al menos en la versión última (5.61TEST5), los valores de rtt-timeout son en segundos. Si los queremos en milisegundos hay que añadir el sufijo ms al número.

Trackbacks

  1. […] un tiempo que no me apetecía invertir, recordé el artículo de mi compañero José Luis sobre la optimización de Nmap. Ya sabéis, básicamente lancé un ping para calcular el RTT mínimo y medio y utilicé esos […]