VirtualBox Slirp NAT Packet Heap Exploitation

Tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks

TL;DR

  • O VirtualBox traz um fork fortemente modificado do Slirp cujos buffers de pacotes (mbufs) vivem em um alocador de zona personalizado com metadados inline e callbacks por ponteiro de função (pfFini, pfDtor).
  • Um guest pode reescrever o confiável m->m_len com um comprimento de cabeçalho IP controlado pelo atacante, o que destrói todas as verificações de limites subsequentes e produz tanto infoleak quanto overwrite primitives.
  • Ao abusar de pacotes UDP com checksum 0 e ip_len excessivo, o guest pode exfiltrar as caudas dos mbufs e os metadados dos chunks vizinhos para descobrir endereços do heap e da zona.
  • Fornecer opções IP forjadas força ip_stripoptions() a memcpy() dados demais in-place, permitindo ao atacante sobrescrever o header struct item do próximo mbuf e apontar seu campo zone para dados totalmente controlados.
  • Liberar o mbuf corrompido aciona zone->pfFini() com argumentos fornecidos pelo atacante; apontá-lo para memcpy@plt fornece uma primitive de cópia/escrita arbitrária que pode ser direcionada a entradas GOT ou outros dados de controle dentro do binário VirtualBox não-PIE.

Packet allocator anatomy

O VirtualBox aloca cada frame Ethernet de entrada a partir de uma zona por interface chamada zone_clust. Cada chunk de dados de 0x800 bytes é precedido por um cabeçalho inline:

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 um mbuf é liberado a pilha de chamadas m_freem -> ... -> slirp_uma_free() confia no cabeçalho inline:

  1. uma_zfree_arg() recalcula item = (struct item *)mem - 1 e deveria validar item->zone, mas Assert() é removido em builds de release.
  2. slirp_uma_free() carrega zone = item->zone e executa incondicionalmente zone->pfFini(zone->pData, data_ptr, zone->size) seguido por zone->pfDtor(...).

Portanto, qualquer write-what-where no cabeçalho do mbuf se traduz em uma chamada indireta controlada durante o free().

Infoleak via m->m_len override

At the top of ip_input() VirtualBox added:

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

Because the assignment happens before verifying the IP header, a guest can advertise any length up to 0xffff. The rest of the stack (ICMP, UDP, fragmentation handlers, etc.) assumes m->m_len is trustworthy and uses it to decide how many bytes to copy off the mbuf.

Use UDP packets with checksum 0 (meaning “no checksum”). The NAT fast-path forwards m->m_len bytes without inspecting payload integrity, so inflating ip_len causes Slirp to read past the real buffer and return heap residues to the guest or to a cooperating external helper beyond the NAT. Because the chunk size is 2048 bytes, the leak can include:

  • The next mbuf’s inline struct item, revealing the freelist order and the real zone pointer.
  • Heap cookies such as magic fields, helping to craft valid-looking headers when performing corruptions later.

Overwriting neighbouring chunk headers with IP options

The same bogus length can be turned into an overwrite primitive by forcing the packet through ip_stripoptions() (triggered when the IP header has options and the payload is UDP/TCP). The helper computes a copy length from m->m_len and then calls memcpy() to slide the transport header over the stripped options:

  1. Supply a long ip_len so the computed move length extends past the current mbuf.
  2. Include a small number of IP options so Slirp enters the stripping path.
  3. When memcpy() runs, it reads from the following mbuf and writes over the current mbuf’s payload and inline header, corrupting magic, zone, ref_count, etc.

Because the allocator keeps packets from the same interface contiguous on the freelist, this overflow deterministically hits the next chunk after modest heap grooming.

Forging uma_zone_t to hijack pfFini

Once the adjacent struct item is corruptible, the exploit proceeds as follows:

  1. Use leaked heap addresses to build a fake uma_zone structure inside a mbuf fully controlled by the guest. Populate:
  • pfFini with the PLT entry of memcpy().
  • pData with the desired destination pointer (e.g. GOT entry, vtable slot, function pointer array).
  • size with the number of bytes to copy.
  • Optional: pfDtor as a second stage call (e.g. to invoke the newly-written function pointer).
  1. Overwrite the target mbuf’s zone field with the pointer to the fake structure; adjust list pointers so freelist bookkeeping remains consistent enough to avoid falhas.
  2. Free the mbuf. slirp_uma_free() now executes memcpy(dest=pData, src=item_data, n=size) while the mbuf still contains guest-controlled data, yielding an escrita arbitrária.

Because the Linux VirtualBox binary is non-PIE, PLT addresses for memcpy and system are fixed and can be used directly. The guest can also stash strings such as /bin/sh inside another mbuf that remains referenced when the hijacked call executes.

Heap grooming via fragmentation

Slirp’s per-interface zone is 3072 chunks deep and initially carved as a contiguous array whose freelist is traversed from high addresses downward. Deterministic adjacency can be achieved by:

  • Flooding the NAT with many IP_MF fragments of constant size so the reassembly code allocates predictable mbuf sequences.
  • Recycling specific chunks by sending fragments that time out, forcing frees back into the freelist in LIFO order.
  • Using knowledge of the freelist walk to place the future victim mbuf right after the mbuf that will carry the IP options overflow.

This grooming ensures the overflow hits the targeted struct item and that the fake uma_zone remains in-bounds of the leak primitive.

From arbitrary write to host code execution

With the memcpy-on-free primitive:

  1. Copy an attacker-controlled /bin/sh string and command buffer into a stable mbuf.
  2. Use the primitive to overwrite a GOT entry or indirect callsite (e.g. a function pointer inside the NAT device state) with the PLT entry of system().
  3. Trigger the overwritten call. Because VirtualBox runs the NAT device inside the host process, the payload executes with the privileges of the user running VirtualBox, allowing a guest-to-host escape.

Alternative payloads include planting a miniature ROP chain in heap memory and copying its address into a frequently-invoked callback, or repointing pfFini/pfDtor themselves to chained gadgets for repeated writes.

References

Tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks