Cisco Talos は、Cisco Advanced Malware Protection(AMP)の不正防止(Exploit Prevention)エンジンのレポートを受け、過去 2 ヵ月にわたって一連の Imminent RAT 感染を追跡してきました。AMP はホスト感染の前にマルウェアを阻止しましたが、最初の分析で RAT が展開されるまでにいくつかの前段階が存在することが強く示されました。驚いたことに、復元されたサンプルには Imminent RAT の痕跡は確認できず、代わりに商用のパッカーが見つかりました。
しかし実際は、検出を逃れ、分析を妨げるように設計された一連の攻撃が行われていました。外から見ると、手頃な価格で購入可能な「Obsidium」という市販のパッカーです。それは正式なソフトウェア ベンダーの知的財産を保護するために使われてきたツールです。このペイロードは Imminent と呼ばれる RAT で発生します。この RAT も正当な目的で使用されていたものです。Imminent は 25~100 ドルで販売される RAT で、価格は顧客の推定ユーザベースによって変わります。不正な使用を意図するものではありませんが、このケースでは検出内容に悪意があることが示されています。
不要と思われるアプリケーション(PUA)の検出で事足りる場合もありますが、誰もが PUA のブロックを有効化できるわけではありません。シスコには不正防止エンジンのような他のテクノロジーがあり、このような脅威の検出に適しています。シスコはこの調査結果を通して、複雑なパッカーを使った攻撃に対する調査の実態についてのみならず、静的検出を免れようとしたり、マルウェア サンドボックスの動的分析のメリットを妨害しようとする攻撃を AMP がどのように阻止するのかについても、みなさんにより深く理解していただけることを願っています。
AMP がこの特定の Imminent を検出し、マルウェアが検出機能から逃れるために使われるパッカーの複雑さを確認できた後も、私たちはさらに調査を続けることにしました。続いて行った動的実行によって、以下が示されました。
私たちは市販のパッカーツールの使用を特定できましたが、この特定のパッカーの実行ファイルがデバッグや仮想化をどの程度回避できるのかを知りたいとも考えました。最初は、数個のインスタンスが SEH 例外ハンドラをオーバーライドするのを確認するところから始まりました。これは 1 つのハンドラを FS:0 の前後でプッシュしてから、スタック ポインタを FS:0 に移動することで達成できます。これが可能になる理由は、32 ビットであるサンプルが SafeSEH でコンパイルされていないためです。意図的なアクセス違反や不正な指示が一部の準備コードにリダイレクトされて、悪意のあるコードが復号されます。
このオーバーライドはほとんどが準備コードを対象としているため、その多くは、ユーザランドのすべての例外が行き着く先である「ntdll- >KiUserExceptionDispatcher」に従うことでスキップできます。例外をアプリケーションに渡し、ジャンプ条件の手前でブレイクして、チェーンの中に別の例外が存在するかどうかを判定、またはランタイムを継続するかどうかを決定します。
最後に、ECX に保存されているポインタにしたがって、コンテキスト構造を解決し、NtContinue を呼び出すと実行される命令の EIP を決定します。EIP は、ランタイム中のこの時点の ECX に従い、32 ビット用のコンテキストにこのコンテキスト構造 を適用することで、手動で解決できます。
マルウェアは悪意のあるコードの各セクションを 1 つずつ複合、再暗号化するため、セクションごとに手動で確認しなければ、複合が完了する時点までのタイムラインを判別することが困難になります。暗号方式は、AES のネイティブ x86 命令とラッパー機能を使用しています。
最初のコード複合の後、複雑な API 解決のようなものが確認できます。最初の部分はバイナリの他の部分に似ていますが、逆アセンブルできないジャンクバイトを挿入することで分析全体を阻止します。
これにより、逆アセンブラによる制御フロー グラフや関数ブロックのレンダリングが非常に厄介になることが予想できます。いくつかのブレークポイントとリターン命令が後の方で出てきますが、同時に API 文字列が汎用レジスタのあちこちで出てくることがわかります。試行錯誤が繰り返された結果、解決済みの API アドレスが EAX に格納されている重要なリターン時点での中断が不可能ではないことがわかりました。リターン命令までデバッガを実行できますが、以下に示すようにトランポリン コードとして機能する新たなアクセス違反や不正な指示が発生します。エンド ユーザがペイロードを実行するパッカーにデバッグ回避機能を含めた場合、アクセス違反や不正な指示はパッカーの標準的な特徴となります。
もう 1 つ特筆すべきこととして、解決済みの API アドレスが中断されたり、リターン命令まで実行する場合のジャンプ先に指定されたりすることはあってはなりません。パッカーが目的の API に移動するために常にリターン命令を使うとは限りません。また、API のアドレスが直接使用されることはありませんが、代わりに関数内で命令が呼び出されます。どこまで深く影響を受けるかは各 API によって異なります。ここでの最善の対処は、API コードでいくつかのコールを中断することです。これを、解決済み API に渡される元のパラメータが表示されるよう十分早い段階で行う必要があります。さらに、パッカーのコードは API コードの中でトランポリン コードの標的をチェックし、リダイレクト(0xCC または int 3 disassembled)前のソフトウェアのブレークポイントを探します。
デバッギング セッションでこの制御を確立できれば、デバッグ回避のチェック機能に対処できるようになります。これは元のペイロードをアンパックするのに必要なステップです。サンプルを実行させたり、フル画像やコードの関連セクションを破棄したりする従来のテクニックは、デバッグ回避のようなチェックがあるケースでは機能しません。このパッカーのデバッグ回避のチェックには次のものが含まれます。
- クラスの登録。CallWindowProc によって呼び出されるコールバック パラメータを含めて CreateWindowsEx に渡されます。このコールバック関数自体が、要求された ProcessInformationClass の列挙子として設定された ProcessDebugPort と NtQueryInformationProcess を呼び出します。
- この API は、文書化されていない ProcessInformationClass の列挙子である ProcessDebugObjectHandle と ProcessDebugFlags を取得するために 2 回呼び出されます。
- NtQuerySystemInformation は、SystemInformationClass パラメータの文書化されていない列挙子(SystemKernelDebuggerInformation)と一緒に呼び出されます 。この特定のケースでは、標準の SYSTEM_BASIC_INFORMATION 構造は返されませんが、代わりに、SYSTEM_KERNEL_DEBUGGER_INFORMATION 構造が返され、そこには UCHAR KernelDebuggerEnabled と UCHAR KernelDebuggerNotPresent が含まれます。ユーザはフラグを適切に切り替えることで、このデバッガのチェックを回避できます。
- 無効なハンドルには CloseHandle が呼び出されます。プロセスのデバッグでは、API のサイレント障害が発生する代わりに、例外が生成されます。この場合、この例外からデバッガの検出をたどっていけます(EnumWindows -> MessageBoxA -> “デバッガが検出されました…”)。例外を破棄すると、デバッグでこのチェックを回避できます。
- CreateFileA は、次のデバッガ関連のファイル名を持つファイル オブジェクトがホストでインスタンス化できるかどうかを確認するために複数回呼び出されます。
\\.SICE \\.\NTICE \\.\NTFIRE
- 次のチェックは、実際のデバッガ チェックを開始する前に 20 個以上の API を解決するという点が特徴的です。幸い、チェックされる API は最後の数個のみ(InternalGetWindowText、IsWindowVisible、EnumWindows)です。前述のとおり、通常はアンパックのこの時点で EnumWindows が出てくるのは悪い兆しで、デバッガ チェックに失敗したことを意味します。このケースでは、そうではありません。EnumWindows に渡されるコールバック関数は、ブレークポイントと一緒に処理され、InternalGetWindowText と IsWindowVisible がスタンドアロン デバッガ チェックとしてコールされるまで繰り返される必要があります。
- 意図的なエラーの後に任意の値が SetLastError に渡されます。GetLastError は、設定値がデバッグ中も同じままであることを確認するためにコールされます。
- GetCurrentThread は現在のスレッド ハンドルを捉えて、THREAD_INFORMATION_CLASS の ThreadHideFromDebugger 列挙子と組み合わせて NtSetInformationThread に渡します。こうすることで、デバッガがある場合に、デバッガからこのプロセスを分離させます。
- CheckRemoteDebuggerPresent
- FindWindowW はウィンドウ名ではなく、次のデバッガ クラス名を検索します: ObsidianGUI、WinDbgFrameClass、ID、OLLYDBG
- CreateFileW は、\\.\VBoxGuest の作成が失敗していないかをチェックします
これはデバッグ回避フェーズの一部に過ぎません。説明が長くなるため、ここではマルウェアの仮想マシン回避テクニックについて触れませんが、学び始めるには十分な情報を提供できたと思います。Talos チームは、ベアメタル ホストでサンプルをアンパックし、最後のバイナリを破棄してみることにしました。そして、商用 RAT が悪い意図で使用される最終段階を特定できました。動的なドメイン名をピボットオフすることで、同様に複雑なパッカー(Themida など)のサンプルが明らかになりました。ホストではなく、さまざまな RAT(Talos がアンパックしたものを含む)のコントロール パネルが実行するケースです。
この一連の攻撃は、検出戦略をさらに複雑にします。最初は正式なソフトウェア ベンダーの知的財産を保護するために使われてきた商用のパッカーを取り上げました。さらには、正当な目的で使用されてきた商用 RAT でもペイロードが発生しました。この場合は PUA 検出で事足りますが、シスコには、さらなる調査のためにテレメトリを回避するだけでなく、動的に脅威を検出する不正防止エンジンのような技術があります。攻撃者は検出を回避するために執拗に新しい手法を試してきます。この特定のケースでは、市販のソフトウェアが無駄に使用されました。この攻撃は Cisco Advanced Malware Protection(AMP)の不正防止エンジンによって阻止され、その結果得られたイベントデータとして、攻撃者が標的に対して使用しているツールに関する貴重な情報が役立っています。
IOC(侵入の痕跡)
本来の Obsidium でパックされたサンプル
3bc0ae9cd143920a55a4a53c61dd516ce5069f3d9453d2a08fc47273f29d1cf3
アンパックされた Imminent RAT のサンプル
12cca4fcfe311d1136db6736e7f17854746a5e6c7a284c27ea84a5016bf982d7
本稿は 2019年1月17日に Talos Group のブログに投稿された「What we learned by unpacking a recent wave of Imminent RAT infections using AMP」の抄訳です。