
Proof-of-concept non privilegiato per CVE-2026-74586, una use-after-free SCTP ASCONF nel kernel Linux. Fornisce un trigger a pacchetti raw, metriche di affidabilità e un'analisi dettagliata della weaponization, inclusi offset delle struct e primitive di spray.
Un PoC autonomo e non privilegiato per CVE-2026-74586, un use-after-free nella
gestione di SCTP ASCONF (riconfigurazione dinamica degli indirizzi) del kernel
Linux. Il merito del bug stesso va a Qing Ming, che lo ha segnalato upstream; la
correzione è il commit beb33f8ee1ca ("sctp: clear new_transport when removing a
peer"), integrato il 2026-08-12 con Cc: stable — Red Hat lo classifica come importante,
i tracker di terze parti lo collocano a CVSS 9.8.
Cosa aggiunge questo repo rispetto all'advisory:
sctp_process_asconf_param() memorizza il transport creato da un parametro ADD-IP
in asoc->new_transport. Un DEL-IP per lo stesso indirizzo nello
stesso chunk ASCONF libera quel transport tramite sctp_assoc_rm_peer(),
che (prima della correzione) non azzera new_transport. Quando
sctp_process_asconf() ritorna, sctp_sf_do_asconf() agisce sul
puntatore obsoleto — su un kernel standard l'effetto visibile è un HEARTBEAT emesso
verso l'indirizzo del transport appena liberato; sotto KASAN è un
report di slab-use-after-free.
Un solo chunk ASCONF è sufficiente:
[Address Parameter L] [ADD-IP G] [DEL-IP G]
gcc -O2 -Wall -o poc poc.c
./poc
Nessun root. Il PoC esegue l'unshare di uno user + network namespace, imposta al suo interno i sysctl SCTP, attiva un'associazione multi-homed su loopback, quindi inietta l'ASCONF craftato con un raw socket.
Output atteso su un kernel vulnerabile:
[+] 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)
L'HEARTBEAT dopo l'ACK è la parte interessante: quel pacchetto esiste
solo perché il kernel ha percorso il new_transport pendente. Il client
esegue ABORT subito dopo perché l'heartbeat è arrivato inaspettatamente.
Niente di tutto ciò è ricerca innovativa, è solo sufficientemente non documentato da costare un pomeriggio, quindi eccolo qui per il prossimo:
sctp_rcv() verifica il checksum e scarta in caso di
mancata corrispondenza. Calcola CRC32C (poly 0x82F63B78, riflesso) sull'intero
pacchetto SCTP con il campo checksum azzerato.sctp_auth_recv_cid() viene eseguito nel percorso
di input prima della macchina a stati e scarta ASCONF non autenticati anche
quando addip_noauth_enable=1 — quel sysctl allenta solo il controllo
all'interno di sctp_sf_do_asconf(). La via d'uscita è non negoziare mai AUTH
a livello di socket fin dall'inizio (nessun SCTP_AUTH_SUPPORTED
setsockopt), così peer.auth_capable rimane false e entrambi i controlli passano.Il tag Fixes: sul commit upstream è 6af29ccc223b ("sctp: Bundle
HEARTBEAT into ASCONF_ACK"), quindi il codice è vecchio. Corretto in mainline e
backportato alla serie stabile (Debian tracker: corretto da 7.1.9-1 in
sid, 6.12.107 in trixie-security). Kali rolling al 2026-09-09 distribuisce
7.1.5-1kali1 in ogni suite, che precede tutto ciò — questa è la principale
rilevanza pratica di questo PoC.
Per chiunque voglia spingersi oltre — misurato rispetto a 7.1.5 in QEMU, offset
da objdump -d del sctp.ko distribuito:
| campo | offset |
|---|---|
| flowi (88 byte) | 0x30 |
| ipaddr | 0x88 |
| af_specific | 0xa8 |
| asoc | 0xb0 |
| dst | 0xe0 |
| state | 0x15c |
| rcu | 0x2b0 |
sctp_association->new_transport si trova a 0x6b0. La tabella proviene da
pahole -C sctp_transport /sys/kernel/btf/vmlinux sul kernel in esecuzione —
una revisione precedente di questa tabella riportava un offset ipaddr errato (0x68,
per un errore di conteggio di flowi); BTF è la verità di riferimento.
sctp_transport_destroy_rcu), non
c'è double-free su questo percorso. Se speravi di chiudere un socket
e ottenere due free — sctp_association_free() percorre solo la lista
dei transport, e rm_peer() ha già scollegato il ghost. Non perdere
una serata su questo.sctp_outq_select_transport() legge state a +0x15c da
chunk->transport a +0xe8 del chunk. Un oggetto reclamato e spruzzato
con state=1 (ACTIVE) cambia il comportamento del kernel dopo la free —
l'oggetto viene consultato.sctp_outq_select_transport() che leggeva state=0xFFFF
(spazzatura dello slab) da un puntatore a transport stale proprio al
momento dell'iniezione. Conseguenza: lo spray deve pre-groomare la
cache prima del trigger, non dopo — l'oggetto viene letto prima che
il grace period scada mai su questo percorso.*(p+0x288) con p+0x288. È un self-pointer. Non puoi
superarlo con lo spray senza conoscere l'indirizzo dello slab, cioè
questo bug richiede un info leak prima che il controllo di
af_specific diventi controllo dell'instruction pointer. af_specific
stesso è un sito di chiamata indiretta (*(af_specific+0x18) con
rdi = transport), quindi è il premio una volta che esiste un leak.setsockopt(SCTP_AUTH_KEY) con una
chiave da 800 byte finisce nella stessa cache kmalloc-1k. Ricorda che
l'header è struct sctp_authkey { assoc_id; keynumber; keylength; key[] }
e AUTH deve essere abilitato per-socket prima tramite SCTP_AUTH_SUPPORTED
con un sctp_assoc_value — un semplice int restituisce EINVAL.Questo è un PoC di trigger/crash. Non è un'escalation di privilegi e questo repo non ne rivendica alcuna. Eseguilo contro i tuoi kernel in una VM.
Testato su: 7.1.5+kali-amd64 (Kali rolling) e 7.0.12+kali-amd64.
GPL-2.0 per le porzioni della logica derivate dal kernel; fai ciò che vuoi con il resto. Solo per test e ricerca di sicurezza autorizzati.
acklock.c — soft-lockup da ASCONF-ACK (non corretto in TUTTE le 7.1.x)