
Unprivileged Proof-of-Concept für CVE-2026-74586, eine Use-after-Free-Schwachstelle im Linux-Kernel-SCTP-ASCONF. Bietet einen Raw-Packet-Trigger, Zuverlässigkeitsmetriken und eine detaillierte Weaponization-Analyse einschließlich Struct-Offsets und Spray-Primitiven.
Ein eigenständiger, nicht privilegierter PoC für CVE-2026-74586, einen Use-after-Free
in der SCTP-ASCONF-Behandlung (dynamische Adressrekonfiguration) des Linux-Kernels.
Die Anerkennung für den Bug selbst gebührt Qing Ming, der ihn upstream gemeldet hat;
der Fix ist Commit beb33f8ee1ca („sctp: clear new_transport when removing a
peer"), gemerged am 2026-08-12 mit Cc: stable — Red Hat stuft ihn als wichtig ein,
Drittanbieter-Tracker setzen ihn auf CVSS 9.8.
Was dieses Repo zusätzlich zum Advisory bietet:
sctp_process_asconf_param() speichert den Transport, der durch einen ADD-IP-Parameter
erstellt wurde, in asoc->new_transport. Ein DEL-IP für dieselbe Adresse im selben
ASCONF-Chunk gibt diesen Transport über sctp_assoc_rm_peer() frei,
was (vor dem Fix) nicht löscht. Wenn
zurückkehrt, agiert auf dem veralteten
Zeiger — auf einem Standard-Kernel ist der sichtbare Effekt ein HEARTBEAT, der an die
Adresse des gerade freigegebenen Transports gesendet wird; unter KASAN ist es ein
Slab-Use-after-Free-Report.
new_transportsctp_process_asconf()sctp_sf_do_asconf()Ein einziger ASCONF-Chunk genügt:
[Address Parameter L] [ADD-IP G] [DEL-IP G]
gcc -O2 -Wall -o poc poc.c
./poc
Kein Root. Der PoC hebt eine User- und Network-Namespace auf, setzt die SCTP- Sysctls darin, bringt eine Multi-Homed-Assoziation über Loopback hoch und injiziert dann das konstruierte ASCONF mit einem Raw-Socket.
Erwartete Ausgabe auf einem verwundbaren Kernel:
[+] Association established (no AUTH)
[+] server_vtag=0x... captured_tsn=0x...
[*] serial=0x... (= initial_tsn)
[+] Injected 60 bytes
[9222->9111 vt=...] ASCONF-ACK(0x80) len=8
[+] ASCONF-ACK -> server processed ADD-IP + DEL-IP
[9222->9111 vt=...] HB(0x04) len=60 <- kernel using freed transport
[9111->9222 vt=...] ABORT(0x06) len=8
[+] GhostTransport UAF TRIGGERED (ASCONF-ACK received)
[+] HEARTBEAT to ghost address observed (dangling transport USED)
Der HEARTBEAT nach dem ACK ist der interessante Teil: Dieses Paket existiert
nur, weil der Kernel den baumelnden new_transport durchlaufen hat. Der Client
bricht direkt danach mit ABORT ab, weil der Heartbeat aus heiterem Himmel kam.
Nichts davon ist neuartige Forschung, es ist nur wenig genug dokumentiert, um einen Nachmittag zu kosten, also hier für die nächste Person:
sctp_rcv() die Prüfsumme und verwirft bei
Abweichung. Berechne CRC32C (Poly 0x82F63B78, reflektiert) über das gesamte
SCTP-Paket mit auf null gesetztem Prüfsummenfeld.sctp_auth_recv_cid() läuft im Eingabe-
pfad vor der Zustandsmaschine und verwirft nicht authentifiziertes ASCONF
selbst dann, wenn addip_noauth_enable=1 ist — dieses Sysctl lockert nur die Prüfung
innerhalb von sctp_sf_do_asconf(). Der Ausweg ist, AUTH auf Socket-Ebene
von vornherein nie auszuhandeln (kein SCTP_AUTH_SUPPORTED-
Setsockopt), sodass peer.auth_capable falsch bleibt und beide Prüfungen bestehen.Das Fixes:-Tag auf dem Upstream-Commit ist 6af29ccc223b („sctp: Bundle
HEARTBEAT into ASCONF_ACK"), der Code ist also alt. Behoben in Mainline und
zurückportiert auf die Stable-Serien (Debian-Tracker: behoben ab 7.1.9-1 in
sid, 6.12.107 in trixie-security). Kali rolling liefert Stand 2026-09-09
7.1.5-1kali1 in jeder Suite aus, was allem vorausgeht — das ist die wichtigste
praktische Relevanz dieses PoC.
Für alle, die es weiter treiben — gemessen gegen 7.1.5 in QEMU, Offsets
aus objdump -d des ausgelieferten sctp.ko:
| Feld | Offset |
|---|---|
| flowi (88 Bytes) | 0x30 |
| ipaddr | 0x88 |
| af_specific | 0xa8 |
| asoc | 0xb0 |
| dst | 0xe0 |
| state | 0x15c |
| rcu | 0x2b0 |
sctp_association->new_transport liegt bei 0x6b0. Die Tabelle stammt aus
pahole -C sctp_transport /sys/kernel/btf/vmlinux auf dem laufenden Kernel —
eine frühere Revision dieser Tabelle trug einen falschen ipaddr-Offset (0x68,
durch falsches Zählen von flowi); BTF ist die Grundwahrheit.
sctp_transport_destroy_rcu), es gibt
kein Double-Free auf diesem Pfad. Wenn du gehofft hast, einen Socket zu schließen
und zwei Frees zu bekommen — sctp_association_free() durchläuft nur die Transport-
liste, und rm_peer() hat den Geist bereits entlinkt. Verbringe keinen
Abend damit.sctp_outq_select_transport() liest state bei +0x15c von
chunk->transport bei +0xe8 des Chunks. Ein zurückgewonnenes Objekt, das mit
state=1 (ACTIVE) besprüht wurde, ändert das Kernel-Verhalten nach der Freigabe — das
Objekt wird konsultiert.sctp_outq_select_transport() beim Lesen von state=0xFFFF (Slab-Müll)
von einem veralteten Transportzeiger genau zum Injektionszeitpunkt. Konsequenz: Der
Spray muss den Cache vor dem Trigger vorgroomieren, nicht danach —
das Objekt wird gelesen, bevor die Gnadenfrist auf diesem Pfad je abläuft.*(p+0x288) mit p+0x288. Das ist ein Selbstzeiger. Du kannst
dich nicht daran vorbeisprühen, ohne die Slab-Adresse zu kennen, d. h.
dieser Bug braucht einen Info-Leak, bevor af_specific-Kontrolle zu
Instruction-Pointer-Kontrolle wird. af_specific selbst ist eine indirekte Aufrufstelle
(*(af_specific+0x18) mit rdi = transport), das ist also der Preis,
sobald ein Leak existiert.setsockopt(SCTP_AUTH_KEY) mit einem 800-Byte-
Schlüssel landet im selben kmalloc-1k-Cache. Denk daran, der Header ist
struct sctp_authkey { assoc_id; keynumber; keylength; key[] } und
AUTH muss pro Socket zuerst über SCTP_AUTH_SUPPORTED mit einem
sctp_assoc_value aktiviert werden — ein einfaches int ergibt EINVAL.Dies ist ein Trigger-/Crash-PoC. Es ist keine Privilegieneskalation, und dieses Repo beansprucht keine. Führe es gegen deine eigenen Kernel in einer VM aus.
Getestet auf: 7.1.5+kali-amd64 (Kali rolling) und 7.0.12+kali-amd64.
GPL-2.0 für die kernelabgeleiteten Teile der Logik; mit dem Rest kannst du machen, was du willst. Nur für autorisierte Sicherheitstests und Forschung.