
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, unprivilegierter PoC für CVE-2026-74586, einen Use-after-free in
der SCTP-ASCONF-Behandlung (dynamische Adressrekonfiguration) des Linux-Kernels.
Das Verdienst für den Fehler selbst gebührt Qing Ming, der ihn upstream gemeldet hat; der
Fix ist Commit beb33f8ee1ca („sctp: clear new_transport when removing a
peer"), gemergt am 2026-08-12 mit Cc: stable — Red Hat stuft ihn als wichtig ein,
Drittanbieter-Tracker setzen ihn bei CVSS 9.8 an.
Was dieses Repo zusätzlich zur Advisory bietet:
sctp_process_asconf_param() speichert den Transport, der durch einen ADD-IP-Parameter
erzeugt 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,
das (vor dem Fix) new_transport nicht zurücksetzt. Wenn
sctp_process_asconf() zurückkehrt, agiert sctp_sf_do_asconf() 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-Bericht.
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. Das PoC hebt die Freigabe eines User- und Netzwerk-Namespace auf, setzt darin die SCTP-Sysctls, baut eine Multi-Homed-Assoziation über Loopback auf und injiziert dann die präparierte 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
sendet direkt danach ein ABORT, weil der Heartbeat völlig unerwartet zurückkam.
Nichts davon ist neue Forschung, es ist nur so schlecht dokumentiert, dass es einen Nachmittag kostet, also hier für den Nächsten:
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 Input-
Pfad vor der State Machine und verwirft nicht authentifizierte ASCONF selbst
dann, wenn addip_noauth_enable=1 — dieses Sysctl lockert nur die Prüfung
innerhalb von sctp_sf_do_asconf(). Der Ausweg besteht darin, AUTH
auf Socket-Ebene von vornherein nie auszuhandeln (kein SCTP_AUTH_SUPPORTED
setsockopt), sodass peer.auth_capable false bleibt und beide Prüfungen durchlaufen.Das Fixes:-Tag im Upstream-Commit ist 6af29ccc223b ("sctp: Bundle
HEARTBEAT into ASCONF_ACK"), der Code ist also alt. Behoben in Mainline und
in die Stable-Serie zurückportiert (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 allen Suites aus, was all dem vorausgeht — das ist die wichtigste
praktische Relevanz dieses PoC.
Für alle, die es weiter treiben wollen — 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 enthielt einen falschen ipaddr-Offset (0x68,
durch Fehlzählen von flowi); BTF ist die Ground Truth.
sctp_transport_destroy_rcu), es
gibt keinen Double-Free auf diesem Pfad. Falls 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 Ghost bereits
aus der Liste entfernt. Darauf musst du keinen Abend verschwenden.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 Kernelverhalten nach dem
Free — das Objekt wird konsultiert.sctp_outq_select_transport()
state=0xFFFF (Slab-Müll) von einem veralteten Transport-Zeiger genau zum
Injektionszeitpunkt las. Konsequenz: Der Spray muss den Cache vor dem
Trigger vorbereiten, nicht danach — das Objekt wird gelesen, bevor die
Grace Period auf diesem Pfad überhaupt abläuft.*(p+0x288) mit p+0x288. Das ist ein Selbstzeiger. Du kannst
dich nicht per Spray daran vorbeiarbeiten, ohne die Slab-Adresse zu kennen,
d. h. dieser Bug benötigt ein Info-Leak, bevor aus af_specific-Kontrolle
Instruktionszeiger-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, dass der
Header struct sctp_authkey { assoc_id; keynumber; keylength; key[] } ist
und AUTH zuerst pro Socket über SCTP_AUTH_SUPPORTED mit einem
sctp_assoc_value aktiviert werden muss — ein einfacher int liefert 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 vom Kernel abgeleiteten Teile der Logik; mit dem Rest kannst du machen, was du willst. Nur für autorisierte Sicherheitstests und -forschung.
acklock.c — ASCONF-ACK-Soft-Lockup (in ALLEN 7.1.x ungefixt)