完璧なバグ:Flashで型の混乱を使用します。 パート1

こんにちは! 明日は、 「リバースエンジニアリング」コースの新しいグループでクラスを開始します。 従来、トピックに関する有用な資料の翻訳を共有しています。 行こう!



この攻撃が非常に信頼できることは、一部の攻撃者にとって重要です。 既知のプラットフォームとバージョンのFlashを搭載したシステムで実行すると、常にコードが実行されるはずです。 作成するには、特に高品質のバグを使用できます。 この記事では、これらのバグの1つを使用することと、それを信頼性の高い操作に特に適したものにする要因について説明します。











CVE-2015-3077は、Adobe Flash ボタンおよびMovieClipフィルターセッターのタイプ混乱の問題であり、これにより、あらゆるタイプのフィルターを他のフィルターと混同することができます。 2015年12月上旬に報告し、5月に修正されました。 このバグは、クラッカーがフィルターオブジェクトの初期化に使用したコンストラクターを上書きできるために発生します。 問題を再現するサンプルコードを以下に示します。







このコードは、Flash CSでのコンパイルに必要な[]演算子を使用しているため、やや混乱します。 論理的に同等のコード(コンパイルするという事実ではありません)を以下に示します。







このコードは、オブジェクトのフィルターフィールド(ButtonまたはMovieClip)をBlurFilterに設定します。これは、Flashに直接保存されます。 BlurFilterコンストラクターは、ConvolutionFilterコンストラクターによって上書きされます。 その後、ゲッターが呼び出され、元のBlurFilterを格納するActionScriptオブジェクトが作成されます。 ただし、コンストラクタは既に上書きされているため、ConvolutionFilterが呼び出されます。 これにより、元のBlueFilterの戻り値によってサポートされるConvolutionFilter型のオブジェクトが生成されます。



最終的に、ConvolutionFilterフィールドは、BlurFilterに属しているかのようにアクセス(読み取りおよび書き込み)できます。 他のタイプのフィルターについても同様です。 これにより、悪用に役立つさまざまな操作が可能になります。



次の図は、64ビットLinuxでこの脆弱性を使用して混乱する可能性があるメモリ内の元のオブジェクトの場所を示しています。







2つの場合、ポインターは整数、および操作可能な浮動小数点数に相当します。 これは、ポインターを直接読み書きできることを意味します。 さらに、オブジェクトのフィールドはクラス定義に従ってサイズ順に並べ替えられているため、常に予測可能な場所にあるため、書き込みや読み取りが失敗することはありません。 これらのプロパティは、エクスプロイトの信頼性を確保するために重要です。



エクスプロイト



この問題を悪用するには、タイプの混乱を繰り返し実行する必要があるため、タイプの混乱のためのユーティリティ関数FilterConfuse.confuseを作成することから始めました。 また、整理します。ActionScriptフィルターコンストラクターを通常の状態に戻し、関数自体の外部のActionScriptの動作に影響を与えずに脆弱な関数を繰り返し呼び出します。

最初のステップは、仮想関数テーブルのアドレス(簡単に言うとvtable)を定義することによりASLRをバイパスすることでした。 これの理想的な方法は、オブジェクトとvtableを、操作可能なvtableに重複する要素があるオブジェクトと混同することです。 ただし、すべてのフィルターオブジェクトのvtableのオフセットは同じです。 代わりに、DisplacementMapFilterのBitmapDataオブジェクトを使用して、vtableアドレスを決定しました。



オブジェクトのBitmapDataメモリ内の場所を特定するために、DisplacementMapFilterとBevelFilterを混同しました。 これにより、DisplacementMapFilterに格納されているBitmapDataポインターがBevelFilterカラープロパティ( shadowColorshadowAlphahighlightColorおよびhighlightAlpha )に合わせられました。 これらのプロパティは、2つの32ビット整数(上下にscolorおよびhcolorとして表示)によってサポートされ、カラープロパティは各整数の24ビットにアクセスし、アルファプロパティは上位8ビットにアクセスします。 これらのプロパティを読み取り、ビット演算を使用して組み合わせた場合、オブジェクトの即時BitmapDataアドレスを抽出できます。







次に、BitmapDataオブジェクトの上部からvtableを読み取る必要があります。 このために、ConvolutionFilterオブジェクトのmatrixプロパティを使用しました。 プロパティを設定するときにメモリが割り当てられる浮動小数点数の配列へのポインタとして格納され、プロパティを受け取るとこれらの数値を含むActionScript配列が返されます。 マトリックスポインターをBitmapDataオブジェクトに設定することにより、このオブジェクトの内容をメモリから浮動小数点数の配列として読み取ることができます。



ポインターを設定するには、ConvolutionFilterオブジェクトとDisplacementMapFilterオブジェクト(上記で使用したDisplacementMapFilterとは異なります!)と混同し、 mapPointプロパティで上記のオブジェクトのBitmapDataの位置を設定します。 mapPointプロパティは、ConvolutionFilterのマトリックスポインターに対応する整数座標xおよびy(下図のp_xおよびp_y)を持つポイントであり、この値を簡単に設定できます。 その後、ConvolutionFilterオブジェクトの行列配列を使用してBitmapDataオブジェクトからvtableを読み取ることが可能になりました(このため、オブジェクトをDisplacementBitmapFilterと混同し、ConvolutionFilterと混同しなければならないことに注意してください)。







この時点で、浮動小数点数の使用により、エクスプロイトの信頼性を維持することがより困難になります。 値vtable_lowおよびvtable_highは、ConvolutionFilterマトリックスから浮動小数点数として読み取られます。これは配列型であるためです。 ただし、残念ながら、すべての有効なポインター値が有効な浮動小数点数であるとは限りません。 つまり、値を読み取るとNaNが返されるか、さらに悪いことに、完全に正しい数値ではありません。



理想的には、この問題を解決するには、ゲッターを介してvtable_lowおよびvtable_highにアクセスする必要があります。ゲッターは整数として解釈しますが、これはフィルター要素が通常その機能のためにフロートするためではありません。



幸いなことに、AS2仮想マシンは浮動小数点数を解釈するのに十分なほど怠けていて、ActionScriptで操作が実行されたときにのみ値を浮動小数点に変換します。 通常、元の演算は、算術演算などの特別な演算を除き、解釈を必要としません。 これは、浮動小数点数をマトリックス配列からvtable_lowまたはvtable_highにコピーする場合、floatに対して無効であっても値をメモリに保持しますが、コピー先の変数はActionScriptまたはネイティブで算術演算を実行するために使用されませんコード。 したがって、変数の値が、32ビット値の全範囲をサポートする別の型(intなど)と即座に混同される場合、行列配列のメモリ内の元の値と同じであることが保証されます。 したがって、エクスプロイトでの信頼性の低下を避けるために、ActionScriptでフロートを操作する前に型の混乱を実行することが重要です。



これを行うために、フィルターで型の混乱を使用して整数から浮動関数および浮動小数点から整数関数を実装する変換クラスFloatConverterを作成しました。 マトリックスの ColorMatrixFilterプロパティを混同しないでください(ConvolutionFilterマトリックスプロパティと混同しないでください)。これは、一連の組み込みフロートであり、異なるintバイトを参照するGlowFilter カラーおよびアルファプロパティと混同します。







したがって、floatからintへの信頼性の高い変換を実装できますが、残念ながら、これは反対方向では確実に機能しません。 ActionScriptのColorMatrixのカラー配列にアクセスするには、最初の配列のみにアクセスする場合でも、配列全体がコピーされます。 配列をコピーすると、各要素はNumberに変換されます。これには、ポインターの呼び出し(たとえば、オブジェクトのvalueOfの呼び出し)が含まれます。 色配列はGlowFilterクラス全体の中で最も長いため、GlowFilterに絡まるとヒープに落ちます。 これは、このヒープから不明な値の変換が発生する可能性があり、Numberへの変換時に無効なポインターを参照するとクラッシュする可能性があることを意味します。 したがって、int-to-floatの場合は、ConvolutionFilterとDisplacementMapFilterという別の混乱を使用してfloatコンバーターを実装しました。これは直接キャストであり、ヒープから不明な値を呼び出しません。







これにより、ヒープから不明な値にアクセスすることで発生するクラッシュの問題が解決されますが、残念ながら、このエクスプロイトのフロートに関連する別の信頼性の問題があります。 ConvolutionFilterマトリックスゲッターの実装が原因です。 ActionScript 2の数値はすべてNumber型であり、整数と倍精度数値(double)へのポインターの和集合です。 元のConvolutionFilterマトリックスは浮動小数点数の配列として保存されますが、ActionScript配列にコピーされ、マトリックスゲッターが呼び出されたときにアクセスを維持し、プロセスで値がdoubleに変換されます。 次に、floatコンバーターを呼び出すときに、浮動小数点数に変換されます。



浮動小数点数を倍精度数にキャストすると、通常はその値が保存されますが、float値がSNaNの場合は保存されません。 浮動小数点の仕様によると、NaNには2つのタイプがあります。サイレントNaN(QNaN)とシグナルNaN(SNaN)です。 QNaNが表示されても何も起こりませんが、場合によってはSNaNは浮動小数点例外をスローします。 x86では、doubleをfloatに変換すると、予期しない例外を回避するために、常にdoubleがSNaNから来た場合でもQNaNになります。



したがって、ポインターの下位ビットがSNaNの場合、QNaNに変換されます。つまり、最初のビット(最初の仮数ビット、ビット22)が設定されるべきではないときに設定されます。 この問題は、vtableを読み取るときに回避できます。最初のビットを含むポインターの3番目のバイトは、現在の値を確認するためのアライメントなしで読み取ることができます。 そのため、コードは(ビットマップポインターを1増やしてvtableを再度読み取った後)アライメントなしで読み取り、floatがSNaNであることが判明した場合にint値を調整します。



上記のフロートコンバーターを使用して、vtableアドレスを整数に変換できます。 次に、このアドレスを使用して実行するコードを取得する必要があります。 命令ポインターを移動する簡単な方法は、オブジェクトのvtable(またはvtableを持つオブジェクトへのポインター)を上書きすることです。 これは、ConvolutionFilterマトリックス配列とDisplacementFilterポインターのBitmapDataを混同することで実行できます。



最初の部分の終わり。 翻訳の第2部は少し後に公開されます。今、あなたのコメントを待っています。OTUS リバースエンジニアリングコースに皆さんを招待します。



All Articles