CVE-2020-17053: Internet Explorerの新たな脆弱性について解説

本ブログ記事では、新たに確認されたInternet Explorer(IE)の脆弱性「CVE-2020-17053」について解説します。トレンドマイクロはこれまでもIE内に存在する脆弱性の解析結果について報告しています。2020年8月には、IEの実行時コンパイラ(JITエンジン: Just-In-Time Engine)の型推論によるエラーが、ゼロデイ脆弱性「CVE-2020-1380」をどのように引き起こしたかについて解説しました。このゼロデイ脆弱性は、ArrayBufferを無効にすることで悪用可能となり、メモリ解放後使用(Use-After-Free、UAF)の脆弱性をもたらします。弊社は、この脆弱性の根本原因を解析しているときに、同様の脆弱性をトリガーする別の方法を見つけました。この方法ではJITエンジンは必要がないため、「CVE-2020-17053」とは別の脆弱性と判断されました。このセキュリティ上の弱点(バグ)は、トレンドマイクロが運営する脆弱性発見コミュニティ「Zero Day Initiative(ZDI)」により2020年9月にMicrosoft社に提出されました。脆弱性としてCVE-2020-17053が割り当てられ、Microsoftは11月にリリースされた月例セキュリティパッチ「Patch Tuesday」にて修正を行いました。

アイテムをTypedArrayに設定する

CVE-2020-1380の概念実証(PoC)コードでは、JITコンパイルされたコードによって生成されるコードスニペット「arr[0] = value1,」が、コールバック関数であるオーバーライドされたvalueOfを呼び出すことによってUAFバグをトリガーすることに留意してください。

図1:CVE-2020-1380における部分的なPoC

図1:CVE-2020-1380における部分的なPoC

このJITエンジンは、アイテムを型付き配列(TypedArray)に設定する操作を実行します。jscript9.dll内でJITエンジンを用いずにアイテムをTypedArrayに設定するとどうなるでしょう。

TypedArrayへのアイテムの設定がJIT内で実行されない場合、jscript9.dllはインタープリタ内で次のコードスニペットを使用します(図2)。

図2:Js::TypedArray<float,0>::BaseTypedDirectSetItemのコードスニペット

図2:Js::TypedArray<float,0>::BaseTypedDirectSetItemのコードスニペット

この関数には4つの主要な手順があります。

  1. 関数Js :: JavascriptConversion :: ToNumber()を初めに呼び出す
  2. TypedArrayのArrayBufferが切り離されているかどうかを確認する
  3. 設定された要素のインデックスをTypedArrayの長さと比較する
  4. 関数Js :: JavascriptConversion :: ToNumber()をもう一度呼び出し、値をArrayBufferに設定する

上記の手順において、なぜ関数Js :: JavascriptConversion :: ToNumber()が2回呼び出されているのでしょうか。

おそらくその答えは、Js :: JavascriptConversion :: ToNumber()の最初の呼び出しにあります。これは、ユーザコード内で潜在的なコールバック関数を呼び出そうと試みる呼び出しです。攻撃者はこのコールバックの機会を悪用して、TypedArrayのArrayBufferを解放し、UAFの脆弱性を取得する可能性があります。この最初の試行変換の後、TypedArrayのArrayBufferが切り離されているかどうかを確認します。これは必要な確認作業であり、ユーザコード内のコールバックでArrayBufferのメモリが解放されるのを防ぎます。

切り離されているかどうかの確認後、変換用関数を再度呼び出し、変換結果をArrayBufferに保存します。図3は、前述のコードを実行する流れを示しています。

図3:Js::TypedArray<float,0>::BaseTypedDirectSetItemのコードを実行する流れ

図3:Js::TypedArray<float,0>::BaseTypedDirectSetItemのコードを実行する流れ

この段階で、潜在的な問題が何であるかを確認できます。前述のコードは最初の変換呼び出しのみを確認しますが、2番目の変換呼び出しではどうでしょう。ユーザ定義のコールバック関数で、コールバックごとに異なるコードを実行する流れを持つことができるか確認してみましょう。

図4:潜在的な問題

図4:潜在的な問題

JITエンジンなしでArrayBufferを無効にする

CVE-2020-17053の概念実証を図5に示します。

図5:CVE-2020-17053の概念実証

図5:CVE-2020-17053の概念実証

以下の手順を実行した場合、このバグが発生する可能性があります。

  1. 関数「pwn」が、TypedArrayの指定されたインデックスの要素にアイテムを設定するために使用されます
  2. オブジェクト「obj」には、コールバック関数であるオーバーライドされたvalueOfがあります。これは、CVE-2020-1380で利用されたように、WorkerによってArrayBufferを解放するために使用されます
  3. 関数pwnを呼び出し、2番目のパラメータをobjとして設定します。コード「’arr[index] = value'[EC(1]」が実行されると、ネイティブ関数「Js :: JavascriptConversion :: ToNumber()」が呼び出され、コールバック関数であるvalueOfが2回呼び出されます
  4. 最初のコールバック「valueOf」では、変数「flag」が0であるため、ArrayBufferを無効にする操作は実行されません。最初のコールバック「valueOf」の最後に、フラグが1に設定されます
  5. 2番目のコールバック「valueOf」では、変数「flag」が1に等しいため、ArrayBufferを無効にする操作が実行され、関数「Js::JavascriptConversion::ToNumber()」の2番目の変換呼び出し後にArrayBufferのデタッチの確認がないため、UAFの脆弱性が発生します

最後に、ArrayBufferの解放されたメモリを取得します。これにより、UAFの脆弱性が発生します。あらゆる種類のTypedArrayがこのバグを引き起こす可能性がありますが、11月のパッチによりすべて修正されています。

図6:クラッシュ時のスクリーンショット

図6:クラッシュ時のスクリーンショット

脆弱性攻撃コードの解析

解放されたArrayBufferのメモリサイズは制御可能であるため、このUAFの脆弱性は、以下の手順を実行することで簡単に悪用されてしまいます。

  1. LargeHeapBlockを使用して、複数のJavaScriptの配列とオブジェクトを作成することにより、解放されたメモリ(CRTヒープで割り当てられている)を占有します
  2. コールバック関数「valueOf」の戻り値を使用して、LargeHeapBlockのallocCountを0に設定します
  3. 関数「CollectGarbage()」を手動で呼び出して、allocCountが0に設定したLargeHeapBlockオブジェクトを解放します。これにより、IEのカスタムヒープでUAFの脆弱性を取得する2回目の機会が発生します
  4. 複数のJavaScriptの配列とオブジェクトを再度作成します。今回は、IEのカスタムヒープでUAFの脆弱性の2回目の機会を利用し、2つのJavaScriptの配列が同じバッファを指すようにします
  5. 2つのJavaScriptの配列を使用して、読み取り/書き込みロック(ロックプリミティブ)を実現し、最後にコードを実行します
結論

CVE-2020-1380のPoCコードの解析から、攻撃者は、過去に悪用していた「vbscript.dll」や「jscript.dll」を利用するのではなく、今後は「jscript9.dll」を標的とすることが推測できます。本ブログ記事では、単純なリバースエンジニアリングによってJITエンジンを使用しない、別に存在する同様のUAFの脆弱性をどのように見つけたかを解説しました。jscript9.dllを取り巻くセキュリティの問題は、まだ終わっていない可能性があります。

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

脆弱性を利用する攻撃は対象の脆弱性をアップデートすることで回避可能です。ご利用のソフトウェアに関してはアップデートを欠かさず、常に最新のバージョンを利用してください。攻撃者は以前の脆弱性についてアップデートを行っていない環境を探し出し、攻撃します。業務上の都合などにより速やかにアップデートが行えない場合には、脆弱性を含む機能の停止や攻撃を防ぐ技術的対策など、その他の回避策を怠らないことが重要です。

■トレンドマイクロの対策

トレンドマイクロ製品では脆弱性を利用する遠隔攻撃の対策として、仮想パッチが用意されています。この仮想パッチは、公式のベンダーパッチの適用前に、脆弱性からデバイスを保護する追加のセキュリティレイヤーを提供します。仮想パッチとは名前からわかるように、第三者が対象の脆弱性を悪用しようとした場合に、環境を保護するものです。仮想パッチは、各企業に適した方法でパッチの適用を可能にする、重要なセーフティネットになる場合があります。

Trend Micro DeepSecurity™およびTrend Micro Apex One™は、これらの脆弱性を利用しようとする攻撃からシステムを保護し、以下のルールを介してユーザを保護します。

  • 1010602 – Microsoft Internet Explorer Memory Corruption Vulnerability (CVE-2020-17053)

TrendMicro™TippingPoint™は、以下のルールを介して、以下の脆弱性を標的とするエクスプロイトからユーザを保護します。

  • 38412: HTTP: Microsoft Internet Explorer Worker Use-After-Free Vulnerability (CVE-2020-17053)

参考記事:

翻訳:益見 和宏(Core Technology Marketing, Trend Micro™ Research)