Skip to content
KitploitKITPLOIT
FerramentasBlog
Log in
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

FeedsContatoPrivacidade© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
TEST-CVE-2023-32233 — CVE-2023-32233 | Kitploit
Ferramentas/GitHubGitHub/rogeliopumajulca/test-cve-2023-32233
Forensia de MemóriaAnálise de VulnerabilidadesExploraçãoAprendizado e EducaçãoExploração de Binários
GitHubrogeliopumajulca/test-cve-2023-32233

TEST-CVE-2023-32233

CVE-2023-32233

Ver Repositório
14há 2 anosAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

Use-After-Free no Netfilter nf_tables ao processar requisições em lote

Detalhes da Vulnerabilidade

O código afetado é originário do kernel Linux oficial disponível em https://kernel.org/ e faz parte do componente Netfilter nf_tables (net/netfilter/nf_tables_api.c).

O Netfilter nf_tables permite atualizar sua configuração como uma operação atômica. Ao usar esse recurso, os clientes em modo de usuário enviam requisições em lote contendo uma lista de operações básicas. O Netfilter nf_tables então processa todas as operações dentro do lote como uma única transação. Ao processar o lote, o Netfilter nf_tables verifica as atualizações de estado da configuração para garantir que cada operação básica sucessiva seja válida, e isso também leva em conta as atualizações de estado de todas as operações anteriores dentro do lote. No entanto, a verificação atualmente implementada é insuficiente.

Em nosso cenário específico, começamos com uma configuração do Netfilter nf_tables que possui um nft_rule com expressão lookup em um nft_set anônimo, e onde o nft_set anônimo contém alguns elementos. Em seguida, enviamos uma requisição em lote contendo as seguintes duas operações básicas:

  1. Operação NFT_MSG_DELRULE para excluir o nft_rule.
    Observe que isso também exclui implicitamente a expressão lookup e o nft_set anônimo.
  2. Operação NFT_MSG_DELSETELEM para excluir qualquer um dos elementos do nft_set anônimo excluído.

A versão atual do Netfilter nf_tables aceita a requisição em lote acima. Em seguida, ela chama nf_tables_commit_release() que anexa recursos liberados à nf_tables_destroy_list. A nf_tables_destroy_list é então processada por nf_tables_trans_destroy_work() que primeiro desaloca recursos relacionados à operação NFT_MSG_DELRULE chamando:

nft_commit_release()
    nf_tables_rule_destroy()
        nf_tables_expr_destroy()
            expr->ops->destroy() que aponta para nft_lookup_destroy()
                nf_tables_destroy_set()
                    nft_set_destroy()
                        kvfree() que desaloca a memória usada por `nft_set`

antes de processar a operação NFT_MSG_DELSETELEM, onde a referência ao nft_set desalocado é acessada via nft_trans_elem_set() durante as seguintes chamadas:

nft_commit_release()
    nf_tables_set_elem_destroy()
        nft_set_elem_ext()

Dentro de nft_set_elem_ext() acima, a localização de memória do nft_set desalocado é acessada para determinar a localização de nft_set_ext:

static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set,
                                                   void *elem)
{
        return elem + set->ops->elemsize;
}

para as operações seguintes. Portanto, sempre que o valor de set->ops->elemsize for corrompido, uma localização de memória inesperada pode ser interpretada como uma lista de nft_expr a ser destruída:

static void nf_tables_set_elem_destroy(const struct nft_ctx *ctx,
                                       const struct nft_set *set, void *elem)
{
        struct nft_set_ext *ext = nft_set_elem_ext(set, elem);

        if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS))
                nft_set_elem_expr_destroy(ctx, nft_set_ext_expr(ext));

Técnicas de Exploração

Explorar a vulnerabilidade acima requer vencer uma condição de corrida com nf_tables_trans_destroy_work() que executa em uma thread de trabalho em segundo plano do kernel Linux. Isso parece complicar a exploração prática mesmo antes de considerarmos mitigações existentes, como o endurecimento do alocador de slabs do kernel, Randomização de Espaço de Endereçamento do Kernel (KASLR) e especialmente Integridade de Fluxo de Controle. No entanto, o PoC anexado prova que ainda é possível alcançar uma exploração razoavelmente confiável na prática.

Para explorar a vulnerabilidade, precisamos modificar o conteúdo da memória do nft_set depois que ele é desalocado em nf_tables_rule_destroy(), mas antes de ser usado em nf_tables_set_elem_destroy(). Ambas nf_tables_rule_destroy() e nf_tables_set_elem_destroy() são chamadas dentro de uma única invocação de nf_tables_trans_destroy_work() que executa em uma thread de trabalho em segundo plano do kernel Linux. Além disso, o bloco de memória desalocado geralmente está disponível para reutilização apenas a partir do mesmo núcleo de CPU.

Ao competir com nf_tables_trans_destroy_work(), melhoramos nossas chances adicionando um atraso controlado para a thread de trabalho em segundo plano entre suas chamadas nf_tables_rule_destroy() e nf_tables_set_elem_destroy(). Para isso, inserimos uma operação adicional para destruir outro nft_set contendo um grande número de elementos. Adicionalmente, mantemos todos os outros núcleos de CPU ocupados, de modo que a thread de trabalho em segundo plano provavelmente será escalonada em um núcleo de CPU específico, para que possamos tentar alocar uma nova estrutura a partir do mesmo núcleo de CPU logo após ele desalocar nft_set em nf_tables_rule_destroy(). Nosso objetivo é alocar um novo nft_set de tipo diferente para reutilizar a localização de memória do nft_set desalocado em nf_tables_rule_destroy().

O novo tipo de nft_set é selecionado para usar um valor diferente para set->ops->elemsize. Então, quando a thread de trabalho em segundo plano finalmente chama nf_tables_set_elem_destroy() para processar a operação NFT_MSG_DELSETELEM, ela interpreta seu argumento elem incorretamente, de modo que o nft_set_ext *ext corrompido está alguns bytes após a localização correta. Isso significa que determinados campos de dados controlados pelo usuário do nft_set_ext original são agora interpretados como cabeçalhos, resultando em confusão de tipo.

Uma maneira de abusar dessa confusão de tipo é crafting os cabeçalhos do nft_set_ext corrompido com valores de deslocamento tais que nf_tables_set_elem_destroy() interprete o conteúdo de quaisquer blocos de memória adjacentes como a lista de nft_expr a ser destruída através das seguintes chamadas:

nft_set_elem_expr_destroy()
    __nft_set_elem_expr_destroy()
        nf_tables_expr_destroy()
            expr->ops->destroy()

Neste ponto da exploração, ainda não temos detalhes do layout da memória do kernel. Portanto, não é possível criar endereços de ponteiro absolutos. No entanto, ao criar os cabeçalhos do nft_set_ext corrompido, ainda podemos usar deslocamentos fora do intervalo, de modo que expr->ops->destroy() seja chamado em certos nft_expr válidos nos blocos de memória adjacentes.

Para isso, espalhamos expressões nft_log, com NFTA_LOG_PREFIX controlado. Esse nft_log->prefix é então desalocado por nft_log_destroy() quando expr->ops->destroy() é chamado:

static void nft_log_destroy(const struct nft_ctx *ctx,
                            const struct nft_expr *expr)
{
        struct nft_log *priv = nft_expr_priv(expr);
        struct nf_loginfo *li = &priv->loginfo;

        if (priv->prefix != nft_log_null_prefix)
                kfree(priv->prefix);

Observe que ainda podemos acessar e até mesmo desalocar novamente essa memória através da outra referência da expressão nft_log espalhada.

Além disso, também podemos controlar o tamanho de nft_log->prefix, de modo que ele possa ser alocado a partir de qualquer um dos slabs kmalloc-{8, ..., 192}. Finalmente, a memória referida é interpretada como uma string de caracteres pelo kernel, então não há necessidade de se preocupar com corrupções ao sobrepor diferentes objetos sobre ela. Isso é essencialmente game over.

Baixar ferramenta