
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 senza privilegi per CVE-2026-74586, un use-after-free
nella gestione ASCONF (riconfigurazione dinamica degli indirizzi) SCTP
del kernel Linux. Il merito del bug 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 valuta importante, i tracker di terze parti lo
collocano a CVSS 9.8.
Cosa aggiunge questo repository 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
. Quando restituisce il controllo,
agisce sul puntatore ormai stantio — su un kernel
stock l'effetto visibile è un HEARTBEAT emesso verso l'indirizzo del
transport appena liberato; sotto KASAN è un report slab-use-after-free.
new_transportsctp_process_asconf()sctp_sf_do_asconf()Basta un singolo chunk ASCONF:
[Address Parameter L] [ADD-IP G] [DEL-IP G]
gcc -O2 -Wall -o poc poc.c
./poc
Niente root. Il PoC crea un namespace utente + di rete, imposta i sysctl SCTP al suo interno, porta su una associazione multi-homed su loopback, poi inietta l'ASCONF costruito ad hoc con un socket raw.
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 attraversato il new_transport pendente. Il
client ABORT subito dopo perché l'heartbeat è arrivato out-of-the-blue.
Niente di tutto questo è ricerca nuova, è solo abbastanza poco documentato da costare un pomeriggio, quindi eccolo per il prossimo:
sctp_rcv() verifica il checksum e
scarta in caso di mismatch. 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
rilassa solo il controllo dentro sctp_sf_do_asconf(). La via
d'uscita è non negoziare mai AUTH a livello di socket (niente
setsockopt SCTP_AUTH_SUPPORTED), così peer.auth_capable resta
false ed 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 alle serie stable (tracker Debian: 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
questo — è la rilevanza pratica principale di questo PoC.
Per chi volesse spingersi oltre — misurato contro 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 viene 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 su 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() attraversa 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 rispetto a
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() mentre leggeva
state=0xFFFF (spazzatura slab) da un puntatore transport stantio
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 periodo di grazia scada su questo percorso.*(p+0x288) con p+0x288. È un self-pointer. Non
puoi superarlo con lo spray senza conoscere l'indirizzo slab, cioè
questo bug richiede una info leak prima che il controllo di
af_specific si trasformi in controllo del instruction pointer.
af_specific stesso è un sito di chiamata indiretta
(*(af_specific+0x18) con rdi = transport), quindi quello è il premio
una volta che esiste una 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 va abilitato prima per-socket tramite
SCTP_AUTH_SUPPORTED con una sctp_assoc_value — un semplice int
restituisce EINVAL.Questo è un PoC trigger/crash. Non è una escalation di privilegi e questo repository non ne rivendica una. 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 quello che vuoi con il resto. Solo per test di sicurezza autorizzati e ricerca.