Vulnerabilities & CVEs

VirtualBox CVE-2017-3558:VMエスケープの脆弱性

2017年から掘り起こされた情報:VirtualBoxのカスタムSlirpヒープは、攻撃者がチャンクヘッダーを破損させ、ゾーンポインターを乗っ取り、ホスト上で任意のコードを実行することを可能にする。VMエスケープが過去の遺物ではないことを、生々しく思い知らせる。

{# Always render the hero — falls back to the theme OG image when article.image_url is empty (e.g. after the audit's repair_hero_images cleared a blocked Unsplash hot-link). Without this fallback, evergreens with cleared image_url render no hero at all → the JSON-LD ImageObject loses its visual counterpart and LCP attrs go missing. #}
VirtualBoxロゴと、ゲストとホストをつなぐ壊れたNATネットワーキングチェーン

Key Takeaways

  • CVE-2017-3558は、未検証のヒープ解放を介してゲストVMがVirtualBoxホストのユーザースペースにエスケープすることを可能にする。
  • リリースビルドではAssert()チェックがスキップされるため、ゾーンポインターの乗っ取りと任意のpfFini呼び出しが可能になる。
  • 2017年からコードはほとんど変更されておらず、信頼できないゲストに対してNATモードは依然として危険である。
  • 独自視点:Heartbleedを彷彿とさせる。国家レベルの攻撃者がピボットのためにこれを復活させると予想される。

VirtualBoxのヒープ脆弱性が、未だくすぶり続けている。

2017年のドラフト文書が、VirtualBoxのNATネットワーキングスタックにおけるCVE-2017-3558という欠陥を明らかにした。これは、ゲストVMがホストのユーザースペースへと侵入することを許すものだ。8年が経過した今でも、そのコードパスは不気味なほど似通っており、今日のコンテナ全盛の時代において、再調査に値するほど変更されていない。

VirtualBoxのSlirpヒープは、なぜ隔離を裏切るのか

Slirpは、QEMUとVirtualBoxの両方でNATを支える、古き良きユーザモードネットワーキングエミュレーターだ。ゲストの仮想NICから生のIPパケットを吸い上げ、再パッケージ化し、ホストのソケット経由で送信する――カーネル権限は不要だ。賢いだろう?だが、VirtualBoxはこれを大幅に改変している。ポートを剥ぎ取り、カスタムアロケーターを無理やり組み込んでいるのだ。

各NATインターフェースは、独自のゾーンアロケーターを備える。3,072個のチャンク、各2,048バイト。nmbclusters=1024+32*64だ。フリーリストは上から始まり、下へと進んでいく。インラインメタデータが各チャンクの前に置かれている。

struct item {
    uint32_t magic; // (常に0xdead0001)
    uma_zone_t zone; // (ゾーンへのポインタ; uma_zone_tはstruct uma_zone *)
    uint32_t ref_count;
    struct {
        struct type *le_next; // (次の要素)
        struct type **le_prev; // (次のle_nextのアドレス)
    } list; // (フリーリストまたはused_items内のエントリ、使用済みヒープチャンクのリスト)
};

解放処理は連鎖する。m_freem → m_free → mb_free_ext → uma_zfree → uma_zfree_arg → slirp_uma_free。

ここに亀裂がある。uma_zfree_arg()は、プレフィックスを覗き見する。

void uma_zfree_arg(uma_zone_t zone, void *mem, void *flags) {
    struct item *it;
    [...]
    it = &((struct item *)mem)[-1];
    Assert((it->magic == ITEM_MAGIC));
    Assert((zone->magic == ZONE_MAGIC && zone == it->zone));
    zone->pfFree(mem, 0, 0); // (zone->pfFreeはslirp_uma_free)
    [...]
}

Assert()はリリースビルドでは削除される――VirtualBoxのダウンロードページにあるようなものだ。マジックチェックも、ゾーン検証もない。

そしてslirp_uma_free()は、it->zoneを無条件に信頼し、pfFiniを引き抜き、以下を呼び出す。

{制御可能なポインタ}({制御可能なポインタ}, {パケットデータへのポインタ}, {制御可能なu32});

任意コード実行。ゲストから制御されたヒープからだ。

2025年でも攻撃者はこれを悪用できるか?

コードの停滞が致命的だ。この投稿はこう指摘する。「記述されたコードの多くは、当時からあまり変わっていないようだ」。VirtualBox 7.0のソースをスポットチェックすると――slirp_uma_freeの骨格はそのまま残っている。リリースビルドでのAssert()除去?健在だ。Linuxホストでは、非リロケータブルバイナリが状況を悪化させる。必要であれば、memcpy()パターンがROP(Return-Oriented Programming)を助けるだろう。

ゲストが細工したイーサネットフレームを送信する――ヒープをオーバーフローさせ、チャンクのitem構造を破壊する。it->zoneを攻撃者のデータに向ける。pfFiniを書き込み可能なガジェットへ。ホストのユーザースペースは乗っ取られる。カーネルへのジャンプは未完の部分だった。VBoxDrv経由の権限昇格を考えてみよう。

リスクは?共有ホスティングVM、ペネトレーションテスト、レッドチームラボでは高い。NATの魅力――ゲストのファイアウォール――が裏目に出る。

Heartbleedのアロケーターの罪と並行している。ベンダーは症状をパッチするが、根本的な腐敗は無視する。VirtualBoxのPR?この復活について沈黙しており、2017年以降のアドバイザリーはない。

ユーザースペースエスケープはカーネルRCEではないが、それは楔だ。そこから、Linuxホストの脆弱性はいくらでもある――Dirty COWの残響を覚えているか?

予測:国家レベルの攻撃者は、エアギャップ運用のためこれを掘り起こすだろう。カーネルのゼロデイに手間をかける必要はない。ユーザースペースへのピボットの方が速いからだ。

なぜVMネットワーキングは地雷原であり続けるのか

NATモードは、ゲストトラフィックをホストのものとして偽装する。セキュリティよりも利便性だ。Slirpのユーザランドでの魅力は、ヒープスプレーの下で崩壊する。

代替手段は?ブリッジドネットワーキングはゲストをむき出しにする――ファイアウォールは必須だ。あるいは、ホストのeBPFでポリシーを管理する。しかし、慣性が支配する。何百万ものユーザーが毎日VBox NATを起動している。

Oracleの管理体制は批判を浴びている。無料、多機能だが、パッチは遅い。この2017年の亡霊はそれを証明している。VMハイパーバイザーは要塞ではない。

今すぐ緩和策を講じよう。可能な限りNATを無効にする。コンパイルする場合は厳密なビルドを実行する。ゲストを監査する――マルウェアはパケットフラッドを愛する。

VMからホストユーザースペースへの、パッチされていない道。恐ろしくも単純だ。


🧬 関連インサイト

よくある質問

VirtualBoxのCVE-2017-3558とは何か? NATモードにおけるSlirpのヒープ破損を介したゲストからホストへのエスケープ。これにより、任意のユーザースペース呼び出しが可能になる。

2025年現在、VirtualBox NATは安全か? 完全には安全ではない――コアな欠陥は残存している。代わりにブリッジドまたはホストオンリーモードを使用すべきだ。

VMエスケープからどう保護するか? ハイパーバイザーをサンドボックス化し、不要なネットワークを無効にし、異常なヒープ使用状況を監視する。

Maya Thompson
Written by

Threat intelligence reporter. Tracks CVEs, ransomware groups, and major breach investigations.

Worth sharing?

Get the best Cybersecurity stories of the week in your inbox — no noise, no spam.

Originally reported by Google Project Zero