Unit 42

LabyREnthセキュリティ コンテスト: Windows種目7~9の解答

Clock Icon 5 min read

今回このブログシリーズでは、LabyREnth、Unit 42セキュリティ コンテストの課題の解答を明らかにします。週に1種目ずつ解答を公開しますが、今回は、Windows種目の課題7~9です。

Windows 7課題: 誰かがこのpcapと実行可能ファイルを見つけた。位置に着いて、用意、ドン!

課題作成者: Josh Grunzweig @jgrunzweig

この課題では、PCAPとWindows実行可能ファイルの両方が用意されていました。PCAPファイルをざっと見ると、それぞれほぼ同じサイズの172.16.95.1から172.16.95.190への多数の個別の接続があることがわかります。

図1 G0blinKing pcapファイル内の接続

特定の接続を見ると、それぞれ一度に1バイトのデータを転送していることがわかります。

図2 G0blinKing pcapファイルのストリーム0内のデータ

おそらく、一部のデータはホスト間で一度に1バイトずつ送信されていると結論付けることができそうです。どのデータが生成されているかを判別するために、Windows実行可能ファイルを調べる必要があります。

ファイルを開くと、pdb文字列が上書きされたマイナーなイースター エッグが表示されます。

図3 上書きされたpdbパス

このファイルにとっては不運ですが、あまり逆アセンブルされていないため、関連コードを含む実際の関数にジャンプするだけの多数の関数が見つかります。これは、ファイルをコンパイルした際の副産物にすぎません。ただし、コードに目を通すと、コードの実際のコアの機能がオフセット0x412300にある関数で始まることがわかります。

サンプルのクイック トリアージと呼び出された関数を実行すると、何が実行されているか概要を把握できます。以下の図では、関数が実行している内容の推測に基づいていくつかの関数に名前を付けている点に注意してください。

図4 実行可能ファイルのメイン関数

そこで、file.txtに含まれるデータに対して何らかの形式の暗号化が実行され、その後、エンコードされて、PCAPの生成元であるネットワーク経由で送信されていると、確実に結論付けることができます。この時点では、暗号化とエンコードの間にそれぞれ何が起こっているかを識別する必要しかありません。

暗号化については、主に役割を担っていると見られる関数を見つけ出します。また、暗号化で使用されたキーである可能性が高い、‘AWildKeyAppears!’と適切に名付けられた文字列を特定します。この関数の最初の部分を見ると、以下の図に示すとおり、多数の定数が識別されます。

図5 暗号化関数

これらの多数の定数は、結局は偽装であることがわかります。ただし、定数0x9E3769B9を調べると、この定数はTEA/XTEA暗号化アルゴリズム内で使用されています。さらに、この関数のレビューから、単に、いくつかの偽装が成されたXTEAを扱っているだけであることがわかります。以下の元のソースは、コンパイル前の内容を示しています。

次に、エンコード関数に進むことができます。一見、base64のように見えます。ただし、アルファベットを見ると、標準ではないようです。以下は、従来のbase64のアルファベットです。

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/

Windows実行可能ファイル内で見つかったアルファベットは以下のとおりです。

qtgJYKa8y5L4flzMQ/BsGpSkHIjhVrm3NCAi9cbeXvuwDx+R6dO7ZPEno21T0UFW

これがわかれば、PCAPファイルの解析と、ネットワーク経由で送信された定数の復号の両方を実行するスクリプトを作成できます。これを実現するため、以下のスクリプトが作成されました。

このスクリプトを提供されたPCAPファイルに対して実行すると、以下の結果が得られます。

PAN{did_1_mention_th0se_pupp3ts_fr34ked_m3_out_recent1y?}

Windows 8課題: Windowsカーネルのデバッグの準備をする! ()

課題作成者: Esmid Idrizovic @xedi25

初期分析

PEビューアーでファイルrevloader.exeを開くと、それがPE64ファイルであることがわかります。ファイルには、RCDataディレクトリに3つの暗号化されていないリソースが含まれています。3つのリソース ファイルをすべて抽出して、これらのファイルの内容を調べます。

revloader.exeのリソース

リソース101

このファイルもPE64で、いくつかのバージョン情報文字列が含まれています。それらから、それがDSEFixであることがわかります。DSEFixは、署名付きVirtualBoxドライバ内でエクスプロイトを使用して、Windowsにおけるドライバの署名の強制(DSE)をバイパスできるツールです。DSEFixを使用することで、DSEを無効にし、必要なドライバをロードできます。そのため、この課題は、署名なしWindowsドライバを使用することで解決できそうです。

リソース102

これは、ドライバ自体です。これもPE64で、FLTMGR.SYSを使用しています。バージョン情報から、内部名がrevhunt.sysのWindowsドライバであることがわかります。ここでもファイルは未署名であるため、revloader.exeがDSEFixを使用してrevhunt.sysをロードするものと考えると理に適っています。

リソース103

これは、ドライバのINFファイルです。これがミニ ファイル フィルタ ドライバであることと、高度31337を使用していることがわかります。

Revloader

revloader.exeを起動して、それがDSEFixドライバをドロップし、DSEFixを実行して、Windowsドライバの署名の強制を無効にしていることを確認します。インポート テーブルを調べると、この種の作業に使用された可能性がある重要な関数を見つけることができます。

  • LookupPrivilegeValueW/AdjustTokenPrivilege: ドライバをロードするために不足している権限を獲得します(“SeLoadDriverPrivilege”)。
  • LoadResource/FindResource: リソースを見つけ、それをメモリにマッピングします。
  • CreateFileW: ディスク上にファイルを作成するか、それにアクセスします。
  • WinExec: ファイルを実行します。
  • FilterLoad/FilterUnload: ミニ フィルタ ドライバをロードおよびアンロードします。

FilterLoadへの相互参照を辿ると、ドライバをロードするための関数が0x140001800にあることがわかります。その関数は、InstallHinfSectionWと引数“DefaultInstall 132 <filename.inf>”を使用して、ドライバをインストールしてから、FilterLoadを使用してドライバをロードします。関数を逆に辿ると、この関数がメイン関数から呼び出されていることがわかります。また、さらにいくつかの興味深い呼び出しがあることもわかります。

0x140001FD0にある関数は、引数101、102および103で呼び出されることが判明しました。つまり、この関数はおそらく埋め込まれたリソースにアクセスしています。関数を調べると、それがFindResourceW、SizeofResource、LoadResource、CreateFileWおよびWriteFileを使用していることがわかります。関数の名前をDropResourceに変更することができます。これらの3つの呼び出しの後、WinExecを呼び出します。

つまり、revloaderはリソースを現在のディレクトリにドロップして(リソース101、102、103)、その後、ミニ ファイル フィルタ ドライバ(リソース102 + 103)をロードした後、dsefix.exe (リソース101)を実行しています。

では、revloader.exeを実行し、ドライバをカーネルにロードして、それが動作するかどうかを確認しましょう。

“Welcome to revhunt x86-x64”と表示されます。次に、ドライバを分析し、フラグを見つけてみましょう。

Revhunt

IDA Proでrevhunt.sysを開くと、DriverEntry関数内に、0x140007000へのジャンプがあることがわかります。それが、実際のメイン関数のようです。

ドライバが、KdDebuggerNotPresentやKdDisableDebuggerなどのいくつかのカーネル アンチデバッグ関数を使用していることがわかります。つまり、カーネル デバッガを仮想マシンに接続すると、関数KdDisableDebuggerがその接続を切断します。NOP命令を使用して、KdDebuggerNotPresentおよびKdDisableDebuggerにパッチを適用する必要があります。または、ドライバが署名されていないため、ドライバ自体の内部の呼び出しにパッチを適用することもできます。

さらに、ドライバがFltRegisterFilterとFltStartFilteringを使用していることもわかります。FltRegisterFilterの定義を調べてみましょう。

FltRegisterFilterは、第2引数としてFLT_REGISTRATION構造をとります。これには、ミニ ファイル フィルタ ドライバの登録に関する情報(フラグ、コールバック ルーチンなど)が含まれます。この場合のFltRegisterFilterの第2引数は、次のとおりです。

IDA Proを使用して、0x140003140にある構造をFLT_REGISTRATIONとして設定しますが、それを行う前に、正しいタイプ ライブラリ、Windowsドライバ キット8 (カーネル モード) – wdk8_kmをロードする必要があります。また、より新しいDDKライブラリも取得できます。これで、正しい構造を設定できます。

それを使用して、各サブ関数の使用目的をすばやく識別できます。構造のフラグが0x02に設定されており、FLTFL_REGISTRATION_SUPPORT_NPFS_MSFSであることもわかります。MSDNによると、それはドライバが、通常のファイル イベントと同様に、名前付きパイプとメールスロット リクエストをサポートしていることを意味しています。

次に、OperationRegistrationで同じことを実行し、FLT_OPERATION_REGISTRATIONの構造を設定してみましょう。

PreOperationとPostOperationのコードをざっと確認すると、実際に興味深いコードはPostOperation関数内で動作していることがわかります。そこで、それを分析してみましょう。

PostOperationコールバック

始める前に、IDA Proで新しい関数定義をMSDN内の正しい定義に設定する必要があります。それによって、分析が大幅に容易になります(Hex-Rays Decompilerを使用している場合は、より的確に逆コンパイルされます)。関数定義を次のように変更してみましょう。

正しいタイプ ライブラリをロードすることで、コードがより読み取り易くなるため、分析を簡素化できます。

関数が、現在のFileObjectがメールスロット イベント(FO_MAILSLOT)であるかどうか、オブジェクトの現在のファイル名が18文字長であるかどうか、“\gsrt.txt”であるかどうか、さらに、現在のイベントがファイルを開いているかどうかをチェックしていることがわかります。当てはまる場合には、関数はFltReadFileを使用してファイルから読み取り、そのコンテンツと暗号化されたデータを比較します。

では、pythonスクリプトを使用して、それをすばやくデコードしてみましょう。

つまり、コンテンツとして“labyrenth.com”を含む“\gsrt.txt”という名前のファイルを作成して、そのファイルを開く必要があります。その後、スタック上に初期化された別のバイト配列があることがわかります。以前と同じ手法で、それをデコードします。

それを私たちのスクリプトに記述し、その文字列もデコードします。

それは、プロセス間通信(IPC)を使用して同じことを実行できたことを気付かせてくれるヒントのようです。再びスクロール アップすると、サンプルが現在のFileObject.FlagsとFO_MAILSLOTを比較したことがわかります。つまり、何らかのメールスロット イベントがあった場合は、直接0x140001405にジャンプします。これは、初期化関数のようです。

その関数を調べると、ExAllocatePoolWithTagを使用してメモリ アドレスを割り当て、その結果を0x140004020に保存していることがわかります。それは、lpszBufferと名付けられています。

lpszBufferへの相互参照を調べると、それがPostOperation、InitBuffer、0x14000181CおよびUnloadの4つの関数で使用されていることがわかります。0x14000181Cについては少し後で取り上げますが、ここでは、PostOperation関数に戻って、次に何が起こるかを確認します。

InitBuffer呼び出しの後、再びFileObjectのファイル名を確認し、それを“\pan.flag”と比較しています。それが正しい場合は、関数0x14000181Cを呼び出し、DbgPrintを使用してフラグを出力します。つまり、復号化ルーチンが0x14000181Cにあり、lpszBufferにデコードされたフラグが含まれます。

次に、0x14000181Cにある関数を調べてみましょう。関数の名前は、TestFlagに変更してあります。

フラグ テスト関数の分析

関数の最初の部分で、再びFltReadFileを使用して、所定のFileObjectのコンテンツを読み取っていることがわかります。また、それが58バイトを読み取っていることもわかります。最初の4バイトは、“PAN{“と比較されます。

その後、最初のセットのXORデータが判明します。それは、次の4バイトを取得し、0x1A1B1C1DとXOR演算して、結果を0x366C734Aと比較します。では、デコーダの記述を開始しましょう。

今のところ良好です。

次の文字に進みましょう。次のコード セグメントは、KdDebuggerNotPresentの結果に基づいてシフトとXOR演算を実行しています。デバッガが接続されていない場合、関数KdDebuggerNotPresentは1を返します。また、正しいキーを取得するために、順序を逆にする必要があります。

結果はスペース文字(16進数の0x20)です。次の文字に進むことができますが、ここでも、正しい文字を取得するために順序を逆にする必要があります(addの代わりにsubを使用する必要がある、など)。

ここでは、続行して、フラグ全体をゆっくりデコードする必要しかありませんが、0x140001AFCを呼び出すため多少難しくなります。この関数では、異なるオフセットにある文字へアクセスし、奇妙な計算を試みています。

たとえば、0x140001B41では、4番目の文字をロードし、それから0x20を減算して、それに3 [rax+rax*2]を乗算してから、それを0xC3と比較します。オプティマイザーは、ときどきかなりクールなことを実行できます。

関数全体とそのサブ関数を分析すると、以下のデコーダ スクリプトが取得されます。

フラグのテスト

次に、フラグをテストして、すべてが正しいかどうかを確認します。以下の手順を実行する必要があります。

  • コンテンツとして“labyrenth.com”を含む“gsrt.txt”という名前のファイルを作成します。
  • “gsrt.txt”を開いて、FILE_OPENイベントを発生させ、バッファを初期化します。
    • これらの2つの手順の代わりにメールスロット イベントを作成することもできます(CreateMailslot + WriteFileを使用できます)。
  • フラグ“PAN{…}”を含む“pan.flag”という名前のファイルを作成します。
  • “pan.flag”を開いて、FILE_OPENイベントを発生させ、分析関数を呼び出します。
  • DbgViewに接続し、カーネル デバッグの出力を確認します。

対応するジョブを実行するバッチ ファイルを作成できるため、ファイルを作成する解決策の方がより簡単です。

revloader.exeを実行し、ここでの解決策のバッチ ファイルを実行して、何が起こるか確認します。

最後のスクリーンショットで、フラグが正しく、それが出力されたことを確認できます。

Windows 9課題: ある信奉者がそのcrypterを記述したのだと思う

課題作成者: Esmid Idrizovic @xedi25

初期分析

最後の課題を実行すると、30文字を入力する必要があるダイアログが表示されます。アイコンが異なっており、それがデフォルトのDelphi 7実行可能ファイル アイコンであることがわかります。任意のテキストを入力し、Enterキーを押すと、ラベルが“Wrong”に変わります。つまり、正しいフラグを入力して、Enterキーを押し、それを検証する必要があります。

PEビューアーでそのサンプルを開くと、それが未知のEXE crypterを使用し、コードを非表示にしていることがわかります(実際には、そのcrypterは、Delphiで記述されたMorphine 2.7です)。インポート テーブルを見ると、LoadLibraryA、GetProcAddressおよびPEセクションのみが存在します。

探す必要のあるものがわかれば、この課題はかなり短時間で解決できます。有効なフラグを確認するルーチンを特定します。ステータスを“Wrong”に設定する関数の分析から始めますが、それを実行するには、事前に、実行可能ファイルをダンプしてIDA Proでロードできるようにしておく必要があります。

実行可能ファイルのダンプ

Scylla x86を使用してプロセスをダンプできるため、課題に取り掛かり、Scyllaを実行してみましょう。“DelphiChallenge.exe”を選択して、“IAT Autosearch (IAT自動検索)”をクリックします。“Get Imports (インポートの取得)”をクリックした後、Scyllaでのインポートの検索が可能になります。すでに、次のスクリーンショットで正しいインポートを確認できます。

Scylla内で“Dump”関数と“Fix dump”関数を使用して、PEダンプを保存できます。ダンプしたファイルを実行することはできませんが、IDA Proでの静的分析には十分です。ファイルを正しくダンプできますが、いくつかの追加手順が必要です(「追加」部分を参照)。

静的分析

次にIDA Proでファイルを開き、Shift+F12を使用して“strings window (文字列ウィンドウ)”に直接移動します。文字列ウィンドウで“Wrong”を検索すると、以下の5つのエントリが表示されます。

相互参照を使用して最初の文字列を辿ると、それがsub_4C17BC内で参照されており、さらにもう1つの興味深い文字列があることがわかります。

byte_4C4C5Cが0以外の場合は、“Correct!! You are so g00d”と表示されます。byte_4C4C5Cを辿ると、それがオフセット0x004C127Fで1に設定され、関数がオフセット0x004C0E1Cで始まることがわかります。この関数は、私たちのフラグのテスト ルーチンに似ています。

かなり早く、正しいフラグを確認するルーチンが特定されました。次に、その関数を分析する必要があります。これは、Windows種目の課題8にかなり似ています。フラグが“PAN{“で始まる必要があることはわかっています。関数が最初にフラグをチェックしていることを確認できます。

Windows課題8と同様に、再度、関数を辿り、計算を逆にする必要があります。最終的なデコーダのスクリプトは以下のようになります。

これで、そのフラグが正しいかどうかをテストして検証できます。

追加: 手動での展開

プロセスを正しくダンプするために、OllyDbgとOllyDumpを使用できます。元の入口点(OEP)を特定するために、“現在のESPにハードウェア ブレークポイントを設定する”手法を使用できます。その場合、最初の命令を実行して、ダンプ ウィンドウに移動し、ESPにジャンプする必要があります。その後、現在のアドレスに“Hardware, on access(ハードウェア、アクセス時)”ブレークポイントを設定できます。

F9キーを数回押すと、元の入口点に到達します。次に、いくつかの計算を行い、それを新しいファイルにダンプする必要があります。

実際のOEPを計算する場合は、実行可能ファイルがロードされているアドレスを知っている必要があります。

次に、これらのオフセットを減算するだけで、OllyDumpの正しいオフセットが取得されます。

では、そのデータでOllyDumpを満たし、ファイルを“dumped.exe”としてダンプしてみましょう。

しかし、展開したサンプルの実行を試しても、まだ何かが間違っています。

おそらく、問題の原因はインポート テーブルですが、Scyllaを使用してそれを自分たち用に修正できます。そのために、DelphiChallenge.exeを再度実行して(新しいプロセス)、インポート テーブルを検索する必要があります。また、正しい入口点とイメージ ベースも入力する必要があります。

これで、“Fix dump(ダンプの修正)”を使用して、dumped.exe実行可能ファイルを修正できます。

Scyllaによって新しいインポート テーブルが作成され、これで、展開された実行可能ファイルを実行できます。やりました!

目次

Enlarged Image