VirtualBox Slirp NAT Packet Heap Exploitation

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

TL;DR

  • VirtualBox liefert einen stark modifizierten Fork von Slirp, dessen Paketpuffer (mbufs) in einem eigenen Zone-Allocator mit Inline-Metadaten und Funktionszeiger-Callbacks (pfFini, pfDtor) leben.
  • Ein Gast kann das vertraute m->m_len mit einer vom Angreifer kontrollierten IP-Header-Länge überschreiben, was alle späteren Grenzprüfungen zerstört und sowohl infoleak- als auch overwrite-Primitives liefert.
  • Durch Missbrauch von UDP-Paketen mit Checksumme 0 und übergroßem ip_len kann der Gast mbuf-Tails und die Metadaten benachbarter Chunks exfiltrieren, um heap- und zone-Adressen zu ermitteln.
  • Durch das Bereitstellen speziell gefälschter IP-Optionen wird ip_stripoptions() dazu gezwungen, mit memcpy() zu viel Daten in-place zu kopieren, sodass der Angreifer den struct item-Header des nächsten mbuf überschreiben und dessen zone-Feld auf vollständig kontrollierte Daten zeigen lassen kann.
  • Das Freigeben des korrumpierten mbuf löst zone->pfFini() mit vom Angreifer gelieferten Argumenten aus; das Zeigen darauf zu memcpy@plt ergibt eine beliebige copy/write-Primitive, die auf GOT-Einträge oder andere Steuerdaten innerhalb der non-PIE VirtualBox-Binärdatei gelenkt werden kann.

Aufbau des Packet-Allocators

VirtualBox weist jeden eingehenden Ethernet-Frame aus einer pro-Interface-Zone namens zone_clust zu. Jeder 0x800-Byte-Datenchunk wird von einem Inline-Header vorausgegangen:

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

When an mbuf is freed the call stack m_freem -> ... -> slirp_uma_free() trusts the inline header:

  1. uma_zfree_arg() recomputes item = (struct item *)mem - 1 and should validate item->zone, but Assert() is compiled out in release builds.
  2. slirp_uma_free() loads zone = item->zone and unconditionally executes zone->pfFini(zone->pData, data_ptr, zone->size) followed by zone->pfDtor(...).

Therefore, any write-what-where into the mbuf header translates into a controlled indirect call during 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);

Weil die Zuweisung vor der Überprüfung des IP-Headers erfolgt, kann ein Gast beliebige Längen bis zu 0xffff angeben. Der Rest des Stacks (ICMP, UDP, Fragmentierungs-Handler, etc.) geht davon aus, dass m->m_len vertrauenswürdig ist und nutzt ihn, um zu entscheiden, wie viele Bytes vom mbuf kopiert werden.

Verwende UDP-Pakete mit Checksumme 0 (bedeutet “no checksum”). Der NAT fast-path leitet m->m_len Bytes weiter, ohne die Payload-Integrität zu prüfen, sodass das Aufblähen von ip_len Slirp dazu bringt, über den echten Puffer hinaus zu lesen und heap-Reste an den Gast oder an einen kooperierenden externen Helfer jenseits des NAT zurückzugeben. Da die Chunk-Größe 2048 Bytes beträgt, kann der leak Folgendes enthalten:

  • Das inline struct item des nächsten mbuf, das die freelist-Reihenfolge und den echten zone-Pointer offenlegt.
  • Heap cookies wie magic-Felder, die helfen, später beim Durchführen von Korruptionen gültig aussehende Header zu konstruieren.

Overwriting neighbouring chunk headers with IP options

Die gleiche gefälschte Länge lässt sich in eine overwrite-Primitive verwandeln, indem man das Paket durch ip_stripoptions() zwingt (ausgelöst, wenn der IP-Header Optionen enthält und die Payload UDP/TCP ist). Der Helfer berechnet eine Copy-Länge aus m->m_len und ruft dann memcpy() auf, um den Transport-Header über die entfernten Optionen zu verschieben:

  1. Liefere ein großes ip_len, sodass die berechnete Move-Länge über das aktuelle mbuf hinausreicht.
  2. Enthalte eine kleine Anzahl von IP-Optionen, damit Slirp den stripping-Pfad betritt.
  3. Wenn memcpy() ausgeführt wird, liest es aus dem folgenden mbuf und schreibt über die Payload und den inline-Header des aktuellen mbuf und korrumpiert so magic, zone, ref_count, etc.

Da der Allocator Pakete von derselben Schnittstelle im freelist zusammenhängend hält, trifft dieser Overflow nach moderatem heap grooming deterministisch auf den nächsten Chunk.

Forging uma_zone_t to hijack pfFini

Sobald das angrenzende struct item korrumpierbar ist, geht der Exploit wie folgt vor:

  1. Nutze leaked heap-Adressen, um eine gefälschte uma_zone-Struktur in einem vom Gast vollständig kontrollierten mbuf aufzubauen. Befülle:
    • pfFini mit dem PLT-Eintrag von memcpy().
    • pData mit dem gewünschten Zielpointer (z. B. GOT-Eintrag, vtable-Slot, Function-Pointer-Array).
    • size mit der Anzahl der zu kopierenden Bytes.
    • Optional: pfDtor als zweite Stufe-Aufruf (z. B. um den neu geschriebenen Function-Pointer aufzurufen).
  2. Überschreibe das zone-Feld des Ziel-mbuf mit dem Pointer zur gefälschten Struktur; passe list-Pointer so an, dass die freelist-Buchführung ausreichend konsistent bleibt, um Abstürze zu vermeiden.
  3. Free das mbuf. slirp_uma_free() führt nun memcpy(dest=pData, src=item_data, n=size) aus, während das mbuf noch guest-controllierte Daten enthält, was einen arbitrary write ermöglicht.

Da das Linux VirtualBox-Binary non-PIE ist, sind PLT-Adressen für memcpy und system fest und können direkt verwendet werden. Der Gast kann außerdem Strings wie /bin/sh in einem anderen mbuf deponieren, das beim Ausführen des hijacked Aufrufs noch referenziert wird.

Heap grooming via fragmentation

Slirps per-interface zone ist 3072 Chunks tief und ursprünglich als zusammenhängendes Array angelegt, dessen freelist von hohen Adressen nach unten durchlaufen wird. Deterministische Adjazenz lässt sich erreichen durch:

  • Überfluten des NAT mit vielen IP_MF-Fragmenten konstanter Größe, sodass der Reassembly-Code vorhersehbare mbuf-Sequenzen alloziert.
  • Recycling bestimmter Chunks durch Senden von Fragmenten, die auslaufen, wodurch Frees in LIFO-Reihenfolge zurück in die freelist gelangen.
  • Nutzung des Wissens über den freelist-Walk, um das zukünftige Ziel-mbuf direkt hinter das mbuf zu platzieren, das den IP-Options-Overflow tragen wird.

Dieses Grooming stellt sicher, dass der Overflow das anvisierte struct item trifft und dass die gefälschte uma_zone im Rahmen des leak-Primitives bleibt.

From arbitrary write to host code execution

Mit der memcpy-on-free primitive:

  1. Kopiere einen vom Angreifer kontrollierten /bin/sh-String und Befehlsbuffer in ein stabiles mbuf.
  2. Nutze die Primitive, um einen GOT-Eintrag oder eine indirekte Callsite (z. B. einen Function-Pointer im NAT-Device-State) mit dem PLT-Eintrag von system() zu überschreiben.
  3. Trigger den überschriebenen Aufruf. Da VirtualBox das NAT-Device im Host-Prozess ausführt, läuft die Payload mit den Rechten des Benutzers, der VirtualBox ausführt, was eine guest-to-host-Escape ermöglicht.

Alternative Payloads umfassen das Platzieren einer miniaturisierten ROP-Chain im Heap-Speicher und das Kopieren ihrer Adresse in einen häufig aufgerufenen Callback, oder das Umschwenken von pfFini/pfDtor selbst auf verkettete Gadgets für wiederholte Writes.

Referenzen

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks