
Use-After-Free no nf_tables do Kernel Linux (CVE-2026-23111) — PoC LPE
Uso-Após-Liberação (Use-After-Free) no nf_tables do Kernel Linux (Escalação Local de Privilégios) poc por baba01hacker
net/netfilter/nf_tables_api.c — nft_map_catchall_activate()| Faixa de Versão | Corrigido Em |
|---|---|
| 6.19-rc1 a 6.19-rc8 | 6.19-rc9+ |
| 6.13 a 6.18.9 | 6.18.10 |
| 6.7 a 6.12.69 | 6.12.70 |
| 6.4.1 a 6.5.x | EOL (sem correção estável) |
| 6.3.10 a 6.3.x | EOL |
| 6.0.x, 6.2.x | EOL |
| 6.1.36 a 6.1.162 | 6.1.163 |
| 5.15.121 a 5.15.199 | 5.15.200 |
| 5.10.188+, 5.4.262+, 4.19.316+ | Vários LTS |
CVE-2026-23111 é um uso-após-liberação no nf_tables causado por uma verificação invertida de máscara de geração em nft_map_catchall_activate(). Durante o rollback de uma transação falhada, elementos de mapa de veredito catchall que foram desativados por NFT_MSG_DELSET devem ser reativados para que referências como referências de cadeia NFT_GOTO sejam restauradas. O caminho de ativação catchall, em vez disso, pulou elementos inativos e processou os ativos, deixando elementos catchall desativados não restaurados.
A lógica de ativação não-catchall correta é:
if (nft_set_elem_active(ext, iter->genmask))
return 0; /* skip active, process inactive */
O caminho catchall vulnerável usou a condição oposta:
if (!nft_set_elem_active(ext, genmask))
continue; /* skip inactive, process active */
Quando um mapa de veredito pipapo excluído é revertido, isso impede que nft_setelem_data_activate() seja executado para o elemento catchall inativo. Para dados de veredito NFT_JUMP/NFT_GOTO, a referência de cadeia obtida por nft_data_hold() não é restaurada. Repetir a sequência aborto/alternar/excluir pode levar chain->use a zero enquanto outra regra ainda contém uma referência de veredito válida para a cadeia. Um DELCHAIN posterior pode então liberar a cadeia enquanto ela permanece acessível, criando o UAF.
Criar tabela, cadeia base, cadeia vítima e mapa de veredito pipapo com catchall NFT_GOTO -> vítima
|
Lote 1: excluir o conjunto pipapo, depois forçar um erro de transação
|
O caminho de aborto chama nft_map_catchall_activate(), mas a verificação invertida pula o catchall inativo
|
O catchall permanece inativo e a contagem de referência da cadeia vítima não é restaurada
|
Lote 2: confirmar uma transação benigna para alternar o cursor de geração
|
Lote 3: excluir o conjunto pipapo com sucesso, decrementando novamente a referência da cadeia vítima
|
Lote 4: excluir a cadeia vítima enquanto o veredito da cadeia base ainda a referencia
|
Uso-após-liberação quando a avaliação de pacotes ou a descarga de regras segue a referência de veredito pendente
A correção upstream é remover a negação para que nft_map_catchall_activate() corresponda a nft_mapelem_activate(): elementos ativos são ignorados, elementos inativos são reativados e referências de veredito/objeto são restauradas durante o tratamento de aborto.
python3 CVE-2026-23111-checker.py --detailed
python3 CVE-2026-23111-checker.py --json
wget https://raw.githubusercontent.com/Baba01hacker666/CVE-2026-23111/refs/heads/master/exploit_full.c && gcc -Wall -O2 -o exploit_full exploit_full.c -lnftnl -lmnl && ./exploit_full -d
Use esta se o PoC completo/v1 falhar no kernel alvo.
wget https://raw.githubusercontent.com/Baba01hacker666/CVE-2026-23111/refs/heads/master/exploit_v2.c && gcc -Wall -O2 -o exploit_v2 exploit_v2.c -lnftnl -lmnl && ./exploit_v2 -d
Instale as dependências de compilação primeiro se o compilador não conseguir encontrar os cabeçalhos libmnl/libmnl.h ou libnftnl:
scripts/install-build-deps.sh
# or
make deps
make # Compilar exploit (apenas PoC)
make v2 # Compilar exploit v2 (abordagem FuzzingLabs — tente quando v1 falhar)
make full # Compilar exploit LPE completo
make v3 # Compilar variante v3 calibrada de caminho de avaliação
make run-d # Executar PoC com saída de depuração
make run-v2-d # Executar v2 com saída de depuração
make run-v3 # Mostrar uso/ajuda da v3
make run-full # Executar LPE completo
Cria um conjunto de mapa pipapo com um elemento catchall (goto cadeia vítima), em seguida, usa o bug de genmask invertido durante um aborto de DELSET para corromper chain->use. Após avançar o contador de geração, DELCHAIN é bem-sucedido apesar das referências pendentes, liberando a cadeia.
Após o UAF, a memória chain->name é liberada. Pulverizamos estruturas seq_operations (32 bytes) abrindo /proc/self/stat para recuperar o slot de cache slab liberado. Ler de volta a regra de veredito imediata da cadeia base via NFT_MSG_GETRULE despeja o nome da cadeia recuperada, vazando ponteiros de função do kernel. A validação de ponteiro garante que apenas endereços canônicos de texto do kernel sejam aceitos.
Reaciona o UAF com um nome de cadeia mais longo (140 bytes → kmalloc-cg-192), em seguida, pulveriza objetos nft_rule para recuperar a memória. Os ponteiros list_head vazados revelam endereços heap na região de mapa direto.
nft_expr_ops e nft_rule_blob falsosblob_gen_0 da cadeia liberadacommit_creds(&init_cred) → switch_task_namespaces → swapgs; iretq/proc/kcoreexpr->ops->eval sequestradoO exploit v2 (exploit_v2.c) usa as técnicas descritas no artigo do FuzzingLabs e pode ser usado quando o PoC v1 falha. Principais diferenças:
| Componente | v1 (exploit.c) | v2 (exploit_v2.c) |
|---|---|---|
| Acionamento UAF | Conjunto único + regra goto imediata | Dois conjuntos (estilo FuzzingLabs) + regra de busca |
| Leitura de retorno KASLR | NFT_MSG_GETRULE (despejo de regra) | NFT_MSG_GETELEM (despejo de elemento) + fallback GETRULE |
| Vazamento heap | pulverização nft_rule + vazamento list_head | cadeia de leitura arbitrária init_ipc_ns |
| Hospedeiro ROP | regras nft + userdata de tabela | msg_msg-2k (mensagem de 2048 bytes) |
| Acionamento de sequestro | Avaliação de pacote em goto imediato | Avaliação de pacote no catchall de conjunto sobrevivente |
| Escalação de privilégios | commit_creds + switch_task_namespaces | substituição modprobe_path + desativação SELinux |
| Caminho de retorno | swapgs; iretq | msleep() (permanece no kernel) |
init_ipc_ns)init_ipc_ns + 0x118 → msg_ids.xa_head → msg_queue (ponteiro marcado)
msg_queue + 0xc0 → q_messages.next → endereço heap msg_msg