BinDiffによる調査で明らかになったInternet Explorerのゼロデイ脆弱性「CVE-2019-1208」を利用するPoCについて解説

2018年6月、トレンドマイクロが運営する脆弱性発見・研究コミュニティ「Zero Day Initiative(ZDI)」は、Internet Explorer(IE)のメモリ解放後使用(Use After Free、UAF)の脆弱性についてMicrosoftに報告しました。深刻度「緊急」に該当する脆弱性とされ、識別番号「CVE-2019-1208」が割り当てられたこの脆弱性に対し、Microsoftは2019年9月のセキュリティ更新プログラムを公開し、対処済みとなりました。ZDIのリサーチャは、この脆弱性をバイナリコード分析ツール「BinDiff」によって発見、解析し、どのようにWindows 10「Redstone5(RS5)」においてこの脆弱性を突いた攻撃が可能になるかの概念実証(Proof of Concept、PoC)を報告しました。

■脆弱性「 CVE-2019-1208」とは

上述したように、「CVE-2019-1208」はUAFの脆弱性です。このクラスの脆弱性は、データを破損させ、プロセスを停止させる恐れがあります。これらの動作が引き起こされたタイミングに応じて、攻撃者は任意の、また遠隔からのコードの実行が可能になります。脆弱性の利用に成功した場合、攻撃者は、現在のユーザが持つシステム権限と同等の権限を取得する可能性があります。現在のユーザが管理者権限を所有していた場合、攻撃者はシステムを乗っ取り、以下の動作の実行が可能となります。

  • プログラムのインストールまたはアンインストール
  • データの表示と変更
  • 完全な権限を持つユーザーアカウントの作成

■脆弱性「CVE-2019-1208」による影響予測

この脆弱性を利用した攻撃のシナリオとして、ソーシャルエンジニアリングの技巧を施したフィッシングメールを送信し、ユーザが、IEで、脆弱性攻撃(エクスプロイト)コードが埋め込まれた不正なWebサイトにアクセスするように仕向けます。もしくは、脆弱性攻撃コードを含むファイルが添付されたスパムメールをユーザに送信する手口も考えられます。利用され得る添付ファイルの例として、IE仕様のレンダリングエンジンが有効になっているMicrosoft Officeのドキュメントファイル、またはMicrosoft Video ActiveXコントロールが埋め込まれたアプリケーションファイルが挙げられます。また、攻撃者は、ユーザが提供したコンテンツや広告を受け入れる正規のWebサイトを侵害することによって、脆弱性攻撃コードを埋め込むことも可能です。

図1:VbsJoinのコードフロー
図1:VbsJoinのコードフロー

■この脆弱性を利用した攻撃の可能性

Windows 10で既に無効とされていたVBScriptは、2019年8月13日、Windows 7、8、および8.1に実装されているInternet Explorer 11でも無効になりました。したがって、今回紹介するPoCはローカルモードで実証されたものです。しかし、Microsoftによれば、この設定はレジストリまたはグループポリシーを介して有効にすることが可能です。いずれにせよ、攻撃者はスパムメールやその他のソーシャルエンジニアリングと組み合わせることにより、この脆弱性を利用する攻撃を行う可能性があります。

■「CVE-2019-1208」が確認された経緯

発端は、2018年5月から6月にかけてVBScriptエンジンのAPI関数を含むモジュール「vbscript.dll」の関数に加えられた変更を比較しようとしてBinDiffを使用したことでした。そこで「SafeArrayAddRef」関数、「SafeArrayReleaseData」関数、および「SafeArrayReleaseDescriptor」関数を利用した変更が確認されました。さらに調査を進めるにあたり、過去に脆弱性「CVE-2018-8373」を見つけた経験から「VBScriptClass」関数を使用したところ、以下の手順でUAFの脆弱性をトリガーできることがわかりました。

  1. arr = Array(New MyClass) :「SafeArray」 を作成して「VBScriptclass: MyClass」を「arr[0]:」 に保存します。
  2. Callback: arr = Array(0) :「Join(arr)」は、MyClass「Public Default Property Get」関数のコールバックをトリガーします。このコールバックで、バリアント「arr」に新しい「SafeArray」を作成します。この新しい「SafeArray」は「SafeArrayAddRef」関数によって保護されていないため、通常のコードフローの仮定は、このコールバックによって破られます(図1を参照)。
  3. arr(0) = Join(arr):コールバック「Public Default Property Get」から戻ると、VbsJoinのコードフローは、「SafeArrayReleaseData」関数と「SafeArrayReleaseDescriptor」関数を呼び出して、「SafeArrayData」関数と「SafeArrayDescriptor」関数を参照する回数を減らします。しかし、新しい「SafeArray」は、「SafeArrayAddRef」関数によって保護されていないため、「SafeArrayData」関数および「SafeArrayDescriptor」関数の参照回数は0です。したがって、新しい「SafeArray」の「SafeArrayData」関数および「SafeArrayDescriptor」関数は、「SafeArrayReleaseData」関数および「SafeArrayReleaseDescriptor」関数を使用して解放されます(図2を参照)。

図2-1:メモリ内の「arr = Array(New MyClass)」
図2-1:メモリ内の「arr = Array(New MyClass)」

図2-2:メモリ内のarr = Array(0)のコード
図2-2:メモリ内のarr = Array(0)のコード

図2-3:コールバックのコード(赤枠内)
図2-3:コールバックのコード(赤枠内)

「VbsJoin」関数の戻り値を「arr(0)」に保存すると、PoCは「vbscript!AccessArray」関数でクラッシュします(図3)。これは、「SafeArrayDescriptor」が解放されますが、バリアント「arr」は解放された「SafeArrayDescriptor」のポインタを保存するためです。

図3:「vbscript!AccessArray」関数でPoCがクラッシュした様子を示すコード
図3:「vbscript!AccessArray」関数でPoCがクラッシュした様子を示すコード

■PoCはUAFの脆弱性をどう利用したか

今回のPoCではUAFがトリガーされる経緯を完全に再現するにあたって、基本的な文字列/バイナリ文字列(BSTR)をデータ構造として使用したため、限定的に成功したといえます。 「SafeArray」関数は多次元配列ですが、「VbsJoin」関数は1次元配列しか処理できないため、コールバックにおいて「SafeArray」関数の次元を変更しました。 残念ながらそれでは成功せず、「Joinで配列型が一致しない」というランタイムエラーが確認されました。そこで、エラー処理に有効なVBAのステートメント「On Error Resume Next」を使用して、このランタイムエラーを回避しました。以下の図4は、変更後のPoCです。

図4:VBAのステートメント「On Error Resume Next」を使用して変更したPoC
図4:VBAのステートメント「On Error Resume Next」を使用して変更したPoC

0x20バイトの解放されたメモリを取得した後、0x20バイトのBSTRを使用して、大きなサイズの「SafeArray」を偽造しました。 そして、ヒープ領域に対する攻撃「Heap Feng Shui」を利用して、このBSTRは0x20バイトの安定して解放されたメモリを再利用します。 以下の図5-1に示すように、最終的に、要素番号「0x7ffffffff」および要素サイズ1biteの偽の1次元SafeArrayを取得しました。

図5-1:偽のSafeArray
図5-1:偽のSafeArray

「0x00000000」から「0x7fffffff」までのメモリの読み取り/書き込みに使用する「SafeArray」を作成しました。図5-2に示されているように、Simon Zuckerbraun氏による技術を適用し、ヒープスプレー攻撃を利用して固定読み取り/書き込みアドレス「(0x28281000)」を取得しました。これは、脆弱性を突くために利用する読み取り/書き込みアドレスの一部を確保するために利用します。

図5-2:読み取り/書き込み用の固定アドレス
図5-2:読み取り/書き込み用の固定アドレス

■このUAF脆弱性を利用して遠隔からコードが実行される可能性

Simon Zuckerbraun氏のブログで説明されているようにオブジェクト「Scripting.Dictionary」を使用して遠隔からのコード実行(RCE、Remote Code Execution)を実行しましたが、偽の辞書を作成するためには別の方法を使用しました。今回は、図6に示すように、前述のBSTRを使用して、以下の手順を実行しました。

  1. 読み取り/書き込みメモリ機能を使用して、元の辞書メモリを読み取り、そのデータを1つのBSTRに保存し、「VBADictionary :: Exists」関数を「kernel32!Winexec」関数に置き換えます。
  2. Winexecパラメーター「(\ .. \ calc.exe)」をこのBSTRに書き込みます。
  3. このBSTRを「util_memory + 0x1000」に保存し、「fake_array(util_memory + 0x1000)」をオブジェクトにするために「util_memory + 0x1000 – 8 = 9」を変更します。
  4. 「fake_array(util_memory + &h1000).Exists」のダミーを利用して「Winexec」関数をトリガーします。

図6:偽の辞書のメモリレイアウト
図6:偽の辞書のメモリレイアウト

図7. 遠隔からのコード実行に成功
図7. 遠隔からのコード実行に成功

■被害に遭わないためには

単体では危険性は低いと見積もられた脆弱性であっても、攻撃者は他の方法と組み合わせたり、利用者を騙したりすることにより、有効化してくる場合があります。いずれにせよ、ユーザと法人組織は常に脆弱性に対するベストプラクティスを採用する必要があります。

  • パッチを適用して更新する
  • 必要でない場合はコンポーネントを無効、もしくは使用制限を設ける

この調査の技術的詳細は、こちら(英語)を参照してください。

参考記事:

翻訳: 下舘 紗耶加(Core Technology Marketing, Trend Micro™ Research)