
PoC sin privilegios para CVE-2026-74586, un use-after-free en SCTP ASCONF del kernel de Linux. Proporciona un disparador de paquetes sin procesar, métricas de fiabilidad y un análisis detallado de armamento, incluidos desplazamientos de estructuras y primitivas de spray.
Un PoC autónomo y sin privilegios para CVE-2026-74586, un use-after-free en
el manejo de ASCONF (reconfiguración dinámica de direcciones) de SCTP en el
kernel de Linux. El crédito por el fallo en sí corresponde a Qing Ming,
quien lo reportó upstream; el parche es el commit beb33f8ee1ca ("sctp: clear
new_transport when removing a peer"), fusionado el 2026-08-12 con
Cc: stable — Red Hat lo califica como importante, y rastreadores de
terceros lo sitúan en CVSS 9.8.
Lo que este repositorio añade además del aviso:
sctp_process_asconf_param() almacena el transporte creado por un parámetro
ADD-IP en asoc->new_transport. Un DEL-IP para la misma dirección en el
mismo chunk ASCONF libera ese transporte mediante ,
que (antes del parche) no limpia . Cuando
retorna, actúa sobre el puntero
obsoleto — en un kernel estándar el efecto visible es un HEARTBEAT emitido
hacia la dirección del transporte recién liberado; bajo KASAN es un informe
de slab-use-after-free.
sctp_assoc_rm_peer()new_transportsctp_process_asconf()sctp_sf_do_asconf()Un solo chunk ASCONF es suficiente:
[Address Parameter L] [ADD-IP G] [DEL-IP G]
gcc -O2 -Wall -o poc poc.c
./poc
Sin root. El PoC desvincula un namespace de usuario y de red, establece los sysctls de SCTP en él, levanta una asociación multi-homed sobre loopback y luego inyecta el ASCONF manipulado con un socket crudo.
Salida esperada en un kernel vulnerable:
[+] 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)
El HEARTBEAT después del ACK es la parte interesante: ese paquete solo
existe porque el kernel recorrió el new_transport colgante. El cliente
emite ABORT justo después porque el heartbeat regresó de la nada
(out-of-the-blue).
Nada de esto es investigación novedosa, simplemente está lo bastante poco documentado como para costar una tarde, así que aquí está para la próxima persona:
sctp_rcv() verifica la suma de comprobación y
descarta si no coincide. Calcula CRC32C (poly 0x82F63B78, reflejado)
sobre todo el paquete SCTP con el campo de suma de comprobación en cero.sctp_auth_recv_cid() se ejecuta en
la ruta de entrada antes de la máquina de estados y descarta ASCONF no
autenticado incluso cuando addip_noauth_enable=1 — ese sysctl solo
relaja la comprobación dentro de sctp_sf_do_asconf(). La salida es no
negociar AUTH a nivel de socket en primer lugar (sin setsockopt de
SCTP_AUTH_SUPPORTED), de modo que peer.auth_capable permanece en
false y ambas comprobaciones se superan.La etiqueta Fixes: del commit upstream es 6af29ccc223b ("sctp: Bundle
HEARTBEAT into ASCONF_ACK"), así que el código es antiguo. Corregido en
mainline y con backport a la serie estable (rastreador de Debian: corregido
desde 7.1.9-1 en sid, 6.12.107 en trixie-security). Kali rolling a fecha de
2026-09-09 incluye 7.1.5-1kali1 en todas las suites, lo cual precede a todo
esto — esa es la relevancia práctica principal de este PoC.
Para quien quiera llevarlo más lejos — medido contra 7.1.5 en QEMU, offsets
de objdump -d del sctp.ko incluido:
| campo | offset |
|---|---|
| flowi (88 bytes) | 0x30 |
| ipaddr | 0x88 |
| af_specific | 0xa8 |
| asoc | 0xb0 |
| dst | 0xe0 |
| state | 0x15c |
| rcu | 0x2b0 |
sctp_association->new_transport se sitúa en 0x6b0. La tabla proviene de
pahole -C sctp_transport /sys/kernel/btf/vmlinux en el kernel en
ejecución — una revisión anterior de esta tabla llevaba un offset de ipaddr
incorrecto (0x68, por contar mal flowi); BTF es la fuente de verdad.
sctp_transport_destroy_rcu), no
hay doble liberación en esta ruta. Si esperabas cerrar un socket y obtener
dos liberaciones — sctp_association_free() solo recorre la lista de
transportes, y rm_peer() ya desvinculó el fantasma. No pierdas una
noche en eso.sctp_outq_select_transport() lee state en +0x15c desde
chunk->transport en +0xe8 del chunk. Un objeto reclamado rociado con
state=1 (ACTIVE) cambia el comportamiento del kernel después de la
liberación — el objeto está siendo consultado.sctp_outq_select_transport()
leyendo state=0xFFFF (basura de slab) desde un puntero de transporte
obsoleto justo en el momento de la inyección. Consecuencia: el rociado
necesita pre-acondicionar la caché antes del disparador, no después —
el objeto se lee antes de que el período de gracia expire en esta ruta.*(p+0x288) contra p+0x288. Eso es un auto-puntero. No
puedes rociar para superarlo sin conocer la dirección del slab, es decir,
este fallo necesita una fuga de información antes de que el control de
af_specific se convierta en control del puntero de instrucción.
af_specific en sí es un sitio de llamada indirecta (*(af_specific+0x18)
con rdi = transporte), así que ese es el premio una vez que exista una
fuga.setsockopt(SCTP_AUTH_KEY) con una
clave de 800 bytes aterriza en la misma caché kmalloc-1k. Recuerda que la
cabecera es struct sctp_authkey { assoc_id; keynumber; keylength; key[] }
y AUTH necesita habilitarse por socket primero mediante
SCTP_AUTH_SUPPORTED con un sctp_assoc_value — un int simple devuelve
EINVAL.Esto es un PoC de disparador/fallo. No es una escalada de privilegios y este repositorio no reivindica una. Ejecútalo contra tus propios kernels en una VM.
Probado en: 7.1.5+kali-amd64 (Kali rolling) y 7.0.12+kali-amd64.
GPL-2.0 para las porciones de la lógica derivadas del kernel; haz lo que quieras con el resto. Solo para pruebas de seguridad autorizadas e investigación.