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.