Revista Informática

Cómo crear paquetes de red desde cero en Linux

Publicado el 21 marzo 2017 por Drassill
Cuando estamos realizando alguna prueba de red; ya sea para testear un cortafuegos o para hacer alguna tarea de debugeo, a veces tenemos la necesidad de tener que realizar tareas muy concretas que una aplicación "convencional" no puede ofrecernos; pues podemos tener que necesitar usar un protocolo IP o puerto concreto que no es fácil de emular de forma convencional. Es por ello que a veces necesitamos tener que hilar muy fino y recurrir a aplicaciones que nos puedan permitir crear paquetes de red de forma precisa y personalizada. Aquí es donde entra en juego una aplicación que, si bien he de admitir que inicialmente no es muy intuitiva, nos ofrece un enorme control sobre los paquetes de red; ya sea para crearlos desde cero, como para editar unos existentes: Dicha herramienta, desarrollada en Python, se denomina Skapy y hoy quiero enseñaros como desenvolvernos con ella para, por lo menos, saber defendernos a la hora de tener que usarla.
Portada_crear_paquete_red
Scapy es una herramienta usada puramente en la consola pero a la que no hay que tenerle miedo, ya que si bien inicialmente tiene una cierta curva de aprendizaje, tras un par de pequeñas pruebas comprobareis que podréis realizar, al menos, las pruebas y operaciones más básicas. Se trata de una herramienta que no está instalada por defecto en ningún sistema Linux ni tampoco en en ningún repositorio oficial, con lo que tiene que ser bajada desde la página oficial de ésta. La forma más ágil de hacerlo, en mi opinión, es desde la consola, ya que es tan sencillo como escribir:
wget https://github.com/secdev/scapy/archive/v2.3.2.zip
Al ser una utilidad comprimida en formato, zip; sería necesario tener ésta utilidad instalada en el sistema, cosa muy sencilla ya que está incluida en los repositorios oficiales. Después, simplemente habría que descomprimir el archivo descargado mediante el comando unzip. Todo esto se traduce en dos simples acciones:
  1. apt-get install zip
  2. unzip v2.3.2.zip

Del fichero descomprimido obtendremos una carpeta llamada scapy-2.3.2 en la que tendríamos que entrar para poder ejecutar su binario, llamado run_scapy.
Ahora bien; antes de continuar hay que tener claro qué es lo que necesitamos para crear nuestro paquete. Todo paquete de red requiere (explicado de forma muy resumida) de dos elementos:
  • Una cabecera IP: La cabecera IP es un conjunto de reglas o campos que se usan para transmitir el paquete de un lugar a otro. En concreto son 13 campos: La versión IP, longitud del paquete, tipo de servicio, longitud total, identificador, flags (etiquetas), Offset, tiempo de vida, protocolo, checksum, IP origen, IP destino y opciones. A veces lleva también un campo extra usado para "rellenar" el paquete para que así tenga un tamaño que sea múltiplo de 32 bits. Si bien todos los parámetros son importantes, los que más nos interesan al usar Scapy son: La versión IP (IPv4 o IPv6), el protocolo, la IP de origen y la IP de destino. 

Composicion_cabecera_IPComposición cabecera IP
  • Payload: El payload serían los datos que se quieren incluir dentro del paquete; datos que pueden ser de cualquier tipo; desde un texto plano a una imagen. Dicho mensaje no es siempre necesario, pues dependiendo de la prueba que queramos hacer tal vez no nos interese en absoluto el contenido del paquete, sino que simplemente éste llegue.

Teniendo esto claro, vamos a crear nuestro propio paquete. Con Scapy en ejecución, lo primero que haremos será crear el elemento más importante: la cabecera IP. Dicha creación puede parecer compleja, pero no es así: Una cabecera vacía (a modo de test) sería tal que así:
Nombre_variable=IP()
Por ejemplo:
CABECERA=IP()
Con esto Scapy ya habría creado la cabecera, cabecera que tendría una serie de parámetros asignados por defecto; muchos de los cuales nos sirven tal y como están. Para ver el contenido de nuestra cabecera, escribiremos:
CABECERA.show()
Cabecera_IP_vaciaCabecera IP vacía
Si os fijáis bien, la mayoría de los campos están rellenados, y la mayoría, con los datos que se han introducido por defecto, serían datos válidos para una cabecera IP; a excepción de 3: proto, src y dst. Los cuales serían, protocolo, IP origen (src, o IP source) e IP de destino (dst, o IP destiny). Al protocolo le daremos un trato personalizado, tal y como veréis más adelante, pero los otros dos parámetros deben de ser introducidos. Para ello, haremos que la variable CABECERA, tenga dichos nuevos valores, tal que así:
CABECERA=IP(src='192.168.1.2',dst='192.168.1.3')
Es importante tener en cuenta que, si queremos editar en el futuro otros valores, tendremos que seguir manteniendo estos valores. Por ejemplo si deseásemos ahora que el campo flags, tuviese el valor 2; si hiciésemos esto:
CABECERA=IP(flags=2)
Sobrescribiríamos lo introducido anteriormente, ya que lo que estamos haciendo es asignar un valor a la variable CABECERA, con lo que para añadir dicho valor tendríamos que hacer:
CABECERA=IP(src='192.168.1.2',dst='192.168.1.3',flags=2)
Tenemos la cabecera casi formada, pero tal y como he comentado antes, el protocolo que ha asignado Scapy por defecto no es el correcto. Pero no valdría que le asignásemos un valor a dicho campo, ya que el protocolo es un campo que debe de ser tratado por separado debido a su relevancia. Podemos optar por uno de estos tres protocolos: ICMP, TCP y UDP. En mi caso optaré por UDP, pero se puede escoger cualquiera de ellos. Para crear un campo UDP, haríamos:
Nombre_variable=UDP()
En mi caso:
PROTOCOLO=UDP()
Ahora veremos el interior de dicho campo del mismo modo que hemos hecho con nuestra cabecera; es decir:
PROTOCOLO.show()
Protocolo_sin_datosProtocolo_sin_datos
Tenemos el protocolo UDP "creado", pero el problema está en que no hemos dicho desde qué puerto vamos a enviar el paquete y a qué puerto queremos enviarlo. Para ello tendremos que seguir el procedimiento, de forma parecida a la anterior, y editar la variable PROTOCOLO para que sus valores sport (puerto origen) y dport (puerto destino) tengan unos valores determinados; lo cual haríamos de esta forma:
PROTOCOLO=UDP(sport=1024,dport=80)
Por último, vamos a crear un payload con algo de contenido a modo de prueba. La creación de payloads en sí no es sencillo, pero en este caso, al querer enviar un payload con un texto plano a modo de prueba, su complicación no es elevada. Para crear el payload tendremos que usar el mismo procedimiento que el que hemos hecho antes para el resto pero llamando a la "función" Raw. Función que solo tiene un campo llamado load, con lo que a diferencia de con los procesos anteriores en los que primero hemos creado la variable, luego la hemos consultado y por último hemos rellenado sus campos. Aquí directamente la crearemos con su campo rellenado:
PAYLOAD=Raw(load='Esto es una prueba ->UDP\n')
Ya tenemos todos los elementos creados, solamente habría que unificarlos para crear el paquete; para lo cual recurriremos a las variables que hemos creado. El paquete tendrá la estructura:
nombre_variable=Cabecera_IP/Protocolo/Payload
Para nuestro caso particular sería:
PAQUETE=CABECERA/PROTOCOLO/PAYLOAD
Para ver cómo ha quedado nuestro paquete escribiremos PAQUETE.show(), que mostrará la unión de todo lo que hemos creado hasta ahora:
Paquete_scapyPaquete Scapy
Ya tenemos todo listo; ahora solamente tendremos que enviarlo mediante el comando send:
send(PAQUETE)
Lo ideal sería tener algo que nos permita detectar en tiempo real si el paquete ha sido enviado con éxito. Esto se puede lograr de dos formas. La primera es usando un analizador de red como tcpdump o Wireshark, ya sea en este equipo o en el receptor del paquete. La segunda sería usando netcat en el receptor para "escuchar" en el puerto 80 y ver qué es lo que nos llega. Lo más normal sería optar por la primera opción; con lo que si hiciésemos una captura y la abriésemos, veríamos que efectivamente nuestro paquete ha sido enviado con éxito.
Captura_WiresharkCaptura de red leída con Wireshark
Como podéis ver la creación de un paquete de red con Scapy, no es tan complicado como parece; solamente requiere tener cierto cuidado durante su "elaboración", pero gracias a la flexibilidad otorgada por esta utilidad podremos enviar paquetes de red creados de forma completamente "artesanal" cuya creación hemos controlado de principio a fin.
Espero que os haya resultado útil.
Saludos.

Volver a la Portada de Logo Paperblog