VirtualBox Slirp NAT Packet Heap Exploitation

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks

TL;DR

  • VirtualBox include un fork fortemente modificato di Slirp i cui packet buffers (mbufs) risiedono in un custom zone allocator con metadata inline e callback tramite puntatori a funzione (pfFini, pfDtor).
  • Una guest può riscrivere il valore m->m_len considerato trusted con una lunghezza dell’header IP controllata dall’attaccante, il che annulla tutti i successivi controlli di bounds e fornisce sia primitive di infoleak che di overwrite.
  • Abusando di pacchetti UDP con checksum 0 e ip_len sovradimensionato, la guest può esfiltrare le tail degli mbuf e i metadata dei chunk adiacenti per apprendere gli indirizzi dell’heap e del zone allocator.
  • Fornendo IP options appositamente costruite si costringe ip_stripoptions() a fare memcpy() di troppi dati in-place, così l’attaccante può sovrascrivere l’header struct item del mbuf successivo e puntare il suo campo zone verso dati completamente controllati.
  • Il free dell’mbuf corrotto attiva zone->pfFini() con argomenti forniti dall’attaccante; puntandolo su memcpy@plt si ottiene una primitiva di copy/write arbitraria che può essere diretta verso GOT entry o altri dati di controllo all’interno del binario VirtualBox non-PIE.

Packet allocator anatomy

VirtualBox allocates every ingress Ethernet frame from a per-interface zone named zone_clust. Each 0x800-byte data chunk is preceded by an inline header:

struct item {
uint32_t magic;      // 0xdead0001
void    *zone;       // uma_zone_t pointer with callbacks
uint32_t ref_count;
LIST_ENTRY(item) list; // freelist / used list links
};

Quando un mbuf viene liberato lo stack di chiamate m_freem -> ... -> slirp_uma_free() si fida dell’header inline:

  1. uma_zfree_arg() ricalcola item = (struct item *)mem - 1 e dovrebbe validare item->zone, ma Assert() è compilato fuori nelle build di release.
  2. slirp_uma_free() carica zone = item->zone ed esegue incondizionatamente zone->pfFini(zone->pData, data_ptr, zone->size) seguito da zone->pfDtor(...).

Pertanto, qualsiasi write-what-where nell’header dell’mbuf si traduce in una chiamata indiretta controllata durante free().

Infoleak via m->m_len override

All’inizio di ip_input() VirtualBox ha aggiunto:

if (m->m_len != RT_N2H_U16(ip->ip_len))
m->m_len = RT_N2H_U16(ip->ip_len);

Perché l’assegnazione avviene prima di verificare l’intestazione IP, un guest può dichiarare qualsiasi lunghezza fino a 0xffff. Il resto dello stack (ICMP, UDP, gestori di frammentazione, ecc.) assume che m->m_len sia affidabile e lo usa per decidere quanti byte copiare dall’mbuf.

Usare pacchetti UDP con checksum 0 (significa “no checksum”). Il fast-path del NAT inoltra m->m_len byte senza ispezionare l’integrità del payload, quindi gonfiare ip_len fa sì che Slirp legga oltre il buffer reale e restituisca residui di heap al guest o a un helper esterno cooperante oltre il NAT. Poiché la dimensione dei chunk è 2048 byte, il leak può includere:

  • L’struct item inline dell’mbuf successivo, rivelando l’ordine della freelist e il puntatore zone reale.
  • Heap cookies come i campi magic, utili per costruire header dall’aspetto valido quando si eseguono corruzioni successivamente.

Overwriting neighbouring chunk headers with IP options

Lo stesso valore di lunghezza fasullo può essere trasformato in una primitive di overwrite forzando il pacchetto attraverso ip_stripoptions() (scatenato quando l’header IP ha options e il payload è UDP/TCP). L’helper calcola una lunghezza di copia da m->m_len e poi chiama memcpy() per spostare l’header di trasporto sopra le options rimosse:

  1. Fornire un lungo ip_len così che la lunghezza di move calcolata si estenda oltre l’attuale mbuf.
  2. Includere un piccolo numero di IP options in modo che Slirp entri nel percorso di stripping.
  3. Quando memcpy() viene eseguito, legge dall’mbuf successivo e scrive sul payload dell’mbuf corrente e sull’header inline, corrompendo magic, zone, ref_count, ecc.

Poiché l’allocator mantiene i pacchetti della stessa interfaccia contigui sulla freelist, questo overflow colpisce deterministically il chunk successivo dopo un moderato heap grooming.

Forging uma_zone_t to hijack pfFini

Una volta che il struct item adiacente è corrompibile, l’exploit procede come segue:

  1. Usare indirizzi heap leakati per costruire una struttura uma_zone fasulla dentro un mbuf completamente controllato dal guest. Popolare:
    • pfFini con la voce PLT di memcpy().
    • pData con il puntatore di destinazione desiderato (es. voce GOT, slot di vtable, array di puntatori a funzione).
    • size con il numero di byte da copiare.
    • Opzionale: pfDtor come chiamata di secondo stadio (es. per invocare il puntatore a funzione appena scritto).
  2. Sovrascrivere il campo zone dell’mbuf target con il puntatore alla struttura fasulla; adattare i puntatori list in modo che la bookkeeping della freelist rimanga sufficientemente consistente per evitare crash.
  3. Freeare l’mbuf. slirp_uma_free() ora esegue memcpy(dest=pData, src=item_data, n=size) mentre l’mbuf contiene ancora dati controllati dal guest, ottenendo un arbitrary write.

Poiché il binario VirtualBox su Linux non è PIE, gli indirizzi PLT per memcpy e system sono fissi e possono essere usati direttamente. Il guest può anche nascondere stringhe come /bin/sh dentro un altro mbuf che rimane referenziato quando la chiamata hijackata viene eseguita.

Heap grooming via fragmentation

La zona per-interfaccia di Slirp è profonda 3072 chunk ed inizialmente ricavata come un array contiguo la cui freelist viene percorsa da indirizzi alti verso il basso. Si può ottenere adjacency deterministica tramite:

  • Inondare il NAT con molti frammenti IP_MF di dimensione costante in modo che il codice di riassemblaggio allochi sequenze di mbuf prevedibili.
  • Riciclare chunk specifici inviando frammenti che scadono, forzando le free a rientrare nella freelist in ordine LIFO.
  • Usare la conoscenza del cammino della freelist per posizionare l’mbuf futuro vittima subito dopo l’mbuf che conterrà l’overflow delle opzioni IP.

Questo grooming assicura che l’overflow colpisca il struct item target e che la uma_zone fasulla rimanga entro i confini della primitive di leak.

From arbitrary write to host code execution

Con la primitive memcpy-on-free:

  1. Copiare una stringa /bin/sh controllata dall’attacker e il buffer dei comandi in un mbuf stabile.
  2. Usare la primitive per sovrascrivere una voce GOT o un callsite indiretto (es. un puntatore a funzione nello stato del dispositivo NAT) con la voce PLT di system().
  3. Scatenare la chiamata sovrascritta. Poiché VirtualBox esegue il dispositivo NAT all’interno del processo host, il payload viene eseguito con i privilegi dell’utente che esegue VirtualBox, permettendo una guest-to-host escape.

Payload alternativi includono il piazzare una mini ROP chain nella memoria heap e copiarne l’indirizzo in una callback frequentemente invocata, oppure ripuntare pfFini/pfDtor stessi su gadget concatenati per scritture ripetute.

References

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks