エクスプロイト&脆弱性
「VMware ESXi」上で事前認証によるリモートコード実行を行う脆弱性の解説
このブログ記事では「CVE-2020-3992」と「CVE-2021-21974」の2つの脆弱性に注目し、「CVE-2021-21974」のヒープオーバーフローがどのようにしてコード実行に利用されたかを解説します。また、この脆弱性利用をする動作に関する簡単な動画もご確認ください。
2020年2月23日、VMware社は「CVE-2021-21972」、「CVE-2021-21973」、「CVE-2021-21974」の脆弱性に対処するアドバイザリ「VMSA-2021-0002」を公開しました。2020年第4四半期頃、VMware社のソフトウェア「VMware ESXi」のプラットフォームにおいて「事前認証によるリモートコード実行を行う」という深刻度の高い2件の脆弱性が確認されました。これらの脆弱性は、いずれも同一のコンポーネントである「Service Location Protocol(SLP)」サービス内に存在しており、1つは「use-after-free(UAF)」、もう1つは「ヒープオーバーフロー」が原因でした。2020年10月、VMware社は修正パッチをリリースしましたが、修正が迂回される可能性が判明したため、2020年11月に2回目の修正パッチをリリースすることで、「CVE-2020-3992」が割り当てられた「UAF」の脆弱性に対し、完全に対応しました。そして今回のアドバイザリで触れられている修正パッチで「CVE-2021-21974」が割り当てられた「ヒープオーバーフロー」の脆弱性に完全に対処しました。
このブログ記事では「CVE-2020-3992」と「CVE-2021-21974」の2つの脆弱性に注目し、「CVE-2021-21974」のヒープオーバーフローがどのようにしてコード実行に利用されたかを解説します。また、この脆弱性利用をする動作に関する簡単な動画もご確認ください。
図1:「VMware ESXi」上で事前認証によるリモートコード実行を行う脆弱性
「CVE-2021-21974」に関する動画
「Service Location Protocol(SLP)」は「VMware ESXi」をデフォルトでインストールした際にTCPおよびUDPポート427を待機するネットワークサービスです。また、この際の実装では「OpenSLP 1.0.1」をベースにしています。VMware社は独自のバージョン管理により、いくつかのシステム堅牢化を実施しています。
このサービスは、認証なしでネットワーク入力を解析し、ルートとして実行されるため、「ESXi」の「SLP」サービスに存在する脆弱性が悪用されると、ルート権限として認証前のリモートコードを実行される可能性があります。デフォルトでは、ゲストがホスト上の「SLP」サービスにアクセスできるため、この経路は、仮想マシンを回避するための手法として利用される可能性があります。
■「Use-After-Free」の脆弱性「CVE-2020-3992」
この脆弱性は、「SLP」が実装されたVMware製品に存在します。簡略化した疑似コードを示すと、以下のようになります。
(3)において「SLP_FUNCT_DAADVERT」または「SLP_FUNCT_SRVREG」のリクエストが正しく処理された場合、割り当てられた「SLPMessage」がデータベースに保存されます。しかし(4)においては、処理されたリクエストがエラー無しで戻ってきたにもかかわらず、「SLPMessage」が解放されてしまい、データベースにポインタがぶら下がったままになります。(4)における「free」は、古いバグを修正する過程で追加された可能性があります。
■「CVE-2020-3992」への最初の修正パッチが迂回される
VMware社がリリースした最初の修正パッチ「build-16850804」では興味深い対応となっていました。この場合、上記の脆弱性のあるコードには何も変更を加えず、その代わり、リクエストの処理前にソースIPアドレスをチェックするロジックを追加していました。このロジックは「IsAddrLocal()」に存在しており、ソースIPアドレスが「localhost」の場合のみ、リクエストを許可するようになっていました。
この場合、わずか数秒後には、「IPv6」のリンクローカルアドレスからLAN経由でアクセス可能になることに気づかれるかもしれません。
■「CVE-2020-3992」への2回目の修正パッチ
それから2週間後、2回目の修正パッチ「build-17119627」がリリースされました。この段階でIPソースアドレスのチェックロジックに関する部分が改善されました。
この改善により、IPv6からの経路が除去されました。さらに、データベースに追加した後に「SLPMessage」へのポインタをクリアにすることで、「UAF」の欠陥に関する根本原因が修正されました。
■ヒープオーバーフローに関する脆弱性「CVE-2021-21974」
上述の「CVE-2020-3992」脆弱性と同様、この脆弱性も「SLP」が実装されたVMware製品に存在します。そして簡略化された疑似コードは、以下のようになります。
この場合、(5)において「srvurl」がネットワーク入力されます。しかしこの関数は、「strstrstr()」の使用前に「srvurl」を「NULLバイト」で終了させていません。また(6)のおいては、範囲外の文字列検索によりヒープオーバーフローが発生しています。こうした状況は、VMware側において元の「OpenSLP」プロジェクトからの更新がマージされなかったことが原因となっています。
■「CVE-2021-21974」への修正パッチ
その後、3つ目の修正パッチ「build- 17325551」がリリースされました。これは、上記(6)において「memcpy」の前の長さをチェックさせることで、ヒープオーバーフローの欠陥の根本的な原因への対処としました。
■脆弱性利用について
すべてのLinuxにおける脆弱性への対処は「/bin/slpd」で有効化することを前提にしており、中でも注目すべきは「Position Independent Executables(PIE)」の利用です。これにより、最初にメモリからいくつかのアドレスを公開しない限り、コード実行を実現することが困難になります。したがって、今回の脆弱性利用の検証では、「UAF」を活用する手法ではメモリを開示する有効な方法がないため、ヒープオーバーフローの脆弱性に焦点を移しました。
■オーバーフローのアップグレードについて
「SLP」は、送受信するイベントを処理するために「struct SLPBuffer」を使用します。この場合、「SLPDSocket*」接続ごとに、それzれ1つの「SLPBuffer* sendbuf」と1つの「SLPBuffer* recvbuf」が割り当てられます。
この場合、「SLPBuffer」内の「start」または「curpos」ポインタに部分的な上書きが施され、次のメッセージ返信時にメモリがリークされることになります。しかし「sendbuf」は空となり、各返信の前に更新されることになります。このような選択ベースのソケットモデルにより「sendbuf」が存続するためのタイムスロットが保持されます。
- 送信バッファは一杯まで受信せず、送信バッファソケットが埋められる
- ソケットの「sendbuf->curpos」が部分的に上書きされる
- ソケットからの受信を開始され、リークされたメモリは最後に追加される
ただし以下のような課題も存在しています。
- 「strstrstr()」の使用により「NULLバイト」ではオーバーフローできない
- オーバーフローしたバッファ(obuf)は、「SLPParseSrvUrl()」へのリターン後、すぐに自動的に解放される
これらを合わせると、上書き自体は、次のチャンクヘッダの途中までしか拡張できないということになります。さもなければ、次の空きチャンクのサイズは、非常に大きな値(4つの非NULLバイト)に設定され、「obuf」が解放された直後にプロセスが強制終了してしまいます。
これらの課題を図式化すると以下ようになります。
上図ではターゲットを「sendbuf」と仮定します。その場合(F1)では「IN USE」と書かれた各チャンクは「SLPBuffer」か「SLPDSocket」かのいずれかになり、(F2)において「obuf」への「ホール」が用意されることになります。そして(F4)でオーバーフローがトリガされた後、次の解放されたチャンクは拡大され、ターゲットにオーバーラップされます。次に(F5)で「obuf」を解放してマージします。ここで(F6)でターゲットを上書きするため、新しい接続から新規の「recvbuf」を割り当てることができます。この場合、上書きで「NULLバイト」を含むことができます。
ただしここでも以下の課題が存在しています。
- 「OpenSLP」における「malloc()」関数の多くは、VMware側で「calloc()」に置き換えられてしまう
この場合、(F6)における「recvbuf」も「calloc()」から割り当てられるため、メモリがゼロ初期化されます。つまり、「recvbuf」がターゲットと重なった場合には、ポインタの部分的な上書きはできなくなります。しかし、この状況を回避するための工夫も可能ではあります。最初に(F4)で解放されたチャンクの「IS_MAPED」フラグで上書しておくことです。これにより、「calloc()」は、次の割り当てるでのゼロの初期化をスキップします。これは、ターゲットに対して上書きを実行したい多くの状況において有用な一般的手法となっています。
まとめると以下のようになります。
- 接続状態「connection->state」を「STREAM_WRITE_FIRST」として上書きする。これはメモリ開示に備えて「sendbuf->curpos」を「sendbuf->start」へリセットするために必要となる
- ステップ1の接続状態での「sendbuf」に関して「sendbuf->start」を2つの「NULLバイト」で部分的に上書きする。そして接続からの受信を開始し、これにより「sendbuf」のアドレスを含むメモリ開示を取得できる
- 新規接続へ「sendbuf->curpos」を上書きして、「mmap()」から確保した「recvbuf」のアドレスをリークする。「mmap」されたアドレスがわかれば、「libc」のベースアドレスの推論が可能になる。
- 新規接続から「recvbuf->curpos 」を上書きし、その上で「free_hook」のアドレスに設定して接続先で送信を開始する。この後「free_hook」を上書きする
- 接続を閉じ、「free_hook 」を呼び出してROPチェーンを開始する
なお、これらのステップは、まだ最適化された形ではないかもしれません。
■権限取得について
こうして問題がなければ、ターゲットとなる「ESXi」のシステム上でルート権限により任意のコードを実行することが可能になります。なお、「ESXi 7」の場合、「SLP」の対応するため「DaemonSandboxing」と呼ばれる新機能が用意されています。これは「AppArmor」のようなサンドボックスを使用して「SLPデーモン」を隔離します。ただし今回の検証環境では、この機能は、デフォルトで無効になっていました。
このことは、今後、この新機能のサンドボックス脱出するための手順が必要になってくることを示唆しています。
■被害に遭わないためには
「VMware ESXi」は、クラウドサービスプロバイダをはじめ、多くのユーザに人気のあるインフラ用ソフトウェアです。その人気の高さから、いつの間にかこれらの脆弱性が悪用される危険性があります。この脆弱性を防御するには、関連するパッチを適用するか、回避策を実行するかのいずれかの方法が必要です。ご使用のシステムが適切に保護されていることを確認するためには、もちろん両方の適用を検討することです。さらにVMware社からは、「ESXi」におて「OpenSLP」サービスを使用しない場合は、その機能を無効にすることを推奨しています。
これらの脆弱性を悪用する他の方法や、さらなる他の「ESXi」の脆弱性全般に関する情報があれば「ZDI」にお知らせください。Twitter上で筆者「@_wmliang_」や私たちのチームをフォローし、最新の脆弱性利用技術やセキュリティ修正パッチの情報を入手いただくことも可能です。
■トレンドマイクロの対策
法人利用者においては、脆弱性を利用する遠隔攻撃のトラフィックを、ネットワーク脅威防御ソリューション「TippingPoint」で検知、ブロックすることが可能です。また、ネットワーク挙動監視ソリューション「Deep Discovery ™ Inspector」は、ネットワーク内の不審な挙動を可視化します。
参考記事:
• 「CVE-2020-3992 & CVE-2021-21974: PRE-AUTH REMOTE CODE EXECUTION IN VMWARE ESXI」
By: Lucas Leong, ZDI
翻訳:与那城 務(Core Technology Marketing, Trend Micro™ Research)