
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 autônomo 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 pela falha em si vai para Qing Ming, que a
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 a classifica como importante, e rastreadores
de terceiros a pontuam como CVSS 9.8.
O que este repositório acrescenta além do aviso:
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 por meio de
sctp_assoc_rm_peer(), que (antes da correção) não limpa
. Quando retorna,
age sobre o ponteiro obsoleto — em um kernel de
distribuiçã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.
new_transportsctp_process_asconf()sctp_sf_do_asconf()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 desfaz o compartilhamento de um namespace de usuário + rede, define os sysctls do SCTP nele, estabelece uma associação multi-homed via loopback e então injeta o ASCONF forjado com um socket bruto.
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 emite
ABORT logo em seguida porque o heartbeat voltou fora do esperado.
Nada disso é pesquisa nova, é 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 o pacote SCTP inteiro 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 só
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 o setsockopt
SCTP_AUTH_SUPPORTED), 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 na
mainline e com backport para as séries estáveis (rastreador Debian:
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, o que
antecede 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 contagem errada de 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
duas liberações — sctp_association_free() só percorre a lista de
transports, e rm_peer() já desvinculou o fantasma. Não gaste uma
noite com isso.sctp_outq_select_transport() lê state em +0x15c a partir de
chunk->transport em +0xe8 do chunk. Um objeto recuperado com spray
de state=1 (ACTIVE) altera o comportamento do kernel após a
liberação — o objeto está sendo consultado.sctp_outq_select_transport()
lendo state=0xFFFF (lixo de slab) de um ponteiro de transport
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 de o período de graça expirar neste caminho.*(p+0x288) com p+0x288. Isso é um auto-ponteiro. Você não
consegue passar por ele com spray sem saber o endereço do slab, ou
seja, esta falha precisa de um vazamento de informação antes de o
controle de af_specific se transformar em controle do 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/queda. 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 derivadas do kernel da lógica; faça o que quiser com o restante. Apenas para testes de segurança autorizados e pesquisa.