Skip to content
KitploitKITPLOIT
StrumentiExploitsBlog
Log in
Invia
StrumentiExploitsBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2023-2598 — Analisi tecnica ed exploit proof-of-concept per CVE-2023-2598, una vulnerabilità di escalation di privilegi del kernel Linux nella registrazione buffer di io_uring, con spiegazione dettagliata di Compound Page e internals folio. | Kitploit
Strumenti/GitHubGitHub/cainiao159357/cve-2023-2598
Escalation di PrivilegiAnalisi delle VulnerabilitàExploitPaper e RicercaApprendimento e FormazioneBinary Exploitation
GitHubcainiao159357/cve-2023-2598

CVE-2023-2598

Analisi tecnica ed exploit proof-of-concept per CVE-2023-2598, una vulnerabilità di escalation di privilegi del kernel Linux nella registrazione buffer di io_uring, con spiegazione dettagliata di Compound Page e internals folio.

Vedi Repository
52 anni faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

Elevazione dei privilegi CVE-2023-2598

Attraverso la CVE-2023-2598 comprendere i meccanismi Compound Page e folio in Linux e, successivamente, verificare se è possibile completare lo sfruttamento della 1day CVE-2023-6560.

Compound Page (huge page)

La memoria cresce sempre di più, ma l'unità di allocazione delle pagine base di Linux è ancora di 4K, ormai inadeguata. Per risolvere questo problema sono state introdotte le compound page. Una compound page consiste in pratica nel trattare più pagine come un unico insieme: due o più pagine fisicamente contigue vengono combinate in un'unica unità che, per molti aspetti, può essere considerata una singola pagina più grande. Sono usate più comunemente per creare pagine di grandi dimensioni, nei sottosistemi hugetlbfs o delle Transparent Huge Pages, ma compaiono anche in altri scenari. Le compound page possono essere usate come memoria anonima o come buffer nel kernel; tuttavia, non possono comparire nella page cache, che può gestire solo singole pagine.

Per allocare una compound page si chiama alloc_pages() impostando il flag di allocazione __GFP_COMP e un numero di page frame maggiore di 1, cioè un order di almeno 1. È quanto determinato dal meccanismo di implementazione delle compound page.

Nota: una compound page è sempre fisicamente contigua.

Il flag nella prima page viene marcato con PG_head, che la contrassegna come pagina di testa della compound page;

Tutte le page successive configurano due attributi: mapping e compound_head; tramite compound_head si determina se si tratta di una tail page o della head page, come si vede nel dettaglio nella funzione compound_head();

Nella seconda page sono memorizzate ulteriori informazioni sulla compound page, ed è anche per questo che l'order di una compound page deve essere almeno 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;
}

Si vede quindi che questo campo contiene non solo un flag, ma anche un puntatore alla head page.

Quindi, quando si ottiene una page, è facile determinare se si tratta di una compound page e, in tal caso, se è una head page o una tail page. Manca però un'informazione fondamentale: la dimensione della compound page. Se non si conosce la dimensione della compound page, quando la si libera è necessario conoscerla. Tutte queste informazioni sono memorizzate nel campo lru della prima tail page: la dimensione (order) della compound page viene prima convertita forzatamente in un tipo puntatore, quindi memorizzata in lru.prev, mentre il distruttore (destructor) viene memorizzato in lru.next.

Finché si conoscono la head page e la dimensione della compound page, si può liberare correttamente la pagina grande, poiché le compound page sono sempre fisicamente contigue.

La struttura è illustrata nella figura seguente:

img

folio

Un folio può essere visto come un livello di involucro attorno alla page, senza alcun overhead. Un folio può essere una singola page o una compound page.

img

La figura sopra è uno schema della struttura page: 64 byte gestiscono informazioni come flags, lru, mapping, index, private, {ref_, map_}count, memcg_data. Quando la page è una compound page, le informazioni come i flags si trovano nella head page, mentre le tail page riutilizzano le informazioni di gestione come compound_{head, mapcount, order, nr, dtor}.

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;
        };
};

Nella definizione della struttura del folio, informazioni come flags e lru sono del tutto identiche a quelle della page, quindi possono essere unite alla page tramite una union. In questo modo si può usare direttamente folio->flags senza dover usare 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)

A prima vista page_folio può sembrare un po' sconcertante; in realtà equivale a:

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

Attraverso la definizione della macro page_folio, si può scoprire che un folio in realtà è la head page di una compound page. Quando un folio viene convertito in page, folio->page serve a ottenere la head page, mentre folio_page(folio, n) può essere usata per ottenere le tail page.

E allora a cosa serve il folio? Per lo più per ragioni di sviluppo ed efficienza. Senza il folio, una funzione non può determinare al suo interno se la page corrente è una head page, quindi deve chiamare _compound_head. Se i percorsi di esecuzione aumentano, il fatto che ogni funzione lungo il percorso chiami _compound_head incide sull'efficienza. Ma se una funzione accetta solo un parametro struct folio *, quel folio punta alla head page, quindi all'interno della funzione non è più necessario chiamare _compound_head.

Quindi svolge principalmente queste tre funzioni:

  1. Riduce le chiamate ridondanti a compound_head.

  2. Dà un suggerimento agli sviluppatori: vedere un folio significa avere la certezza che si tratta di una head page.

  3. Corregge potenziali bug causati dalle tail page.

Principio della vulnerabilità

In io_uring_register_buffer di io_uring c'è questa logica:

image-20240830214419290

Quando lo spazio utente passa più di una pagina, io_uring verifica se il buffer passato è un folio. Il metodo di verifica usa page_folio() per ottenere la head page di page[i]; se la head page di page[i] è uguale a quella di page[0], si ritiene che appartengano alla stessa compound page.

In generale questo trattamento è corretto, ma esiste un caso particolare: se nello spazio utente si usa mmap per mappare la stessa pagina fisica su indirizzi virtuali contigui, anche questa situazione soddisfa la condizione del controllo, quindi alla fine si entra in questo ramo:

image-20240830225137539

A questo punto, lo spazio utente ha allocato una sola pagina fisica, ma la size finale è quella dell'intervallo di indirizzi virtuali contigui, quindi la size può essere maggiore dell'area fisica effettivamente allocata. Ciò provoca infine letture/scritture fuori dai limiti.

Sfruttamento della vulnerabilità

Si esegue uno spray di strutture cred e poi si usa questa interfaccia di lettura/scrittura fuori dai limiti per modificare l'uid.

Scarica lo strumento