
Prova de conceito sem privilégios para CVE-2026-74586, um use-after-free no SCTP ASCONF do kernel Linux. Fornece um acionador de pacotes brutos, métricas de confiabilidade e análise detalhada de weaponization, incluindo offsets de structs e primitivas de spray.
Um PoC autocontido e sem privilégios para CVE-2026-74586, um use-after-free no
tratamento de ASCONF (reconfiguração dinâmica de endereços) do SCTP no kernel
Linux. O crédito pelo bug em si é de Qing Ming, que o reportou upstream; a
correção é o commit beb33f8ee1ca ("sctp: clear new_transport when removing a
peer"), mesclado em 2026-08-12 com Cc: stable — a Red Hat o classifica como
importante, rastreadores de terceiros o colocam em CVSS 9.8.
O que este repositório acrescenta além do advisory:
sctp_process_asconf_param() armazena o transport criado por um parâmetro ADD-IP
em asoc->new_transport. Um DEL-IP para o mesmo endereço no
mesmo chunk ASCONF libera esse transport através de sctp_assoc_rm_peer(),
que (antes da correção) não limpa new_transport. Quando
sctp_process_asconf() retorna, sctp_sf_do_asconf() age sobre o
ponteiro obsoleto — em um kernel padrão o efeito visível é um HEARTBEAT emitido
em direção ao endereço do transport recém-liberado; sob KASAN é um
relatório de slab-use-after-free.
Um único chunk ASCONF é suficiente:
[Address Parameter L] [ADD-IP G] [DEL-IP G]
gcc -O2 -Wall -o poc poc.c
./poc
Sem root. O PoC faz unshare de um user + network namespace, define os sysctls de SCTP nele, estabelece uma associação multi-homed sobre loopback e então injeta o ASCONF forjado com um raw socket.
Saída esperada em um kernel vulnerável:
[+] 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)
O HEARTBEAT após o ACK é a parte interessante: esse pacote só
existe porque o kernel percorreu o new_transport pendente. O cliente
envia ABORT logo em seguida porque o heartbeat surgiu do nada.
Nada disso é pesquisa inédita, é apenas pouco documentado o suficiente para custar uma tarde, então aqui está para a próxima pessoa:
sctp_rcv() verifica o checksum e descarta em caso de
divergência. Calcule o CRC32C (poly 0x82F63B78, refletido) sobre todo o
pacote SCTP com o campo de checksum zerado.sctp_auth_recv_cid() é executada no caminho de entrada
antes da máquina de estados e descarta ASCONF não autenticado mesmo
quando addip_noauth_enable=1 — esse sysctl apenas relaxa a verificação
dentro de sctp_sf_do_asconf(). A saída é nunca negociar AUTH
no nível do socket em primeiro lugar (sem SCTP_AUTH_SUPPORTED
setsockopt), para que peer.auth_capable permaneça falso e ambas as verificações passem.A tag Fixes: no commit upstream é 6af29ccc223b ("sctp: Bundle
HEARTBEAT into ASCONF_ACK"), então o código é antigo. Corrigido no mainline e
backportado para a série stable (Debian tracker: corrigido a partir de 7.1.9-1 no
sid, 6.12.107 no trixie-security). O Kali rolling em 2026-09-09 traz
7.1.5-1kali1 em todas as suítes, que é anterior a tudo isso — essa é a principal
relevância prática deste PoC.
Para quem quiser levar adiante — medido contra 7.1.5 no QEMU, offsets
de objdump -d do sctp.ko distribuído:
| campo | offset |
|---|---|
| flowi (88 bytes) | 0x30 |
| ipaddr | 0x88 |
| af_specific | 0xa8 |
| asoc | 0xb0 |
| dst | 0xe0 |
| state | 0x15c |
| rcu | 0x2b0 |
sctp_association->new_transport fica em 0x6b0. A tabela vem de
pahole -C sctp_transport /sys/kernel/btf/vmlinux no kernel em execução —
uma revisão anterior desta tabela trazia um offset de ipaddr errado (0x68,
por erro de contagem do flowi); o BTF é a fonte da verdade.
sctp_transport_destroy_rcu), não
há double-free neste caminho. Se você esperava fechar um socket
e obter dois frees — sctp_association_free() apenas percorre a lista
de transportes, e rm_peer() já desvinculou o ghost. Não gaste uma
noite nisso.sctp_outq_select_transport() lê state em +0x15c a partir de
chunk->transport em +0xe8 do chunk. Um objeto reclamado e pulverizado
com state=1 (ACTIVE) altera o comportamento do kernel após o free — o
objeto está sendo consultado.sctp_outq_select_transport() lendo state=0xFFFF (lixo do slab)
a partir de um ponteiro de transporte obsoleto exatamente no momento da injeção. Consequência: o
spray precisa pré-preparar o cache antes do gatilho, não depois —
o objeto é lido antes que o período de graça expire neste caminho.*(p+0x288) com p+0x288. Isso é um auto-ponteiro. Você
não consegue passar por isso com spray sem conhecer o endereço do slab, ou seja,
este bug precisa de um vazamento de informação antes que o controle de af_specific se torne
controle de ponteiro de instrução. O próprio af_specific é um local de chamada indireta
(*(af_specific+0x18) com rdi = transport), então esse é o prêmio
uma vez que exista um vazamento.setsockopt(SCTP_AUTH_KEY) com uma chave de 800 bytes
cai no mesmo cache kmalloc-1k. Lembre-se de que o cabeçalho é
struct sctp_authkey { assoc_id; keynumber; keylength; key[] } e
o AUTH precisa ser habilitado por socket primeiro via SCTP_AUTH_SUPPORTED com um
sctp_assoc_value — um int simples retorna EINVAL.Este é um PoC de gatilho/crash. Não é uma escalada de privilégios e este repositório não reivindica uma. Execute-o contra seus próprios kernels em uma VM.
Testado em: 7.1.5+kali-amd64 (Kali rolling) e 7.0.12+kali-amd64.
GPL-2.0 para as porções da lógica derivadas do kernel; faça o que quiser com o resto. Apenas para testes e pesquisas de segurança autorizados.
acklock.c — soft-lockup de ASCONF-ACK (não corrigido em TODOS os 7.1.x)