Estaba la otra tarde dándole vueltas al protocolo ICMP (llamarme raro), y se me ocurrió pensar algo un poco absurdo… aunque una vez buscada información en Internet, me dí cuenta que no soy el único tarado y que mi pregunta ya se la había hecho alguno que otro antes que yo.
Como decía, revisando el protocolo, a parte de la cabecera, datos de origen, destino, y unas cuantas cosas más, tenemos el campo de “payload” o carga del paquete. En principio es un campo más, que podemos utilizar o no.
Me pareció graciosa la opción de poder enviar mensajes a través de dichos paquetes, ya que podemos incorporar cadenas ASCII directamente a dicho campo, siendo visibles por ejemplo con WireShark esnifando paquetes o realizando un script en el equipo destino que nos extraiga dicha cadena del paquete recibido.
Y en estas en cuando se me ocurrió la tontería… ¿Y si en lugar de mandar cadenas ASCII, envío datos en binario? ¿Y si cojo un archivo cualquiera (imagen, pdf, …) y lo voy mandando en trocitos binarios y lo recompongo en el equipo destino?
Así que como me ocurre normalmente cuando se me pasan tonterías de estas por la cabeza, tuve que ponerme a probarlo inmediatamente.
Tengo que decir que me decanté por usar Python por dos motivos muy sencillos: el primero es que estoy aprendiendo este lenguaje desde hace un par de semanas. No lo había utilizado nunca, pero una vez que lo estoy conociendo, me encanta todo lo que se puede hacer y lo sencillo que resulta entenderlo, sobre todo si has programado en otros lenguajes antes. La segunda es que Python tiene una librería llamada “Scapy” que es fantástica para manipular y crear paquetes… A nivel de capa 3, a nivel de capa 2… TCP, UDP… diversos protocolos… y lo mejor de todo es que tú decides los datos que quieres para cada campo y si no rellenas alguno, no hay problema, Scapy pone los valores por defecto para todo lo que tú no modifiques, así que no hay problemas con checksum y demás cositas (sobre todo las que tienes que calcular a mano si usas sockets por ejemplo)…
Volviendo al tema que nos ocupa, creé un par de scripts en Python, uno para enviar el archivo y otro para recibir. Para realizar las pruebas, lo ideal sería tener cada uno en un equipo distinto, pero podemos aprovechar una ventaja del protocolo si queremos realizar las pruebas en el mismo equipo.
La carga del paquete Echo-Request, es devuelta en el paquete Echo-Reply por ejemplo cuando el equipo de destino no es accesible, así que lo que hay que hacer para que funcione en el mismo equipo es ejecutar los dos scripts, pero en el de envío poner UNA IP QUE NO EXISTA, como el paquete no va a llegar a destino y nos será devuelto con el contenido tal cuál, podemos leerlo con el script de lectura como si estuviésemos en otro equipo distinto.
¿Y todo esto para qué? Enviar un archivo de varios megas puede requerir horas… ya que sólo se puede enviar unos pocos bytes por paquete… y hay que darle un tiempo prudencial al script para procesar el paquete, así que aunque el protocolo ICMP sea muy rápido, está forzado a un sólo paquete por segundo.
Pues la respuesta es sencilla y es más de una.
1.- Primero investigar, probar y descubrir cosas nuevas. Si estás leyendo esta entrada, entenderás de lo que hablo. No podemos resistirnos a probar todo lo que se nos ocurre.
2.- Segundo, saber que pasa con esos paquetes si los hago pasar a través de un UTM por ejemplo. ¿Será detectado como paquete sospechoso o malicioso? ¿Pasará? Tengo que probarlo en la oficina en cuanto tenga tiempo.
3.- Buscar nuevos vectores de ataque. ¿Y si hacemos un troyano que se dedique a mandar un paquetito con información cada cierto tiempo, haciéndose casi indetectable y pudiendo atravesar reglas de firewall que permitan ping? Tardaría lo suyo pero, poco a poco, sería capaz de ir extrayendo información del equipo silenciosamente, que es una cosa muy interesante para los ciberdelincuentes y no suelen tener prisa cuando se consiguen datos de manera indetectable.
Así que me puse manos a la obra, y aquí están los dos script que escribí. Ya sé que falta control de errores, que se puede mejorar, que seguro que hay funciones más sencillas… EH! Quietos… Que ya os he dicho que llevo un par de semanas, y anda que no estoy contento con el resultado ;-D.
El modo de funcionamiento es sencillo.
1.- Lanzar el script “recibiricmp.py”, se quedará a la escucha de los paquetes ICMP que lleguen con la palabra clave en el campo payload.
2.- Lanzar el script “enviaricmp.py” con 2 parámetros. El primero la IP del equipo destino (si lo pruebas en el mismo equipo, UNA IP QUE NO EXISTA), y el segundo el nombre del archivo a transmitir.
Primero se envía un paquete con el nombre del archivo a transmitir y el número de partes que se enviarán, para poder mostrarlo en el equipo de destino, y que el usuario esté al corriente de cuantos paquetes tienen que llegar (y cuanto tiempo va a tardar, ya que va a un paquete por segundo… así que la cuenta es fácil…)
Luego se va enviando en archivo, abierto en binario, en trozos de 1000 bytes por paquete. El número de 1000, se puede variar, pero teniendo en cuenta el tamaño máximo de mensaje, que suele ser 1500 bytes. Si calculamos 1500 (Tamaño máximo) -20 (cabecera IP) -8 (cabecera ICMP) nos quedan 1472 bytes para rellenar. Por si hay algún dispositivo que no llega a los 1500, he decidido no aprovechar todo el tamaño, así que lo he dejado en 1000 bytes. Podéis modificarlo a gusto y probar qué pasa.
Cuando se han enviado todos los paquetes con partes del archivo, se envía uno con la palabra clave “fin” para que el equipo de destino termine automáticamente el script.
El archivo de destino, se va rellenando con los datos que van llegando agregándolos al final del archivo (append). Por ese motivo se borra el archivo de destino si existe antes de empezar y por eso lo de un paquete por segundo. Para que el orden sea el correcto y el archivo se reconstruya correctamente.
En un futuro, si ésto se utilizase para algo interesante, lo suyo sería a parte de agregar un control de errores, agregar a la carga un identificador que diga el número de paquete que es dentro del archivo, para que en caso de que lleguen desordenados, poderlos reorganizar correctamente…. pero lo dicho, ésto sólo era una prueba.
Y aquí están los dos scripts. No seáis crueles por mi código de novato.
(TENER EN CUENTA LA INDENTACIÓN QUE NECESITA PYTHON PARA FUNCIONAR CORRECTAMENTE, QUE AQUÍ NO APARECE)
Click en la imagen para agrandar.
Espero que os haya parecido interesante la entrada.
Saludos y hasta la próxima.
ENVIARICMP.PY
#!/usr/bin/python #Script EnviarIcmp.py #Oscar Navarrete Bermejo import sys from scapy.all import * capa3 = IP() capa3.dst = sys.argv[1] capa4 = ICMP() password = "password" longitud = 1000 fp = open(sys.argv[2],"rb") data = fp.read() fp.close() ini = 0 fin = (longitud) contador = (len(data)/longitud)+1 print "Se va a enviar el archivo " + sys.argv[2] + " con una longitud de %s" % len(data) print "Enviando %s partes a la IP %s" % (contador,sys.argv[1]) payload = "nombre" + sys.argv[2] + "," + str(contador) pkt = capa3/capa4/payload a = sr(pkt, verbose = 0, retry = 0, timeout = 1) for a in range(0, contador): print "Enviando la parte %s de %s" %(a + 1, contador) payload = password + data[ini:fin] pkt = capa3/capa4/payload a = sr(pkt, verbose = 0, retry = 0, timeout = 1) ini += longitud fin += longitud payload = "fin" pkt = capa3/capa4/payload a = sr(pkt, verbose = 0, retry = 0, timeout = 1) print "Se han terminado de enviar los datos"
RECIBIRICMP.PY
#!/usr/bin/python #Script RecibirIcmp.py #Oscar Navarrete Bermejo import os.path import subprocess from scapy.all import * contenido = "" contador = 0 partes = 0 nombre = "" vector = "" def recibir(pkt): global contenido, contador, partes, nombre if ICMP in pkt and pkt[ICMP].type == 8 and pkt[ICMP].load[0:6] == "nombre": contenido = pkt[ICMP].load[6:] vector = contenido.split(",") nombre = vector[0] partes = vector[1] if os.path.isfile("recibido_" + nombre): print "El fichero existe, lo elimino primero..." subprocess.call(["rm","recibido_" + nombre]) print "Fichero eliminado..." print "Se va a recibir el archivo " + nombre print "Partes Totales: " + partes elif ICMP in pkt and pkt[ICMP].type == 8 and pkt[ICMP].load[0:8] == "password": f = open("recibido_" + nombre,"ab") datos = pkt[ICMP].load[8:] print >>f, datos, f.close() contador += 1 print "Se ha recibido la parte %s de %s" %(contador,partes) elif ICMP in pkt and pkt[ICMP].type == 8 and pkt[ICMP].load[0:3] == "fin": print "Recepcion completada, saliendo del programa..." exit(0) print "Esperando la llegada de los paquetes..." paquetes = sniff(iface="eth0", prn=recibir)
Útil. Puede utilizarse para establecer una comunicación como una VPN
Muy interesante, si le agregas algun tipo de codificacion, digase base64 o md5, podrias obfuscar cualquier firma que un firewall detecte. Excelente trabajo, muchas felicidades!
Util no se si resultará, pero por lo menos es original y sirve para pensar. Animo y gracias p por compartir.
A ti por leerlo y tomarte tiempo en contestar. Si con estas cosas ayudamos a pensar y a desarrollar nuevas ideas, vale la pena. ¡Gracias!.