Skip to content
KitploitKITPLOIT
FerramentasExploitsBlog
Log in
Enviar
FerramentasExploitsBlog
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.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2023-2598 — Análise técnica e exploit de prova de conceito para CVE-2023-2598, uma vulnerabilidade de escalonamento de privilégios no kernel Linux no registro de buffer do io_uring, com explicação detalhada dos internos de Compound Page e folio. | Kitploit
Ferramentas/GitHubGitHub/cainiao159357/cve-2023-2598
Escalada de PrivilégiosAnálise de VulnerabilidadesExploraçãoPapers e PesquisaAprendizado e EducaçãoExploração de Binários
GitHubcainiao159357/cve-2023-2598

CVE-2023-2598

Análise técnica e exploit de prova de conceito para CVE-2023-2598, uma vulnerabilidade de escalonamento de privilégios no kernel Linux no registro de buffer do io_uring, com explicação detalhada dos internos de Compound Page e folio.

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
Ver Repositório
5há 2 anosAinda não revisado

CVE-2023-2598 Elevação de privilégio

Entendendo o mecanismo de Compound Page e folio no Linux através do CVE-2023-2598, posteriormente verificar se é possível explorar o 1day CVE-2023-6560.

Compound Page (huge page)

A memória está cada vez maior, mas a unidade básica de alocação de página do Linux ainda é 4K, o que se torna insuficiente. Portanto, páginas compostas são introduzidas para resolver esse problema. Uma página composta é basicamente um conjunto de várias páginas, combinando duas ou mais páginas fisicamente contíguas em uma unidade que, em muitos aspectos, pode ser tratada como uma única página maior. Elas são mais comumente usadas para criar páginas grandes, utilizadas em hugetlbfs ou no subsistema de páginas grandes transparentes (transparent huge pages), mas também aparecem em outros cenários. Páginas compostas podem ser usadas como memória anônima ou como buffers no kernel; no entanto, elas não podem aparecer no page cache, que só pode lidar com páginas individuais.

Alocar uma página composta é chamar alloc_pages() e definir a flag __GFP_COMP com um número de quadros de página maior que 1, ou seja, order pelo menos 1. Isso é determinado pelo mecanismo de implementação da página composta.

Observação: as páginas compostas são necessariamente fisicamente contíguas

A flag no primeiro page marcará PG_head, indicando que esta é a página cabeça da página composta;

Todas as páginas subsequentes configurarão duas propriedades: mapping e compound_head, e através de compound_head é confirmado se é uma página cauda ou cabeça. Veja a função compound_head() para detalhes;

O segundo page armazenará mais informações da página composta, é por isso que a order da página composta é pelo menos 1;

static inline unsigned long _compound_head(const struct page *page)
{
        unsigned long head = READ_ONCE(page->compound_head);
 
        if (unlikely(head & 1))
                return head - 1;
        return (unsigned long)page;
}

Pode-se ver que este campo não apenas contém a flag, mas também o ponteiro para a head page.

Portanto, ao obter uma page, é fácil determinar se é uma página composta e, se for, se é head page ou tail page. No entanto, ainda falta uma informação crucial: o tamanho desta página composta. Se o tamanho não for conhecido, ao liberar esta página composta, é necessário saber seu tamanho. Todas essas informações são armazenadas no campo lru da primeira tail page: o tamanho (order) da página composta é primeiro convertido para um tipo de ponteiro e armazenado em lru.prev, e o destructor é armazenado em lru.next.

Desde que a head page e o tamanho da página composta sejam conhecidos, é possível liberar corretamente essa grande página, pois as páginas compostas são fisicamente contíguas.

A estrutura é mostrada na figura abaixo:

img

folio

folio pode ser visto como uma camada de empacotamento sobre a page, sem overhead. Um folio pode ser uma única página ou uma página composta.

img

A figura acima é um diagrama da estrutura page, com 64 bytes gerenciando informações como flags, lru, mapping, index, private, {ref_, map_}count, memcg_data, etc. Quando a page é uma página composta, essas informações de flags ficam na head page, enquanto as tail pages reutilizam o gerenciamento de compound_{head, mapcount, order, nr, dtor}, etc.

struct folio {
        /* private: don't document the anon union */
        union {
                struct {
        /* public: */
                        unsigned long flags;
                        struct list_head lru;
                        struct address_space *mapping;
                        pgoff_t index;
                        void *private;
                        atomic_t _mapcount;
                        atomic_t _refcount;
#ifdef CONFIG_MEMCG
                        unsigned long memcg_data;
#endif
        /* private: the union with struct page is transitional */
                };
                struct page page;
        };
};

Na definição da estrutura do folio, flags, lru e outras informações são exatamente iguais às da page, portanto podem ser colocadas em union com a page. Isso permite usar diretamente folio->flags em vez de folio->page->flags.

#define page_folio(p)           (_Generic((p),                          \
        const struct page *:    (const struct folio *)_compound_head(p), \
        struct page *:          (struct folio *)_compound_head(p)))

#define nth_page(page,n) ((page) + (n))
#define folio_page(folio, n)    nth_page(&(folio)->page, n)

À primeira vista, page_folio pode parecer confuso, mas é equivalente a:

switch (typeof(p)) {
  case const struct page *:
    return (const struct folio *)_compound_head(p);
  case struct page *:
    return (struct folio *)_compound_head(p)));
}

Através da macro page_folio, descobre-se que folio é na verdade uma head page de uma página composta. Quando folio é convertido para page, folio->page é usado para obter a head page, e folio_page(folio, n) pode ser usado para obter a tail page.

Então, para que serve o folio? Mais do que isso, é para desenvolvimento e eficiência. Sem o folio, a função não consegue determinar se a página atual é head page, então seria necessário chamar _compound_head. Se o caminho de execução for longo, cada função no caminho que usa _compound_head repetidamente afetaria a eficiência. No entanto, se a função aceitar apenas o parâmetro struct folio *, este folio aponta para a head page, então a função não precisa mais chamar _compound_head.

Portanto, existem três funções principais:

  1. Reduzir chamadas redundantes de compound_head.

  2. Dar uma dica ao desenvolvedor: ao ver folio, pode-se determinar que é uma head page.

  3. Corrigir potenciais bugs causados por tail page.

Principio da vulnerabilidade

No io_uring_register_buffer do io_uring, existe esta lógica:

image-20240830214419290

Quando a página passada pelo espaço do usuário é maior que 1, io_uring verifica se o buffer passado é um folio. O método de verificação é usar page_folio() para obter a head page de page[i]; se a head page de page[i] for igual a page[0], então considera-se que pertencem à mesma tabela de páginas compostas.

Geralmente, esse tratamento não apresenta problemas, mas existe um caso especial: se no espaço do usuário for usado mmap para mapear a mesma página física em endereços virtuais contíguos, isso também atende à condição de julgamento e, por fim, entra neste ramo:

image-20240830225137539

Neste momento, o espaço do usuário solicitou apenas uma página física, mas o tamanho final é o tamanho do endereço virtual contíguo, fazendo com que o size possa ser maior que a região de endereço físico realmente solicitada. Isso resulta em leitura/escrita fora dos limites.

Exploração da vulnerabilidade

Fazer spray de cred e, em seguida, usar esta interface de leitura/escrita fora dos limites para modificar o uid.

Comparado com o exp da internet, este exp, por modificar o uid, não depende de endereço; qualquer sistema com essa vulnerabilidade pode usar este exp.

Baixar ferramenta