VirtualBox Slirp NAT — Eksploatacja sterty pakietów

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks

TL;DR

  • VirtualBox dostarcza mocno zmodyfikowany fork Slirp, którego packet buffers (mbufs) znajdują się w niestandardowym alokatorze stref z wbudowanymi metadanymi i callbackami wskaźnikowymi (pfFini, pfDtor).
  • Gość może nadpisać zaufane m->m_len za pomocą długości nagłówka IP kontrolowanej przez atakującego, co niszczy wszystkie późniejsze sprawdzenia zakresów i daje zarówno infoleak, jak i overwrite primitives.
  • Poprzez wykorzystanie pakietów UDP z checksumą 0 i zawyżonym ip_len, gość może exfiltrate mbuf tails oraz metadane sąsiednich chunków, aby poznać adresy heap i zone.
  • Dostarczenie spreparowanych opcji IP zmusza ip_stripoptions() do memcpy() zbyt dużej ilości danych in-place, dzięki czemu atakujący może nadpisać nagłówek struct item następnego mbufu i ustawić jego pole zone na w pełni kontrolowane dane.
  • Zwolnienie uszkodzonego mbufu wywołuje zone->pfFini() z argumentami dostarczonymi przez atakującego; skierowanie go na memcpy@plt daje arbitralny copy/write primitive, który można skierować w stronę wpisów GOT lub innych danych kontrolnych wewnątrz nie-PIE binarki VirtualBox.

Packet allocator anatomy

VirtualBox alokuje każdą przychodzącą ramkę Ethernet ze strefy przypisanej do interfejsu o nazwie zone_clust. Każdy 0x800-bajtowy chunk danych poprzedzony jest wbudowanym nagłówkiem:

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

Ponieważ przypisanie odbywa się przed weryfikacją IP header, guest może zadeklarować dowolną długość do 0xffff. Reszta stosu (ICMP, UDP, fragmentation handlers, itd.) zakłada, że m->m_len jest godny zaufania i używa go do decyzji, ile bajtów skopiować z mbuf.

Użyj UDP packets z checksumą 0 (oznaczającą “no checksum”). NAT fast-path przesyła dalej m->m_len bajtów bez sprawdzania integralności payloadu, więc nadmierne ustawienie ip_len powoduje, że Slirp czyta poza rzeczywisty bufor i zwraca heap residues do guest lub współpracującego zewnętrznego helpera poza NAT. Ponieważ rozmiar chunku to 2048 bajtów, leak może zawierać:

  • Inline struct item następnego mbuf, ujawniając kolejność freelist i rzeczywisty wskaźnik zone.
  • Heap cookies takie jak pola magic, ułatwiające stworzenie pozornie poprawnych nagłówków przy późniejszych corruptions.

Nadpisywanie sąsiednich nagłówków chunk przy pomocy IP options

Ta sama fałszywa długość może zostać zamieniona w overwrite primitive przez wymuszenie przejścia pakietu przez ip_stripoptions() (wywoływane gdy IP header ma options, a payload to UDP/TCP). Helper oblicza długość kopiowania z m->m_len, a następnie wywołuje memcpy() aby przesunąć transport header ponad stripped options:

  1. Dostarcz długie ip_len, tak aby obliczona długość przesunięcia sięgała poza bieżący mbuf.
  2. Dodaj niewielką liczbę IP options, żeby Slirp wszedł w ścieżkę stripowania.
  3. Gdy memcpy() się wykona, odczyta z następnego mbuf i zapisze ponad payload i inline header bieżącego mbuf, korumpując magic, zone, ref_count itd.

Ponieważ allocator trzyma pakiety z tego samego interface skontiguowane na freelist, ten overflow trafia deterministycznie w następny chunk po umiarkowanym heap grooming.

Forging uma_zone_t to hijack pfFini

Gdy sąsiedni struct item staje się podatny na corruption, exploit postępuje następująco:

  1. Użyj wyciekniętych adresów heap aby zbudować fałszywą strukturę uma_zone wewnątrz mbuf w pełni kontrolowanego przez guest. Wypełnij:
  • pfFini wpisem PLT memcpy().
  • pData docelowym wskaźnikiem (np. GOT entry, vtable slot, tablica wskaźników funkcji).
  • size liczbą bajtów do skopiowania.
  • Opcjonalnie: pfDtor jako wywołanie drugiego etapu (np. aby wywołać nowo zapisany wskaźnik funkcji).
  1. Nadpisz pole zone docelowego mbuf wskaźnikiem na fałszywą strukturę; dostosuj wskaźniki list, tak aby księgowość freelist pozostała wystarczająco spójna, by uniknąć crashy.
  2. Free’uj mbuf. slirp_uma_free() teraz wykona memcpy(dest=pData, src=item_data, n=size) podczas gdy mbuf nadal zawiera dane kontrolowane przez guest, dając arbitrary write.

Ponieważ linuxowy VirtualBox binarny jest non-PIE, adresy PLT dla memcpy i system są stałe i można ich użyć bezpośrednio. Guest może też przechować stringi takie jak /bin/sh w innym mbuf, który pozostanie referencjonowany gdy hijacked call się wykona.

Heap grooming przez fragmentation

Strefa per-interface Slirp ma głębokość 3072 chunk i początkowo jest podzielona jako kontiguja tablica, której freelist jest przeglądany od wysokich adresów w dół. Deterministyczną sąsiedność można osiągnąć przez:

  • Zatkanie NAT wieloma IP_MF fragments o stałym rozmiarze, tak by reassembly code alokował przewidywalne sekwencje mbuf.
  • Recykling konkretnych chunków przez wysyłanie fragmentów, które wygasają, wymuszając frees z powrotem na freelist w porządku LIFO.
  • Wykorzystanie wiedzy o freelist walk, by umieścić przyszły victim mbuf tuż za mbuf, który będzie niósł IP options overflow.

To grooming zapewnia, że overflow trafia w targetowany struct item i że fałszywy uma_zone pozostaje w granicach leak primitive.

From arbitrary write to host code execution

Z memcpy-on-free primitive:

  1. Skomponuj kontrolowany przez atakującego string /bin/sh i bufor poleceń w stabilnym mbuf.
  2. Użyj primitive aby nadpisać GOT entry lub indirect callsite (np. wskaźnik funkcji w stanie urządzenia NAT) wpisem PLT system().
  3. Wyzwól nadpisane wywołanie. Ponieważ VirtualBox uruchamia NAT device wewnątrz procesu hosta, payload wykona się z przywilejami użytkownika uruchamiającego VirtualBox, umożliwiając guest-to-host escape.

Alternatywne payloady obejmują zainstalowanie miniaturowego ROP chain w pamięci heap i skopiowanie jego adresu do często wywoływanego callbacka, lub przestawienie pfFini/pfDtor bezpośrednio na chained gadgets dla powtarzalnych write’ów.

References

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks