VirtualBox Slirp NAT Packet Heap Exploitation

Tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする

TL;DR

  • VirtualBox は Slirp の大幅に改変されたフォークを同梱しており、そのパケットバッファ (mbufs) はインラインメタデータと関数ポインタコールバック(pfFini, pfDtor)を備えたカスタム zone allocator に配置されている。
  • ゲストは信頼された m->m_len をアタッカー制御の IP ヘッダ長で書き換え可能で、これによって以降の全ての境界チェックが無効化され、infoleak と overwrite primitives の両方が得られる。
  • チェックサムが 0 で過大な ip_len を持つ UDP パケットを悪用することで、ゲストは mbuf の末尾や隣接チャンクのメタデータを持ち出して heap と zone のアドレスを取得できる。
  • 細工した IP オプションを与えると ip_stripoptions() が in-place で過剰にデータを memcpy() するよう強制され、攻撃者は次の mbuf の struct item ヘッダを上書きしてその zone フィールドを完全に制御されたデータに向けられる。
  • 破損した mbuf を free すると攻撃者供給の引数で zone->pfFini() が呼ばれ、これを memcpy@plt に向けることで任意のコピー/書き込みプリミティブが得られ、non-PIE の VirtualBox バイナリ内の GOT エントリやその他の制御データに向けて操ることができる。

Packet allocator anatomy

VirtualBox は各インターフェイスごとの zone_clust と名付けられた zone から全ての着信 Ethernet フレームを割り当てる。各 0x800 バイトのデータチャンクはインラインヘッダが先行する:

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

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.

割り当てがIPヘッダの検証よりも前に行われるため、ゲストは最大 0xffff まで任意の長さを宣言できます。スタックの残り(ICMP、UDP、フラグメンテーションハンドラ等)は m->m_len を信頼できる値と仮定し、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:

チェックサムが 0(“no checksum” を意味する)な UDP パケットを使います。NAT の fast-path はペイロード整合性を検査せずに m->m_len バイトを転送するため、ip_len を水増しすると Slirp が実際のバッファを越えて読み出し、ヒープの残留(heap residues)をゲストやNAT外側の協力する外部ヘルパーに返します。チャンクサイズが 2048 バイトであるため、leak には以下が含まれる可能性があります:

  • 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.

  • 次の mbuf の inline な struct item(freelist の順序や実際の zone ポインタが明らかになる)。

  • magic フィールドのようなヒープクッキー(後の破壊で見かけ上正しいヘッダを作るのに役立つ)。

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:

同じ不正な長さは、パケットを ip_stripoptions()(IP ヘッダにオプションがありペイロードが UDP/TCP のときに発動)経由で処理させることで上書きプリミティブに変えられます。ヘルパーは m->m_len からコピー長を計算し、memcpy() を呼んでトランスポートヘッダを剥がしたオプションの上にスライドさせます:

  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.

  4. 計算された移動長が現在の mbuf を越えるように、長い ip_len を与える。

  5. Slirp がオプション剥離経路に入るよう少数の IP オプションを含める。

  6. memcpy() 実行時に次の mbuf から読み取り、現在の mbuf のペイロードと inline ヘッダ上に書き込むため、magiczoneref_count 等が破壊される。

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

割り当て器は同一インターフェースのパケットを freelist 上で連続させるため、このオーバーフローは適度なヒープグルーミング後に決定論的に次のチャンクを直撃します。

Forging uma_zone_t to hijack pfFini

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

隣接する struct item を破壊可能にしたら、エクスプロイトは次のように進みます:

  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. 漏えいしたヒープアドレスを使い、ゲストが完全に制御する mbuf 内に偽の uma_zone 構造体を構築する。以下を埋める:
  • pfFinimemcpy() の PLT エントリを設定する。
  • pData に目的の書き込み先ポインタ(例:GOT エントリ、vtable スロット、関数ポインタ配列)を設定する。
  • size にコピーするバイト数を設定する。
  • 任意:第二段呼び出しのために pfDtor を設定(例:新たに書き込まれた関数ポインタを呼ぶため)。
  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 crashes.

  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 arbitrary write.

  3. 目標の mbuf の zone フィールドを偽構造体へのポインタで上書きする。list ポインタを調整して freelist のブックキーピングがクラッシュを引き起こさない程度に一貫性を保つ。

  4. その mbuf を free する。slirp_uma_free() は mbuf にゲスト制御データが残っている状態で memcpy(dest=pData, src=item_data, n=size) を実行し、任意書き込みを実現する。

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.

Linux の VirtualBox バイナリは non-PIE であるため、memcpysystem の PLT アドレスは固定されており直接利用できます。ゲストは /bin/sh のような文字列を別の mbuf に置いておき、ハイジャックされた呼び出し実行時に参照されるようにしておくことも可能です。

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:

Slirp のインターフェース毎の zone は深さ 3072 チャンクで、初期は連続した配列として切り出され freelist は高アドレスから下へ走査されます。決定論的な隣接性は次の方法で達成できます:

  • 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.

  • 再アセンブルコードが予測可能な mbuf シーケンスを割り当てるよう、一定サイズの IP_MF フラグメントを大量に NAT に流す。

  • タイムアウトするフラグメントを送って特定チャンクをリサイクルし、free を LIFO 順で freelist に戻す。

  • freelist の走査順を利用して、将来の被害対象 mbuf を IP オプションオーバーフローを持つ mbuf の直後に配置する。

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

このグルーミングにより、オーバーフローが狙った struct item に当たり、偽の uma_zone が leak プリミティブの範囲内に留まることが保証されます。

From arbitrary write to host code execution

With the memcpy-on-free primitive:

任意書き込みプリミティブ(memcpy-on-free)を得たら:

  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.

  4. 攻撃者制御の /bin/sh 文字列やコマンドバッファを安定した mbuf に書き込む。

  5. このプリミティブを使って GOT エントリや間接呼び出し箇所(例:NAT デバイス状態内の関数ポインタ)を system() の PLT エントリで上書きする。

  6. 上書きされた呼び出しをトリガーする。VirtualBox が NAT デバイスをホストプロセス内で実行しているため、ペイロードは VirtualBox を実行しているユーザーの権限で実行され、ゲストからホストへのエスケープを可能にします。

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.

代替ペイロードとしては、ヒープ上に小さな ROP チェインを設置しそのアドレスを頻繁に呼ばれるコールバックに書き込む方法や、pfFini/pfDtor 自体を連結されたガジェット群に向けて繰り返し書き込む方法などがあります。

References

Tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする