
IPv4-SYN mit IP-Optionen, ein Lab-Ziel pro Lauf, für die Forschung zu CVE-2026-98432. Header-Offset-Parser, Regressionstests und eine Postmortem-Analyse eines fehlerhaften Auto-Exploiters.
Eine Einzelziel-Sonde für CVE-2026-98432, den Linux-Kernel-IPv4-TCP-Use-after-free, der im Oktober 2026 gepatcht wurde.
Sie erstellt ein IPv4-SYN, das IP-Optionen trägt (standardmäßig 24 NOP-Bytes, was die IHL auf 11 erhöht), sendet es an einen Host und liest die Antwort anhand des Header-Offsets statt durch Musterabgleich aus. Die Paketlogik läuft auch offline: Ein Probelauf gibt das erstellte Paket aus und parst es erneut, der Hex-Analysator parst aufgezeichneten Datenverkehr, und die Testsuite benötigt weder Root noch ein Netzwerk.
Es gibt keine Zielistenverwaltung, keine Shodan-Integration und keinen Batch-Modus. Eine Adresse pro Lauf, und die CLI lehnt alles außerhalb von Laborbereichen ab, es sei denn, Sie übergeben --allow-public. Das Tool geht davon aus, dass Sie Eigentümer des Ziels sind oder eine schriftliche Genehmigung zum Testen haben.
python3 -m pip install -e ".[test]"
Python 3.9 oder neuer. Nur Standardbibliothek.
Probelauf, keine Privilegien, kein Paket gesendet:
synipopt --dry-run --target 192.0.2.10
Das Paket aus dem Log des Original-Skripts parsen, das eine SYN-ACK meldete:
synipopt --analyze-hex "$(cat docs/fixtures/original-log-packet.hex)"
Eine Labor-VM sondieren und optionstragende SYNs mit einfachen SYNs vergleichen:
sudo synipopt --target 192.168.56.101 --dport 443 --retries 3 --compare --log probe.jsonl
Standardkonstruktion, 64 Bytes auf der Leitung:
0x01-NOP-FüllungEin SYN mit Optionen ist es, was ireq->ireq_opt auf dem Request-Socket setzt. Der Kernel kompiliert den Optionsblock, und reine NOP-Füllung kompiliert zu einem leeren Block, sodass eine SYN-ACK auf die Standardkonstruktion ohne IP-Optionen zurückkommt. Optionen, die Nutzdaten tragen, etwa Record Route, werden zurückgespiegelt. Während der Entwicklung gegen einen lauschenden Socket gemessen:
| SYN-Optionen | SYN-ACK-IP-Header | zurückgespiegelte Optionen |
|---|---|---|
| 24 NOP-Bytes | 20 Bytes (IHL 5) | keine |
| Record Route, 12 Bytes | 32 Bytes (IHL 8) | 12 Bytes |
| keine | 20 Bytes (IHL 5) | keine |
Beide Fakten sind für die Laborarbeit wichtig: Die Header-Länge der Antwort sagt Ihnen, ob der Stack den Optionsblock beibehalten hat, und ein korrekter Parser beginnt den TCP-Header bei IHL * 4, nicht bei Offset 20.
Das kursierende Skript hinter diesem Repo (cve_2026_98432_auto.zip) lieferte sein eigenes Laufprotokoll mit. Jede „erfolgreiche" SYN-ACK in diesem Protokoll hat reply_sport=257 und tcp_seq=16843009. Das sind die NOP-Füllbytes: 0x0101 ist 257 und 0x01010101 ist 16843009. Der Parser suchte nach Flag-Bits an beliebiger Stelle im Paket und landete bei Offset 55, innerhalb der Optionsauffüllung. Ein korrekter Parse dieses 84-Byte-Pakets findet IHL 5, Protokoll TCP und ein TCP-Segment, dessen Flags-Byte 0x00 ist, kein SYN und kein ACK.
Die Nutzlast ist das selbst erstellte SYN des Skripts (0x4b bei Offset 20). Der äußere Header gehört dem Kernel: Das Skript setzte nie IP_HDRINCL, sodass Linux seinen eigenen IPv4-Header baute und das erstellte Paket als Nutzlast nahm. Nichts, was es baute, wurde jemals als Paket übertragen. Zwei Fehler kamen zusammen: Das Paket verließ den Rechner nie wie entworfen, und der Parser las Füllbytes als Antwort.
Die Dork-Stufe war ohne Schlüssel wirkungslos. Sie las --shodan-key oder SHODAN_KEY; ohne beides schrieb sie 192.168.56.101 (ein VirtualBox-Host-only-Standardwert) in targets.txt und machte weiter. Mit einem Schlüssel würde sie Shodan nach öffentlichen Hosts abfragen und diese beschießen, was kein Labor-Tool braucht und was Repos entfernen lässt.
synipopt behält die Idee des Einzelpaket-Datenverkehrs bei, entfernt die Zielauswahlstufe, ersetzt den heuristischen Parser durch Header-Offset-Parsing und verwandelt die alten Log-Werte in einen Regressionstest in tests/test_packet.py.
CVE-2026-98432, Linux-Kernel-IPv4-Netzwerk, Use-after-free, CVSS 3.1 9.8 vom Linux-CNA. Red Hat bewertet es als Critical (RHSB-2026-016) und listet RHEL 8, 9, 10 und OpenShift unter den betroffenen Produkten.
Der Race, wie im Fix-Commit beschrieben: tcp_v4_syn_recv_sock() überträgt ireq->ireq_opt ohne Kopie auf den Child-Socket (newinet->inet_opt). Eine andere CPU kann ein erneut übertragenes SYN für denselben Request-Socket verarbeiten und ein SYN-ACK aus tcp_check_req() senden. tcp_v4_send_synack() und inet_csk_route_req() lesen ireq->ireq_opt nur unter rcu_read_lock(), und ip_build_and_send_pkt() und ip_options_build() lesen anschließend opt->optlen zweimal. Da Request-Sockets ohne den Listener-Lock verarbeitet werden (Commit 079096f103fa), kann das Child freigegeben werden, während das SYN-ACK noch aufgebaut wird. Child-Sockets fehlt SOCK_RCU_FREE, und inet_sock_destruct() gab inet_opt mit einem einfachen kfree() frei, sodass ip_options_build() freigegebenen Speicher lesen konnte.
Der Mainline-Fix ist Commit e3f33b0a1d89d738e2b913dab519226eaf76e15b, „ipv4: free inet_opt and ireq_opt after an RCU grace period“ (Eric Dumazet, gemergt am 2026-10-05). Er ersetzt kfree() durch kfree_rcu() in inet_sock_destruct() und tcp_v4_reqsk_destructor(). Derselbe Commit nennt einen zweiten Pfad über die Migration von Request-Sockets (net.ipv4.tcp_migrate_req=1 oder ein BPF-reuseport-Programm), bei dem inet_reqsk_clone() ireq_opt zwischen dem alten Request-Socket und seinem Klon teilt.
Die temporäre Gegenmaßnahme von Red Hat verwirft eingehende IPv4-TCP-Pakete, die IP-Optionen tragen:
table inet cve_2026_98432 {
chain input {
type filter hook input priority -310; policy accept;
ip protocol tcp ip hdrlength > 5 counter drop
}
}
--compare prüft genau auf diesen Filter: SYNs mit Optionen bleiben unbeantwortet, während einfache SYNs eine Antwort erhalten.
Quellen:
Wire-Probes können keine Speicherkorruption beweisen. Was sie zeigen können: ob ein Host SYNs beantwortet, die IP-Optionen tragen, ob das SYN-ACK diese Optionen zurückspiegelt und ob ein hdrlength-Filter im Pfad liegt.
uname -r gegen die Advisory deiner Distribution.tcpdump -ni eth0 'tcp and (ip[0] & 0x0f) > 5'sudo synipopt --target VM_IP --dport 22 --retries 5 --compare --log run.jsonlreply_kind klassifiziert die Antwort, und der tcp-Block enthält den geparsten Header. ip_options_len zeigt das Echo, das bei Optionsblöcken mit Payload (Record Route, Timestamp) erscheint; reiner NOP-Füllstoff wird wegkompiliert und kommt als 20-Byte-Header zurück.dmesg. Das Wire zeigt nur die Paketverarbeitung.Teste Hosts, die dir gehören oder für die du eine Testautorisierung hast. Der Standard-Guard verweigert Adressen außerhalb von RFC 1918, Loopback, Link-Local, CGNAT und den Dokumentationsbereichen. --allow-public existiert für autorisierte Engagements auf Hosts, die du kontrollierst. Das Versenden präparierter SYNs an Dritte ist nicht der Zweck dieses Tools.
python3 -m pytest -q