viernes, 14 de octubre de 2011

Instalando, usando y parcheando nemesis

Nemesis es una aplicación de creación de paquetes de red. Nos permite crear a nuestro gusto (dentro de unos límites) paquetes de red e inyectarlos a la red. Actualmente su desarrollo está parado pero sigue siendo una herramienta muy buena para curiosear con las redes y el tráfico, se puede conseguir aquí.

Está construida sobre la libnet, la biblioteca básica de inyección de tráfico (aunque a mi no me llega a convencer del todo, prefiero PF_PACKET).

Ayer quería jugar un poco con nemesis, así que me baje el código fuente y, como especifica la web, busqué por internet el código de la libnet-1.0.2a, que es la que usa nemesis. Libnet, a la hora de escribir estas líneas va por la version 1.1.4 si no me equivoco, y no tiene compatibilidad hacia atrás.

Para instalar la libnet lo propio, ./configure, make, sudo make install. El problema vino al instalar nemesis, ./configure decía que no encontraba la libnet. Probé a jugar con los argumentos --with-libnet-include y --with-libnet-libraries pero no logré que la detectara. Al cabo de un rato me di cuenta que era porque ya tenía instalada la última versión (en los repos de Debian libnet1-dev) y la versión anterior no sobreescribió, ni tampoco soltó ningún mensaje. Esto lo descubrí analizando la salida del sudo make install de la libnet.

Después de cargarme la libnet que tenía y reinstalar la 1.0.2a logré que nemesis se instalara sin problemas y empecé a jugar con él (o sea, a leer manual).

Llegué a un punto en que me encontraba un fallo y no tenía muy claro si era mio o de nemesis. La situación era la siguiente, yo quería crear una pregunta DNS e inyectarla a la red, para ello ejecutaba nemesis de la siguiente manera (preguntando por ulpgc.es):


$ perl -e 'print "\x05ulpgc\x02es\x00\x00\x01\x00\x01"' > query.raw; sudo nemesis dns -dlo -g256 -q1 -P query.raw

Como contrapartida tengo que decir que DNS no está completamente soportado por nemesis y que hay que usar un fichero de payload que contenga parte del paquete DNS bien construido, una pena. Además lo que sí está soportado no lo está correctamente, por ejemplo nemesis-dns tiene el parámetro "-g" para poner el valor de flags que queramos, sin embargo en DNS no existe ningún campo "flags", el RFC 1035 especifíca el flag QR, el campo Opcode, los flags AA, TC, RD, RA, y los campos Z y el RCODE concretamente en lo que nemesis entiende "flags". Los que han trabajado conmigo saben que soy muy estricto con estas cosas... y más con DNS (no Héctor? ;).

Cuando hayamos inyectado el paquete en la red y lo analicemos con wireshark veremos esto:

Si analizamos byte a byte todo el mensaje DNS (RFC en mano) veremos que está bien construido y sin embargo wireshark nos dice "Malformed packet". Mirando la cabecera UDP veremos que el campo Length vale 8, el RFC 768 especifica que el campo Length debe ser el tamaño en bytes de la cabecera UDP más el payload que lleva, en nuestro caso el mensaje DNS. Así pues nemesis o libnet (linux lo descarto) están creando mal el paquete.

Después de un ratillo mirando el código de nemesis llegué al sitio del problema, fichero nemesis-proto_dns.c:

92     if (state == 0)
93     {
94         libnet_build_udp(udp->uh_sport, udp->uh_dport, NULL,0
95                 pkt + link_offset + LIBNET_IP_H);
96     }

Es aquí donde nemesis le pide a la libnet que cree la cabecera UDP, sin embargo siempre le dice que no hay payload (NULL, 0) aunque no sea así. Más tarde nemesis si que pone el payload pero ya el campo Length de UDP estará mal generado. La solución es cambiar estas líneas por:

92     if (state == 0)
93     {
94         libnet_build_udp(udp->uh_sport, udp->uh_dport, NULL, LIBNET_DNS_H + pd->file_s,
95                 pkt + link_offset + LIBNET_IP_H);
96     }

Recompilamos y ya nemesis generará paquetes DNS sin el bug antes mencionado. He escrito un mail al autor (Jeff Nathan) con el parche, lo que no estoy seguro de si lo verá ya que como comentaba al principio, nemesis lleva bastantes años parados, una pena, es una buena herramienta.