Hace tiempo que decidí echar un ojo al concepto de ocultar información dentro de los protocolos conocidos por todos: TCP, UDP o IP. Mi idea era generar una pequeña prueba de concepto dónde pudiera utilizar un protocolo común, el cual será utilizado como covert cannel, y transmitir información de manera oculta, lo cual se entiende como esteganografía. Tras estudiar diferentes opciones y revisar algún paper que habla de ello, opté por utilizar los flags de TCP y el número de secuencia de los paquetes TCP.
En esta prueba de concepto, los paquetes TCP son reales, pero no tienen ningún sentido. Es cierto que se podría lograr un Stego más real que permitiese transmitir información sobre tráfico coherente TCP. Es cierto, que si un administrador de red lee el tráfico que generaremos no me encontrará coherencia, y pensará que es tráfico erróneo, pero el mensaje se encuentra ahí, oculto.
Planteamiento
El primer planteamiento es ocultar información dentro de las estructuras que proporciona el protocolo TCP. Si nos fijamos en los flags más famosos de este protocolo, se tienen 6 “huecos” para almacenar bits. Los flags son: SYN, ACK, PSH, URG, RST, FIN. Aquí tenemos 6 bits y utilizando el número de secuencia o sequence number del protocolo se puede aumentar el ratio de bits. Para la prueba de concepto se ha decidido utilizar, solo 2 bits más con el número de secuencia.
En otras palabras, si queremos enviar el mensaje “esto es un secreto”, cada carácter se enviará oculto en los flags y en el número de secuencia de cada paquete TCP. A continuación desglosamos los bits de algunas letras:
En esta prueba de concepto, los paquetes TCP son reales, pero no tienen ningún sentido. Es cierto que se podría lograr un Stego más real que permitiese transmitir información sobre tráfico coherente TCP. Es cierto, que si un administrador de red lee el tráfico que generaremos no me encontrará coherencia, y pensará que es tráfico erróneo, pero el mensaje se encuentra ahí, oculto.
Planteamiento
El primer planteamiento es ocultar información dentro de las estructuras que proporciona el protocolo TCP. Si nos fijamos en los flags más famosos de este protocolo, se tienen 6 “huecos” para almacenar bits. Los flags son: SYN, ACK, PSH, URG, RST, FIN. Aquí tenemos 6 bits y utilizando el número de secuencia o sequence number del protocolo se puede aumentar el ratio de bits. Para la prueba de concepto se ha decidido utilizar, solo 2 bits más con el número de secuencia.
En otras palabras, si queremos enviar el mensaje “esto es un secreto”, cada carácter se enviará oculto en los flags y en el número de secuencia de cada paquete TCP. A continuación desglosamos los bits de algunas letras:
- La letra “e” se traduce en “01100101”, por lo que los 2 bits más significativos serán introducidos como número de secuencia, siendo éste el valor “1”. En el caso de ser “10” sería un “2”, y en el caso de ser “11” sería un “3”. El resto de bits menos significativos se corresponden con los 6 flags comentados anteriormente. Los flags RST, ACK y URG irían a 1. En la captura de Wireshark se puede visualizar.
En la máquina destino tendremos un programa que es capaz de leer el tráfico TCP y decodificar el tráfico oculto en TCP. ¿Cómo sabemos que el tráfico es especial? Hemos utilizado un puerto destino concreto como clave. Es decir, cuando recibamos un TCP destino 3030 entendemos que es un paquete con esteganografía.
El proceso de decodificación es el proceso inverso al de ocultación. El programa remoto leerá el número de secuencia y esos 2 bits (del 0 al 3) serán los bits más significativos. Después concatenaremos los flags, y obtendremos de nuevo un valor de 8 bits. Este valor se identifica con un carácter.
Una vez entendido el mecanismo sencillo que se utilizará para ocultar los caracteres en los paquetes TCP vamos a hablar de la implementación.
Implementación
Para llevar a cabo la prueba de concepto se utilizó Ruby. La librería PacketFu, la cual es parecida a Scapy en Python, permite “jugar” y crear paquetes TCP y datagramas IP a nuestro antojo. Ya fue utilizado para el Port-Knocking con Latch y el modo paranoia.
En la siguiente imagen se puede ver el código de la función main. El coder recibe 2 parámetros: IP destino y mensaje a ocultar en el protocolo TCP.
El coder invoca un método denominado send_message, el cual proporciona la funcionalidad de ocultar el mensaje en el protocolo TCP, tal y como se explicó anteriormente. La función send_message inyecta el tráfico en la red a través de la función Inject y prepara mediante unpack los caracteres que forman el mensaje en formato bytes.
Hay una función importante como es write_byte_into_packet que permite ocultar la información en el paquete TCP gracias a PacketFu, tal y como puede verse en la siguiente imagen. En esta imagen se puede ver la explicación de dónde se oculta dentro de un paquete TCP los bits.
Y, ¿El orden?
El orden de los paquetes TCP importa, ya que no es lo mismo que la “e” de “esto es un secreto” llegue antes o después que la “s”. El mecanismo válido en un ámbito real sería utilizar el sequence number para lo que es, y utilizar el ACK number cómo lo estamos utilizando nosotros en esta prueba de concepto. En este caso, para evitar problemas se ha decidido enviar los paquetes con 1 segundo de diferencia, aunque en entornos LAN no habría problema.
En la siguiente imagen se puede visualizar como el emisor lanza el mensaje a una dirección IP concreta y al puerto 3030. El mensaje es “esto es un secreto”.
El receptor recibe el tráfico y puede obtener el mensaje oculto uniendo los bits del sequence number y los flags TCP, tal y como se puede ver en la imagen. El resultado es el mensaje original. Si viéramos con Wireshark el tráfico veríamos paquetes TCP que podrían ser considerados incoherentes, pero que esconden el mensaje.
Quiero agradecer a mi compañero Daniel Ruiz su apoyo y colaboración en llevar la prueba de concepto hacia adelante.