投稿者:Paul Rascagneres
はじめに
先日のブログ記事で説明した CCleaner 64bit ステージ 2 では、「Symantec Endpoint」の一部である正当な実行可能ファイルが攻撃者によって改ざんされたことを説明しました。改ざんされたファイルの名前は「EFACli64.dll」です。改ざんは、コンパイラに含まれるランタイム コード、正確には __security_init_cookie() 関数内に見られます。関数内では、悪意のあるコードにジャンプするため最終命令が改ざんされています。しかし、一般的な逆アセンブラの IDA Pro ではこの改ざん箇所を正しく表示できません。これについては後半で説明します。記事の末尾では、この種の改ざんを特定する方法と、その方法が抱える制約についてもご紹介します。
改ざんされたランタイムを IDA Pro で分析すると・・・
改ざんされた第 2 の実行可能ファイルを IDA Pro で分析したところ、グラフ ビューではランタイムのアセンブリを正しく表示できないことが判明しました。
上図のグラフ ビューによると、最後の命令は「pop rdi」です。しかしテキスト ビューに切り替えると、最後の命令が実際には JMP(悪意のあるコードへのジャンプ)命令であることが一目瞭然です。
オープンソースの逆アセンブラ「Radare2」で開くと、関数が実際に JMP 命令で終了していることを再確認できます。
ここで生じた最初の疑問点は、IDA Pro で最後の(そして最も重要な)命令が正しく表示されない理由です。
ただし IDA Pro はオープンソース ソフトウェアではないため、コードを確認するという訳にもいきません。そこで、IDA Pro は pdata セクションを使用してランタイム関数の先頭と末尾を取得するという仮説を立てました。この仮説については、次のセクションで説明します。
2 番目の疑問点は、攻撃者が分析を妨害するためにこの手口を意図的に使用したかどうかです。IDA Pro から JMP 命令を隠す意図があった可能性もありますが、単なるまぐれであった可能性も拭えません。
PDATA セクション
pdata セクションはこちらの記事 [英語]で Microsoft 社が説明していますが、この中には、例外処理に使用される関数テーブル エントリの配列が含まれています。今回の例では、pdata セクションに次の構造が含まれています。
+0x000: Begin Address: The RVA of the corresponding function. +0x004: End Address: The RVA of the end of the function. +0x008: Unwind Information: The RVA of the unwind information.
__security_init_cookie() の機能に関するデータは次のとおりです。
+0x000: 0000F620 -> RVA of the beginning of __security_init_cookie() +0x004: 0000F6D3 -> RVA of the end of __security_init_cookie() +0x008: 00010464
関数の終了アドレス(0xF6D3)は、JMP 命令の途中にあります。(0xF6D3 を 0xF6D7 で置き換えて)関数最後のアドレスにパッチを当てれば、IDA Pro でも最後の命令(JMP 命令)を問題なく表示できます。そのため、IDA Pro は実際に pdata セクションを使用してランタイム関数を取得していると想定できます。
不審なランタイムを検出するための Python スクリプト
これまでの仮定に基づき、pdata セクションを足がかりに不審なランタイムを検出する簡単なスクリプトを作成しました。スクリプトの大まかな流れは、pdata セクションで指定されたアドレスに基づいてランタイムをスキャンし、最後の命令を探すというものです。命令が期待されたものでない場合(POC では validInstructions = [ “ret”, “retn”, “jmp”, “int3” ])、不審なランタイム関数として警告します。CCleaner の第 2 ステージの出力は次のようになります。
user@lab:$ ./pdata_check.py sample.exe { 'ASM': [ u'mov qword ptr [rsp + 0x18], rbx', u'push rdi', u'sub rsp, 0x20', [...redacted…] u'mov qword ptr [rip + 0x3ac8], r11', u'mov rbx, qword ptr [rsp + 0x40]', u'add rsp, 0x20', u'pop rdi'], 'StartRaw': '0xea20', 'StartVA': '0x0000f620', 'StopRaw': '0xead3', 'StopVA': '0x0000f6d3', 'end': 'KO', 'lastASM': u'pop rdi'}
上のスクリプトは pefile と capstone に基づいています。出力結果は、0x0000f620 (RVA) でランタイムが「ポップ」命令で終了したことを示していますが、これは珍しいことです。
制限事項
今回のような逆アセンブリ対抗手口では、ここで説明した検出アプローチが特効薬とはなりません。多様な 64 ビット バイナリを用いて検証しましたが、正当なバイナリの多くには一貫性のない pdata セクションが含まれています。このため偽陽性が頻繁に検出されます。さらに、攻撃者は pdata セクションにパッチを適用して追加のバイトを含める可能性さえあります。この場合だとスクリプトに異常は見られず、IDA Pro のブラフ ビューでは追加のオペコードが正しく表示されます。マルウェア研究者にとって、このアプローチはバイナリ分析の補足的な手段だと言えます。
まとめ
侵害された正当なバイナリの分析はマルウェア研究者にとって大きな課題です。サプライチェーン攻撃の新たな傾向により、一見正当なバイナリコードに対する分析需要は今後さらに高まると予想されます。正当なアプリケーションが侵害されると、悪意のあるペイロードは膨大な数の正当なコードに隠れる可能性があります。今回のケースでは、もう 1 つの課題があります。それは、IDA Pro の出力を完全には信頼できないことです。今回の手口が故意であるのかどうかは分かりませんが、結果は同じです。つまり研究者は悪意のあるコードを容易に見逃す可能性があります。Talos では不審なランタイム機能を特定するためのスクリプトを提供していますが、これは特効薬ではなく、ツールキットに追加された新しい補助ツールとして捉える必要があります。
本稿は 2017年10月12日に Talos Group のブログに投稿された「Disassembler and Runtime Analysis」の抄訳です。