
Uma vulnerabilidade relacionada ao Linux
O que é este repositório: minha reprodução prática e estudo do CVE-2023-32233, um use-after-free no subsistema Netfilter nf_tables do kernel Linux que permite escalonamento de privilégios local, divulgado publicamente em maio de 2023 por Patryk Sondej e Piotr Krysiuk.
Crédito: o PoC (exploit.c) e o artigo original são de
@Liuk3r. Meu trabalho neste
repositório é reproduzir o exploit em meu próprio laboratório (Ubuntu 23.04,
kernel 6.2.0-20) e documentar a causa-raiz e a cadeia de exploração abaixo.
Todos os testes foram feitos em máquinas de laboratório dedicadas.
O nf_tables processa atualizações de configuração como um lote atômico. A validação de cada operação contra as mudanças de estado das operações anteriores no mesmo lote é insuficiente. Concretamente:
nft_rule contendo uma expressão lookup em um
nft_set anônimo que contém alguns elementos.NFT_MSG_DELRULE — exclui a regra, que implicitamente exclui a
expressão lookup e o nft_set anônimo;NFT_MSG_DELSETELEM — exclui um elemento do conjunto anônimo
já excluído.nf_tables_commit_release() enfileira recursos
em nf_tables_destroy_list, processados posteriormente por
nf_tables_trans_destroy_work():
primeiro nft_commit_release() → nf_tables_rule_destroy() →
nft_lookup_destroy() → nft_set_destroy() → kvfree() libera o
nft_set;
depois, para NFT_MSG_DELSETELEM, nf_tables_set_elem_destroy()
chama nft_set_elem_ext(), que desreferencia o nft_set
liberado:
Se set->ops->elemsize for corrompido, um local de memória escolhido pelo
atacante é interpretado como um nft_set_ext — a primitiva sobre a qual
todo o resto é construído.
nf_tables_trans_destroy_work() executando em
uma thread de trabalho em segundo plano: insira uma operação grande de
destruição do conjunto como atraso controlado, mantenha outras CPUs
ocupadas e realoque o chunk nft_set liberado na mesma CPU com um
nft_set de um tipo diferente (elemsize diferente) → confusão de
tipo.nft_set_ext corrompidos com offsets fora do
intervalo para que nf_tables_set_elem_destroy() percorra blocos
adjacentes como uma lista de nft_expr a destruir.nft_log com NFTA_LOG_PREFIX controlado;
nft_log_destroy() libera priv->prefix, fornecendo uma primitiva de
alocação sobreposta em kmalloc-{8..192} (limitada por byte NULL no
início).nft_object->udata para eliminar a restrição de byte
NULL na leitura por ponteiro solto.Notavelmente, as primitivas escolhidas evitam qualquer coisa que a CFI bloquearia — nenhum sequestro de chamada indireta é necessário em nenhuma etapa.
SLAB_FREELIST_HARDENED), INIT_ON_FREE e restrições de reutilização
de slab por CPU atacam a etapa 1; CFI (onde presente) restringe a etapa 3
em diante.nf_tables contendo DELRULE+DELSETELEM em conjuntos anônimos; triagem
de crash para nft_set_elem_ext / nf_tables_trans_destroy_work na
pilha.nftables exige CAP_NET_ADMIN,
mas namespaces de usuário sem privilégios o concedem — é por isso que
kernel.unprivileged_userns_clone=0 é um parâmetro de endurecimento
significativo.Testado no Ubuntu 23.04 (Lunar Lobster), kernel 6.2.0-20-generic.
sudo apt install gcc libmnl-dev libnftnl-dev
gcc -Wall -o exploit exploit.c -lmnl -lnftnl
O perfil embutido tem como alvo os kernels binários do Ubuntu 23.04
(linux-image-6.2.0-20-generic 6.2.0-20.20). Para testar outros kernels,
extraia os símbolos para um arquivo profile:
modprobe nf_tables
egrep ' (nft_counter_ops|nft_counter_destroy|free_percpu|modprobe_path)(\s|$)' /proc/kallsyms > profile
O layout do código de máquina de nft_counter_destroy() varia conforme o
compilador e as opções; consulte ORIGIN.md para a referência
completa de parâmetros (nft_counter_destroy_call_offset/mask/check) e os
ajustes finos da corrida (race_lead_sleep etc.). A probabilidade de
sucesso relatada é ≥80% em sistemas Intel bare-metal ociosos; algumas
microarquiteturas (por exemplo, Alder Lake) exigem ajuste adicional.
O PoC deixa o kernel em um estado instável, com memória corrompida, após uma execução bem-sucedida. Teste apenas em um sistema dedicado e descartável ou em um snapshot de VM — nunca em algo com dados que você valoriza.
exploit.c — código-fonte do PoC (por @Liuk3r)ORIGIN.md — artigo original sobre a vulnerabilidade e exploração (por Liuk3r)README.md — este arquivo: reprodução + minhas notas de análisePesquisa conduzida para fins defensivos/educacionais em um ambiente de laboratório isolado.
static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set,
void *elem)
{
return elem + set->ops->elemsize;
}
nft_dynset para dois tipos de expr com estado:
nft_counter — vaza nft_counter_ops → base de nf_tables.ko
(contorna o KASLR do módulo);nft_quota — o ponteiro consumed fornece leitura arbitrária
(NFT_MSG_GETSETELEM → nft_quota_do_dump() → NFTA_QUOTA_CONSUMED)
e escrita arbitrária (nft_overquota() adiciona skb->len a
*priv->consumed no tráfego de loopback).modprobe_path ("/sbin/modprobe" → "//tmp/modprobe") →
execução de processo root com conteúdo controlado pelo atacante.