Cobalt Strike Reflective Loaderの定義

路上でノートPCを使用しているハッカーとして描かれた黒いパーカーを着た男性

セキュリティー・ソリューションの次世代AIや機械学習コンポーネントによって行動ベースの検知機能が強化され続けていますが、その中核となる方法としては依然としてシグネチャー・ベースの検知に依存しています。Cobalt Strikeは、脅威アクターやレッド・チームの両方に利用されている人気のレッド・チームの指揮統制(C2)フレームワークであり、セキュリティソリューションによるシグネチャー検出が依然として頻繁に行われています。

過去におけるCobalt Strikesの運用利用を進化させるために、IBM® X-Force Red Adversary Simulationチームは、Cobalt Strikeを内部ツールでカスタマイズするために、多大な研究を行いました。Cobalt Strike固有の内部ツールには、「InlineExecute- Assembly」、「CredBandit」、「BokuLoader」など、公開バージョンがあるものもあります。過去2年間では、Cobalt Strikeの過剰なシグネチャー検出があったことを踏まえ、IBMでは、その使用をあまり洗練されていない脅威アクターのシミュレーションに限定し、より高度なレッド・チーム演習を実行する際には、他のサード・パーティーや社内のC2を活用しています。

研究開発の取り組みを通じて、以下のような高度なレッド・チーム演習の運用上の成功が見られました。

  • カスタム内部ツール。
  • カスタムの内部ローダー。
  • カスタム内部C2フレームワーク。
  • 代替のサード・パーティC2フレームワークの機能とステルスを拡大するための投資を継続します。

ただし、Cobalt Strikeの偽造コピーを利用する脅威アクターは依然として多数存在しており、これらの脅威アクターをシミュレートできることは依然として重要です。研究開発に前向きなレッド・チームの場合、これらの敵対者をシミュレートしながらCobalt Strikeでの運用が成功する可能性があります。さらに、Cobalt Strikeは優れた学習ツールであり、初心者はレッド・チーム・トレーニング・コースを通じてC2フレームワークの実践的な経験を積むことができます。

C2機能の拡張を続ける中で、特にカスタムReflective Loaderを開発することによって、過去にCobalt Strikeフレームワークをどのように構築してきたかについての洞察を共有しています。また、防御側が、Cobalt Strikeがどのようにしてより堅牢な検出を作成するかを理解することも目的としています。

Reflective Loaderによるフレームワーク上の構築

このブログ投稿は、Cobalt Strike Reflective Loader開発の基本について取り上げる手引きの役割を果たしたシリーズの最初のものです。このシリーズでは、この基盤をベースにして、この投稿を参照していきます。

このシリーズの終わりまでに、Cobalt Strikeの既存の主要な機能と統合し、さらには現在ツールにはない高度なテクニックでそれらを強化する Reflective Loaderを作成することを目指しています。今後の記事では、特定の主要な機能の開発と、それらをCobalt Strikeの Reflective Loaderに実装する方法について深く掘り下げていきます。

まずこの投稿では以下について説明します。

  • Windows DLLローダーを使用してディスクからC2インプラントをロードする際の問題。
  • Cobalt Strikeのリフレクティブ・ローディング・プロセスの概念と仕組み。
  • 効果的なReflective Loaderを構築するために必要な設計要件。
  • リフレクティブ・ローディング・プロセスに含まれるフェーズ。

攻撃的なセキュリティー・ツール開発者の視点からCobalt Strikeのインフラストラクチャー・ロードのリフレクティブ・ローディングを検証する中で、検知と回避の機会を明らかにしていきます。一部の開発側面は省略または簡素化されます。そのため、既存の Reflective Loader・プロジェクトをデバッグするか、最初から再構築するか、トレーニングを確保することによって、ギャップを埋めることをお勧めします。

ビーコンDLLの読み込み中

Cobalt Strike C2インプラント(Beaconと呼ばれる)はWindows ダイナミック・リンク・ライブラリ(DLL)であり、Cobalt Strikeで独自のDLLローダーを使用するモジュール機能は、ユーザー定義Reflective Loader(UDRL)と呼ばれます。

組み込みのWindows DLLローダー

通常、組み込みのWindows DLLローダーは、プロセスの仮想メモリー空間にDLLをロードする役割を担います。Windows DLLローダーは主にユーザー空間内に存在しますが、ディスクからDLLをマッピングする際にはカーネル空間を横断します。

Windows DLLローダーを使用すると、敵対者シミュレーションではいくつかの欠点が生じます。

  • 未加工DLLはファイル・システム上に存在する必要があります。
  • 未加工DLLは難読化されない必要があります。
  • カーネル・イメージ・ロード・イベントは、Windows DLLローダーによってトリガーされます。

したがって、ビーコンDLLのロードにWindows DLLローダーを使用することは理想的なソリューションではありません。これらの課題を克服するために、 Reflective Loaderを使用してメモリーからビーコンDLLをロードします。

リフレクティブ・ローディングが回避する3つの主な検知ポイントは次のとおりです。

  1. ファイル・システム上の署名されたマルウェアを回避します。
  2. セキュリティー・ソリューションで監視できるカーネル・イメージの読み込みイベントを回避します。
  3. プロセス環境ブロック(PEB)にリストされているC2インプラントDLLを回避します。

Reflective LoaderとWindows DLLローダーの比較

リフレクティブ・ローディングは、ファイル・システムから未加工DLLをロードするのではなく、メモリーから直接ロードするだけと考えることができます。

リフレクティブ・ローディングと組み込みのWindows DLLローダーはどちらも、Rawファイル形式からプロセスの仮想メモリー空間にDLLをロードするという同じ目的を果たします。ただし、リフレクティブ・ローディングには、DLLファイルがファイル システム上に存在する必要がないという点で、Windows DLLローダーに比べて重要な利点があります。C2インプラントDLLは、プロセスのメモリー内の暗号化およびエンコーディング層に隠すことができるため、このメモリー内ロードでは無制限の数のチェーン・ロード・フェーズが可能になります。

Rawファイル形式と仮想アドレス形式の比較

DLLをロードするときに理解しておくべき重要な概念は、DLLがディスク上とメモリー上で異なるフォーマットになることを知っておくことです。Rawファイル形式のDLLと仮想アドレス形式のDLLの主な違いは次のとおりです。

Rawファイル形式:

  • ファイル・システムに存在するDLLの形式。
  • DLLのセクションはしっかりと詰め込まれています。
  • オフセットは、ディスク上に存在する未加工DLLファイルの開始に基づいています。
  • この形式では、必要なメモリー容量が少なくなります。

仮想アドレス形式:

  • プロセスの仮想メモリー空間に存在するDLLの形式。
  • セクションは間隔を確保されています。
  • オフセットはリレーショナル仮想アドレス(RVA)です。
  • DLLおよびその他のモジュールは、プロセス内で実行される場合、RVA経由で場所を決定します。
  • このフォーマットは、より多くのメモリー容量を消費します。

Rawビーコンと仮想ビーコン

Aleksandra Doniec氏によるPE-BearツールでHTTPビーコンDLLを調べると、DLLの各セクションにおけるRawアドレスと仮想アドレスの違いがわかります:

ビーコンDLLの各セクションのRawアドレスと仮想アドレスをリストしたテーブル。

ビーコンDLLの各セクションのRawアドレスと仮想アドレスをリストしたテーブル。

このHTTP/SビーコンDLLは0x52000  バイト(327KB )プロセスの仮想メモリー空間にロードされた場合、以前と比較して0x44000 バイト(272KB )、ファイル・システム上に存在するサイズと同じです。このサイズの違いは、セクションがRawファイル形式でタイトに詰められているのとは対照的に、仮想アドレス形式で間隔が確保されているためです。

PE-Bearは、ビーコンDLLがRawファイル形式ではなく仮想アドレス形式で存在するため、ビーコンDLLを視覚的に表現します。

Raw形式と仮想形式におけるビーコンDLLの視覚的表現

Raw形式(左)と仮想形式(右)におけるビーコンDLLの視覚的表現

Windows DLLローダーによるBeaconのロード

敵対者シミュレーション中に実行できる最も賢明な動きではありませんが、ディスクに難読化のないRawビーコンDLLをディスクに落とし込み、Windows DLLローダーでロードすることは、ビーコンと DLLロードの両方をわかりやすく説明する優れた方法です。本質的に、ビーコンは単なるDLLです。Windows DLLローダーと Reflective Loaderは、DLLをプロセスにロードするだけです。

Windows DLLローダーでビーコンDLLをロードするには、次の手順を実行します。

  1. 難読化なしのRawビーコンDLLを生成します。
  2. 以下のようなプログラムを作成します。
    1. を使用しますLoadLibrary ディスクからビーコンDLLをロードするためのAPI
    2. 仮想ビーコンDLLのエントリー・ポイントを呼び出すことで、ビーコンを実行します。
  3. 実行可能プログラムとビーコンDLLを同じフォルダーに配置します。
  4. プログラムを実行します。

難読化されていない生のビーコンDLLを生成する

まず、Windows DLLローダーでビーコンDLLがロード不可能になるように、Malleable PEオプションをすべて無効にします。これを実行するには、Malleable C2プロファイルを変更し、ステージ ブロックにあるMalleable PE回避オプションを無効にします。

Malleable C2プロファイルステージブロックは、Cobalt Strikeの主要な機能を無効にするために変更されました。

Malleable C2プロファイルステージブロックは、Cobalt Strikeの主要な機能を無効にするために変更されました。

プロファイルを変更した後、Cobalt Strike Teamサーバーを再起動し、no_evasion.profile  プロファイルを引数として使用します。

ブログ記事用に作成されたスクリーンショット

Cobalt Strikeクライアントを備えたチーム・サーバーに接続します。次に作成するのがWindows Stageless Payload 出力オプションをRawに設定し、リスナーをに設定した場合https 。ペイロードを次のように保存します。beacon.dll .

Cobalt Strike Clientから「Rawステージレス」ビーコンDLLを作成するスクリーンショット

Cobalt Strike Clientから「Rawステージレス」ビーコンDLLを作成するスクリーンショット

ビーコンDLLローダープログラムを作成する

以下のコードを使用して、次のコードを使用してCプログラムを作成しloadBeaconDLL.c  そして、以下をコンパイルします。

Windows DLLローダーを使用してディスクからビーコンDLLをロードするためのWindows Cコード。

Windows DLLローダーを使用してディスクからビーコンDLLをロードするためのWindows Cコード。

私たちはKernel32.LoadLibraryA ディスクからRawビーコンDLLをロードするためのAPIを使用します。このAPIは、組み込みのWindows DLLローダーを呼び出し、ビーコンDLLをディスクからホスト・プロセスの仮想メモリー空間にロードします。

読み込みプロセスの一環として、Windows DLLローダーは、エントリー・ポイントを呼び出すことでビーコンDLLを初期化します。DLL_PROCESS_ATTACH (1) 引数として。

Windows DLLローダーがビーコンDLLをプロセスの仮想メモリー空間にロードして初期化した後、仮想ビーコンDLLのエントリー・ポイントを引数で再度呼び出す必要があります。0x4.

私たちのプログラムは、仮想ビーコンDLLを実行するために仮想ビーコンDLLのエントリー・ポイントを知る必要があります。これは、エントリー・ポイントの相対的な仮想アドレス(RVA)の仮想ビーコンDLLヘッダーを解析することによってプログラム内で動的に行うことも、それが何であるかをすばやく確認して値をハードコードすることもできます。

概念実証のために、ビーコンDLLのエントリー・ポイントRVAを手動で検出し、プログラムにハードコーディングします。PE-Bearを使用することで、RVAからビーコンのエントリー・ポイントまで0x1D840 :

PE-Bearを使用したビーコンDLLエントリー・ポイントRVAの検索のスクリーンショット

PE-Bearを使用したビーコンDLLエントリー・ポイントRVAの検索のスクリーンショット

そのLoadLibraryA APIは仮想ビーコンDLLのアドレスを返します。これをエントリー・ポイントRVAに追加するだけで、エントリー・ポイントを決定できます。

コードの準備が整ったので、CプログラムをWindows実行可能ファイルにコンパイルします。

プログラムのコンパイルに使用されるコマンド。

プログラムのコンパイルに使用されるコマンド。

プログラムとビーコンDLLをファイル・システムに配置する

ビーコンDLLと実行可能なビーコン・ローダー・プログラムを同じディレクトリーに配置することで、Windows DLLローダーはロード・ルーチンを実行するときにDLLを検出できるようになります。

両方をbeacon.dll およびloadBeaconDLL.exe 同じディレクトリー内のファイル・システム上に配置します。

ビーコンDLLとローダー・プログラムは同じディレクトリーに配置されます。

ビーコンDLLとローダー・プログラムは同じディレクトリーに配置されます。

プログラムを実行する

Windowsデスクトップで、loadBeaconDLL.exeをダブルクリックします。チーム・サーバーへのアクティブなビーコン接続を確立します。

Windows DLLローダーを使用してロードされたビーコンDLLからC2チーム・サーバーへの接続に成功しました。

Windows DLLローダーを使用してロードされたビーコンDLLからC2チーム・サーバーへの接続に成功しました。

Cobalt strikeリフレクティブ・ローディング

Cobalt Strikeは、Stephen FewerによるReflective Loaderプロジェクトの修正バージョンを使用します。この伝説的なインメモリーDLLローダーは10年以上の歴史があり、Metasploitやその他の著名な攻撃的セキュリティー・ツールで使用されています。

UDRL使用に関する考慮事項

長年にわたり、Cobalt Strike Reflective Loaderは、Cobalt Strikeが提供するすべてのMalleable PE回避機能を処理できるように強化されてきました。カスタムUser-Defined Reflective Loader(UDRL)を使用する主な欠点は、Malleable PE回避機能がすぐにサポートされる場合とされない場合があることです。

一部の回避機能は、UDRLを使用する場合に完全に実装され、ビーコン・ペイロードの作成時にCobalt Strikes Malleable PEエンジンによってビーコンDLLにパッチ適用されます。ただし、現在は機能のうちobfuscate のようなものはUDRLする必要がありますが、sleepmask cleanup のようなものは適切なUDRL統合によりビーコンで処理できます。

リフレクティブ・ローディング・メソッド

オリジナル反射型ローダー方式

元のReflective Loaderプロジェクトでは、ReflectiveLoader をDLLプロジェクトに組み込み、C2インクリプションDLL内にエクスポートしました。

その後、別のプロジェクトが以下を担当します。

  1. 仮想アドレスを発見することでReflectiveLoader
  2. エクスポートを実行するとReflectiveLoader 、読み込まれたDLLへのエントリー・ポイントを返します。
  3. 反射的にロードされたDLLのエントリー・ポイントを呼び出します。
元の Reflective Loaderの図。仮想メモリーにDLLをロードしています。

元の Reflective Loaderの図。仮想メモリーにDLLをロードしています。

プリペンド反射型ローダー方式

別の方法では、 Reflective LoaderをDLLに事前にデプロイします。これにより、管理されていないDLLを読み込むことができ、ソース コードからDLLをコンパイルする必要がなくなります。これは、あらゆるPEファイル(EXEまたはDLL)をロードできる堅牢なリフレクティブ・ローディング・メソッドです。

DLLを仮想メモリーにロードしている Reflective Loaderの図

DLLを仮想メモリーにロードしている Reflective Loaderの図

Cobalt Strikeの反射型ローダー方式

Cobalt Strikeのリフレクティブ・ローディングの実装では、上記の2つの方法を組み合わせて使用しています。このリフレクティブ・ローディング・メソッドは、MetasploitのMeterpreterが反射読み込みをどのように行うかについて知識を持つ人にとってはよく知られたものかもしれません。

元の Reflective Loaderの方法のように、 ReflectiveLoader 関数は元のビーコンDLL内でコンパイルされ、エクスポートされます。オペレーターがCobalt Strikeクライアントからビーコン・ペイロードを生成すると、Cobalt StrikeのMalleable PEエンジンはRawビーコンDLLにパッチを適用し、使用するMalleable PEのオプションを Reflective Loaderに通知します。BeaconのDOSヘッダーは、パッチが当てられReflectiveLoader ハードコードされたオフセットでエクスポートを呼び出します。ビーコンのDOSヘッダーのパッチが適用された最初のバイトReflectiveLoader このブログでは、これを「Reflective Loaderスタブ呼び出し」と呼びます。

UDRLがCobalt Strikeに読み込まれ、オペレーターがCobalt Strikeクライアントからビーコンペイロードを生成すると、Cobalt StrikeのMalleable PEエンジンは、未加工のファイル・オフセット位置にReflective LoaderのシェルコードにパッチをあてますReflectiveLoader  エクスポートします。

Malleable PEエンジンがRawビーコンDLLのパッチ適用を完了すると、RawビーコンDLLが実行可能なシェルコードのような形式でオペレーターに与えられます。

ビーコンDLLを仮想メモリーにロードしているCobalt Strike Reflective Loaderの図。

ビーコンDLLを仮想メモリーにロードしているCobalt Strike Reflective Loaderの図。

ビーコンの呼び出し反射型ローダー・スタブ

PE-Bearディスアセンブラーの最初のバイトを見ると、ビーコンDLL自体が実行可能であることがわかります。

Reflective Loaderスタブ呼び出しが実行可能なアセンブリ・オペレーション・コードとして表示されます。

Reflective Loaderスタブ呼び出しが実行可能なアセンブリ・オペレーション・コードとして表示されます。

最初のバイトはMZAR Cobalt Strikes C2プロファイルのMalleable PEオプションを使用してカスタマイズできます。これらのバイトは実行可能であり、結果はノーオペレーションnop )を挿入します。

オプションでプリペアドを実行した後nops マジック・バイトやReflective Loaderのスタブなどです

  • スタックフレームを作成します。
  • RIP相対アドレスを使用して、RawビーコンDLLのベースのアドレスを決定します。
  • 生成AIをReflectiveLoader 既知の輸出0x16E3C RAWファイルのオフセット。
  • ロードされたビーコンDLLのエントリー・ポイントを呼び出します。

Rawファイルのオフセットが確認されていることを確認しましたReflectiveLoader エクスポートは0x16E3C ビーコンDLLのエクスポート・ディレクトリーを見ると:

PE-Bearを使用して、ReflectiveLoaderエクスポートのRawファイル・オフセットを決定する製品の画面。

PE-Bearを使用して、ReflectiveLoaderエクスポートのRawファイル・オフセットを決定する製品の画面。

エクスポート・ディレクトリー内に存在するため、ReflectiveLoader エクスポートはRVA形式で、仮想状態のビーコンDLLを参照します。以降、ReflectiveLoader エクスポート機能が実行可能であることを確認します.text ビーコンDLLのセクション。

Rawファイルを特定することで ReflectiveLoader 輸出していくためにはその違いを知る必要があります.text 仮想アドレスとRawアドレスを使用できます。既知の差があれば単純にReflectiveLoader エクスポートのRVAを使用してReflectiveLoader エクスポートのRAWファイルのオフセットを発見します。

仮想及びRawアドレス.text セクションはビーコンDLLのセクション・ヘッダー内にリストされます。

.textのRawアドレスと仮想アドレスビーコンDLLのセクション。

.textのRawアドレスと仮想アドレスビーコンDLLのセクション。

この2つの違いは0xC00 バイト。データをのためのReflectiveLoader  エクスポートのRVA0x17A3C この差により、Rawファイルのオフセットは次のことがわかります。0x16E3C .

PE-Bearで右クリックすれば確認できますReflectiveLoader エクスポートの関数RVAとクリックFollow RVA:17A3C 。上記ウィジェットの六角形のビューアーは、ReflectiveLoader Rawファイルのオフセットでエクスポートを行うこともできます。

Cobalt Strikeのリフレクティブ・ローディング・プロセス・フローを要約すると、次のとおりです。

  • スレッドは、RawビーコンDLLを実行します。
  • Reflective Loaderスタブは、ReflectiveLoader 既知のRawファイル・オフセットでエクスポートを呼び出します。
  • Reflective Loaderは、RawビーコンDLLをホスト・プロセスの仮想メモリーにロードします。
  • 読み込み後に、 Reflective Loaderは仮想ビーコンDLLのエントリー・ポイントをReflective Loaderスタブ呼び出しに戻します。
  • Reflective Loaderスタブ呼び出しは、仮想ビーコンDLLのエントリー・ポイントを呼び出します。
Cobalt StrikeがビーコンDLのリフレクティブ・ローディングを実行する方法の主要なフェーズを示す図

Cobalt StrikeがビーコンDLのリフレクティブ・ローディングを実行する方法の主要なフェーズを示す図

Reflective Loaderの設計要件

位置に依存しないコード

Reflective LoaderはビーコンDLLがロードされる前に実行されるため、 Reflective Loaderコードは純粋なシェルコードである必要があります。

複雑なシェルコードを作成する最も簡単な方法は、外部依存関係なしでC言語で記述することです。次に、Cファイルがオブジェクト・ファイルにコンパイルされます。オブジェクト・ファイルのセクションにはすべてを含める必要がありますtext最後に、.text Reflective Loaderのシェルコードを取得するセクション。

Cobalt StrikeがUDRLを挿入する方法

Cobalt StrikeのMalleable PEエンジンは、 Reflective Loaderのオブジェクトファイルからシェルコードを取得し、エクスポートのRawファイル・オフセット位置で生のビーコンDLLにパッチを当てる作業を処理しますReflectiveLoader 。これは、以下に示すように、UDRL Aggressorスクリプトで行われます。

Cobalt Strikeを利用してRawビーコンDLLに Reflective Loaderシェルコードを書き込むためのAggressorスクリプト。

Cobalt Strikeを利用してRawビーコンDLLに Reflective Loaderシェルコードを書き込むためのAggressorスクリプト。

UDRL Aggressorスクリプトは、次の手順を実行することで、Cobalt Strikeを Reflective Loaderシェルコードに書き込みます。

  • 私たちは$handle UDRLオブジェクト・ファイルにopenf 集めています。
  • ファイルで$handle バイト・ストリームを読み取って$data バイト配列変数。
  • その上でファイルを閉じます$handle 大規模言語モデルではclosef 集めています。
  • 内蔵の
    <a href="https://hstechdocs.helpsystems.com/manuals/cobaltstrike/current/userguide/content/topics_aggressor-scripts/as-resources_functions.htm#extract_reflective_loader">extract_reflective_loader</a>
    
    Cobalt Strike Aggressor関数はUDRLオブジェクト・ファイルを$data バイト・アレイから解析し、.text UDRLオブジェクト・ファイルからセクションを見つけて、.textセクションを抽出し以下に保存します$loader バイト配列変数。
  • 内蔵の
    <a href="https://hstechdocs.helpsystems.com/manuals/cobaltstrike/current/userguide/content/topics_aggressor-scripts/as-resources_functions.htm#setup_reflective_loader">setup_reflective_loader</a>
    
    Cobalt Strike Aggressor機能は、Malleable PEエンジンを使用して、Rawファイル・オフセットを検出します。ReflectiveLoader エクスポートし、$loader バイト配列変数。
  • 最後に、変更したビーコンDLLを Cobalt Strikeに返し、クライアントからファイルを保存します。

リフレクティブ・ロード・フェーズ

Cobalt Strikeは、リフレクティブローダーのオブジェクト・ファイルから.textセクションを抽出する作業、Reflective Loaderのシェルコードをパッチする作業、そしてビーコンDLLヘッダーにある「call reflective loader stub」を使ってリフレクティブローダーを呼び出す作業を、我々の代わりに済ませてくれます。

これらは、ビーコンを反射的に読み込むために開発しなければならないフェーズです。

  1. Raw Beacon DLLを探す
  2. Beacon DLLヘッダーの解析
  3. 仮想Beacon DLL用のメモリーの割り当て
  4. 仮想メモリー領域へのロード・セクション
  5. DLL依存関係をロードする
  6. Resolveインポート・アドレス・テーブル
  7. 再配置の解決
  8. Beaconを実行する

フェーズ1:Raw Beacon DLLベース・アドレスを見つける

メモリー内のRawビーコンDLLのアドレスを見つけるために使用できる方法はいくつかあります。次のような方法があります。

  • MZ&PEヘッダーを逆算してハンティングする
  • Eggの逆算ハンティング
  • Reflective Loaderの呼び出し元スタブからのRawビーコンDLLベース・アドレスの取得

メモリ内で自らの位置を特定する

逆算してハンティングする方法を使用する場合は、まずスレッドの指示ポインターの現在のアドレスを取得する必要があります(RIP )。このシンプルなトリックを使用してgetRip :

  1. UDRLでは次のような関数を作成しますgetRip .
  2. 私たちはgetRip に続くアドレスをプッシュしますcall getRip 」をスタックの上部に配置します。これが返送アドレスです。
  3. これによりgetRip  関数に、スタックの上部から呼び出し元の戻りアドレスをコピーするだけです
  4. x64 Windows Cコーディングでは、関数は値を返すことができます。この返された値は、RAX レジスタを通じて呼び出し元に返されます。呼び出し元の返信アドレスをRAX 登録に移動した場合は、呼び出し元のアドレスを呼び出し元に返しています
RDIレジスタからRawビーコンDLLのアドレスを取得するためのIntel x64アセンブリ・コード。

RDIレジスタからRawビーコンDLLのアドレスを取得するためのIntel x64アセンブリ・コード。

MZとPEヘッダーを遡って探す

元の Reflective Loaderプロジェクトは、MZおよびPEヘッダーを逆方向にハンティングします。これらのヘッダーは検知ポイントになっています。このCobalt Strikeを克服するために、magic_mz およびmagic_pe  Malleable PE回避機能を備えています。

Cobalt Strikeの文書には、magic_mz オプション:

  • 「Beaconの反射DLLの最初のバイト(MZヘッダーを含む)をオーバーライドします。有効な指示が必要です。CPUの状態を変更する命令に従い、変更を元に戻す命令に従ってください。」

設定すると、MZ--  生のファイル・オフセットのバイト数0x00 およびPE00 Rawファイルのオフセットにおけるバイト数0x80 Reflective Loaderで知られています。これらは、Malleable PEエンジンによってビーコンDLLにパッチが適用されます。

これらのバイトはある程度一意である必要があり、そうでない場合は Reflective Loaderがそれらを見つけることができません。さらに、MZヘッダーのバイトはオペレーションで実行可能でなければなりません。次のような値にはなりません0x00 さもないとビーコンがクラッシュする可能性があります。ここは潜在的な検出ポイントとなる可能性があります。

エッグを遡って探す

この潜在的な検知ポイントを発見した後、私はRawビーコンDLLベース・アドレスを見つけるための別の、同様の方法を開発しました。この方法では、エグゼクティブ・ハンターを使用したエグゼクティブ・ハンターがRIP これは、既知の時点でユニークな64ビットeggの2つの繰り返しインスタンスを探索します。beacon.dll+0x50  生のファイルのオフセット。

このアドレスbeacon.dll+0x50 が選ばれたのは、「このプログラムはDOSモードでは実行できません」バナーの位置のためであり、ビーコンを反射的に読み込む際には必要ありません。

Java Malleable PEエンジンには簡単にアクセスできないため、BokuLoader.cna UDRL Aggressorスクリプトを使用して、0xB0C0ACDC eggをビーコンに書き込みます。以下のコードは、RawビーコンDLLにエグゼクティブを含めるように変更する方法を示しています。

RawビーコンDLLにeggを書き込み、Cobalt Strikeスクリプト・コンソールに変更を表示するAggressorスクリプト。

RawビーコンDLLにeggを書き込み、Cobalt Strikeスクリプト・コンソールに変更を表示するAggressorスクリプト。

UDRLコードは、UDRLスクリプトによってRawビーコンDLLに書き込まれるegg値を知っている必要があります。以下のコードに示すように、既知eggでエグゼクティブ・ハンターはeggの2つのインスタンスを逆方向に検索します。

64ビットeggの2つのインスタンスを逆方向に検索するeggハンターのIntel x64アセンブリ・コード。

64ビットeggの2つのインスタンスを逆方向に検索するeggハンターのIntel x64アセンブリ・コード。

  • UDRL AggressorスクリプトとUDRL Cコードの両方は、さまざまなeggを使用するように変更できます。

MZヘッダーとPEヘッダーが使用されなくなったため、UDRL Aggressorスクリプトでそれらを削除できます。

MZ、PE、およびRawビーコンDLLヘッダーにある DOS バナーの未使用バイトをマスクするAggressorスクリプト。

MZ、PE、およびRawビーコンDLLヘッダーにある DOS バナーの未使用バイトをマスクするAggressorスクリプト。

Call Reflective Loaderスタブから生のBeacon DLLの所在地を取得する

また、RawビーコンDLLベースのアドレスを発見するために、Cobalt Strike固有の方法がもう1つあります。上で見たように、Reflective Loaderスタブ呼び出しの最初のバイトには、RawビーコンDLLのベース・アドレスが保管されます。RDI Reflective Loaderを呼び出す前に登録する必要があります。から逆向きにハンティングするのではなくRIP あるeggの場合、単純に値を次から得ることができます。RDI Reflective Loaderコードの開始時に登録します。

デバッガーでこれをさらに調べるために、ビーコンを生成し、ブレークポイントを前立て(0xCC )、x64dbgでビーコンを開きます。ブレークポイントが前置きされているため、Rawビーコンのベース・アドレスは+1 割り当てられたメモリーです。上記で見たように、Reflective Loaderスタブ呼び出しは、RIP 相対アドレス指定によるRawビーコンDLLのベースアドレスの取得:

Reflective Loaderを呼び出す前に、Reflective Loaderスタブ呼び出しを探索して、RawビーコンDLLのベース・アドレスがRDIレジスタに保存されていることを確認するX64dbgのスクリーンショット。

Reflective Loaderを呼び出す前に、Reflective Loaderスタブ呼び出しを探索して、RawビーコンDLLのベース・アドレスがRDIレジスタに保存されていることを確認するX64dbgのスクリーンショット。

以下は、Reflective Loaderスタブ呼び出しからRawビーコンDLLのベース・アドレスを取得する方法の実行例です。

RDIレジスタからRawビーコンDLLベース・アドレスを取得するためのインライン・アセンブリCコード。

RDIレジスタからRawビーコンDLLベース・アドレスを取得するためのインライン・アセンブリCコード。

フェーズ2:Beacon DLLのヘッダーの解析

RawビーコンDLLのアドレスを使用して、ビーコンをプロセスの仮想アドレス空間にロードするために必要な値を取得できるようになりました。

以下の表は、RawビーコンDLLのヘッダーから必要な値、それらが見つかる場所、およびその種類のリストです

テーブルは、ビーコンDLLのロードに役立つRawビーコンDLLヘッダーからの値をリストします。

テーブルは、ビーコンDLLのロードに役立つRawビーコンDLLヘッダーからの値をリストします。

回避策

ヘッダーのすべての内容がビーコンDLLのロードに必要なわけではありません。必要な値は再パックまたは難読化できます。不要な値は削除するか、ランダム化できます。

フェーズ3:仮想ビーコン用のメモリーの割り当て

一度SizeOfImagef RawビーコンDLLのヘッダーから解放されるため、このサイズのメモリーを割り当てる必要があります。このメモリー空間には仮想ビーコンDLLが保持されます。

仮想ビーコンDLLのメモリー割り当てには、さまざまな方法を使用できます。また、さまざまな方法で、使用するメモリーの種類も異なります。Cobalt StrikeのデフォルトのReflective Loaderによってサポートされているさまざまなメソッドは次のとおりです。

仮想ビーコンDLLのCobalt Strikeのメモリー割り当てオプションを示す表。

仮想ビーコンDLLのCobalt Strikeのメモリー割り当てオプションを示す表。

回避策

UDRLでは、これをさらに一歩進めることができます。これらの関数のNTAPIバージョンを代わりに使用できます。さらに、NTAPI関数は、直接的または間接的なシステム・コールを介して呼び出すことができ、機能の強化に役立つ場合とそうでない場合があります。

アロケーション・メソッドが以下に設定されている場合、VirtualAlloc Cobalt Strike Malleable C2プロファイルでは、現在BokuLoaderプロジェクトは直接システム・コールを用いています。NtAllocateVirtualMemory 仮想ビーコンDLLにメモリーを割り当てる方法:

直接システム・コールを示すpoLoaderプロジェクトのコード・サンプルは、仮想ビーコンDLLにメモリーを割り当てるために使用されます。

直接システム・コールを示すpoLoaderプロジェクトのコード・サンプルは、仮想ビーコンDLLにメモリーを割り当てるために使用されます。

  • システム・コール番号は、HellsGateメソッドを使用して検出されます。
  • システム・コール・スタブにユーザーランドフックが存在する場合は、HaloGateメソッドが使用されます。

以下の画像は、HellsGateおよびHalosGateメソッドを使用してシステム・コール番号を決定するコード例を示しています。

BokuLoaderプロジェクトのコード・サンプルは、プロセスからシステム・コールがどのように検出されるかを示しています。

BokuLoaderプロジェクトのコード・サンプルは、プロセスからシステム・コールがどのように検出されるかを示しています。

フェーズ4:仮想メモリー空間へのセクションのロード

仮想ビーコンDLL用のメモリーを割り当てたので、ビーコンのセクションを、RawビーコンDLLに存在するRawファイル・オフセットから、相対仮想オフセットで割り当てられたメモリーにコピーする必要があります。

メモリを次のように割り当てるとREADWRITE の所在地を追跡する必要があります.text セクションとそのサイズ。仮想ビーコンDLLのエントリ・ポイントを呼び出す前に、のメモリ保護を変更する必要があります.text セクションを実行可能にします。

に対するメモリの割り当てREADWRITE_EXECUTE によって反射型ローディングプロセスが容易になりますが、セキュリティー・ソリューションによる検知の可能性も増えます。

以下は、BootLoaderプロジェクトの簡略化されたコード例であり、これを示しています

RawビーコンDLLから仮想ビーコンDLLにコピーされたセクションを示すBookLoaderプロジェクトのコード・サンプル。

RawビーコンDLLから仮想ビーコンDLLにコピーされたセクションを示すBookLoaderプロジェクトのコード・サンプル。

回避策

ローディング・セクションに関するいくつかの主要な機能には次のようなものがあります。

  • ビーコンヘッダーを仮想ビーコンDLLにコピーしない。
  • ヘッダーが存在する仮想ビーコンDLLのメモリー領域の割り当てを解除する。

パブリックな保護プロジェクトでは、ビーコンDLLのヘッダーはRawビーコンDLLから仮想ビーコンDLLにコピーされない。現在、最初の0x1000  仮想ビーコンDLLのバイトはnullです(0x00‘s )。私のテストでは、ビーコンが仮想メモリーに適切にロードされた後、ビーコンはヘッダーに依存しません。ヘッダーのコピーを避けることはメモリー内のスキャナーの回避に役立つ可能性がありますが、これらのnullバイトは潜在的な検知ポイントになる可能性もあります。

回避できるもう1つの可能性は、UDRL Aggressorスクリプトにセクションを暗号化させることです。このセクションは、UDRLとUDRL Aggressorスクリプトの間で共有された鍵を使用して、UDRLによってメモリー内で復号化できます。

フェーズ5:DLL依存関係の読み込み

x64 HTTP/Sビーコンは、4つのDLLに依存して適切に機能します。これらのDLLが現在プロセスにロードされていない場合は、 Reflective Loaderがそれらをロードする必要があります。

4つのDLLはHTTP/SビーコンDLLのインポート・ディレクトリーにリストされています。

ビーコンDLLのインポート・ディレクトリーからのDLLをリストするPE-Bearのスクリーンショット。

ビーコンDLLのインポート・ディレクトリーからのDLLをリストするPE-Bearのスクリーンショット。

組み込みのCobalt Strike Reflective Loaderは、DLLロードにkernel32.LoadLibraryA APIを使用します。

回避策

DLLのロードは、運用上のセキュリティー上の考慮事項もさまざまですが、さまざまな方法で実行できます。次のような方法があります。

DLLがプロセスにすでに存在する場合は、上記のWindows APIを引き続き使用してDLLベースのアドレスを取得できますが、これにより不要な検知アラートが発生する可能性があります。

あるいは、PEBは以下へのポインターを保持します

<a title="https://learn.microsoft.com/jp-ja/windows/win32/api/winternl/ns-winternl-peb_ldr_data"href="https://learn.microsoft.com/jp-ja/windows/win32/api/winternl/ns-winternl-peb_ldr_data">_PEB_LDR_DATA</a>

構造。内部には、プロセスで読み込まれたすべてのDLLとその関連情報があります(

InMemoryOrderModuleList

)。BokuLoaderはこれを活用してDLL情報を発見し、不要なAPI呼び出しを避けます。

DLLが存在しない場合は、InMemoryOrderModuleList 現在、BokuLoaderは次を使用しますNTDLL.LdrLoadDll 組み込みのWindows DLLローダーを利用してDLLの依存関係をメモリーに読み込むためのAPI。

Reflective Loaderは一般にDLLをプロセスに登録しないため、ネストされたリフレクティブ・ローディングはDLL依存関係のロードに簡単に使用できません。DLLの外部コードは、反射的に読み込まれたDLLを適切に使用できません。DarkLoadLibraryプロジェクトは、カーネルイメージロードイベントをトリガーせずにDLLを適切にメモリーにロードできる可能性があります。

InMemoryOrderModuleTextを実行することで、読み込まれたDLLの基本アドレスを解決する方法を示すサーバー・プロジェクトのコード・サンプル。

InMemoryOrderModuleTextを実行することで、読み込まれたDLLの基本アドレスを解決する方法を示すサーバー・プロジェクトのコード・サンプル。

フェーズ6:インポート・アドレス・テーブルの解決

必要なDLLがプロセスにロードされたら、インポート・ディレクトリーにリストされているAPIを解決する必要があります。その後、APIアドレスを仮想ビーコンDLLのインポート・アドレス・テーブル(IAT)に書き込む必要があります。このようにして、ビーコンは次のようなAPIを呼び出す必要があるときにジャンプすべきアドレスを把握します。WININET.HttpSendRequest

インポート・エントリーは、序数または名前文字列を使用して解決する必要があります。

以下の画像では、Cobalt StrikeビーコンDLLがインポート・エントリーに序数と名前文字列の組み合わせを使用していることがわかります。

序数によって解決する必要があるビーコンDLLの一部のインポート・エントリーを示すPE-Bearのスクリーンショット。

序数によって解決する必要があるビーコンDLLの一部のインポート・エントリーを示すPE-Bearのスクリーンショット。

内蔵のCobalt Strike Reflective LoaderはKernel32.GetProcAddress  インポート・エントリーのアドレスを解決するAPIを使用します。

回避策

APIアドレスを解決するためのいくつかの回避方法は次のとおりです。

  • のカスタム・コード実装GetProcAddress
  • NTDLL.LdrGetProcedureAddress

BokuLoaderはのカスタムコード実装を使用していますGetProcAddress インポート・エントリーのアドレスを解決し、名前文字列と命令の両方を処理します。

このNTDLL.LdrGetProcedureAddress は名前文字列と序数の両方を処理することもできます。Import Entryの返されたアドレスが別のDLLへのフォワーダーである場合、BookLoaderはデフォルトでNTDLL.LdrGetProcedureAddress フォワーダーを解決します

IATを作成する際に、意図したAPIのアドレスではなく、実装したフック関数のアドレスを作成することでフックを実装できます。IATのアドレスが呼び出されたときに期待されるアウトプットがビーコンに返される限り、ビーコンに戻る前に追加のコードを実行できます。今後の投稿とBokuLoaderの公開リリースでは、高度な主要な機能にIATフックを活用する方法を紹介する予定です。

最近のリリースで、公開されているBokuLoaderプロジェクトはobfuscate カスタム実装を備えたCobalt Strike C2プロファイルの主要なPE機能。マスキングキーを変更することでBokuLoader.cna UDRL Aggressorスクリプトでは、独自のシングル・バイトXORキーを選択することで、難読化を改善できます。

運用上のセキュリティーに関しては、パターン・マッチング・エンジンがシングル・バイトXORマスクを総当たり攻撃で実行できることを知っておくことが重要です。今後の投稿では、Cobalt Strikes Aggressorスクリプト機能を使用して独自のMalleable PEエンジンを作成し、ビーコンを難読化してパターン・マッチングを克服する方法を紹介します。

フェーズ7:再配置の解決

ビーコンDLLには、実行前に解決して仮想ビーコンDLLの基本再配置テーブルに書き込む必要がある再配置が多くあります。

PE-Bearでは、デフォルトのビーコンDLLのイメージ・ベース・アドレスがあることが確認できます 0x180000000 :

PE-Bearのスクリーンショットで、ビーコンDLLの画像ベース・アドレスを示しています。

PE-Bearのスクリーンショットで、ビーコンDLLの画像ベース・アドレスを示しています。

再配置の書き込みを開始する前に、仮想ビーコンDLLの基本アドレスとハードコードされた基本アドレスの間の差分を計算する必要があります。

例えば、仮想ビーコンDLLの基本アドレス0x7FFC44FE0000 。仮想ビーコンDLLのベースアドレスからハードコードされたベースアドレスを削除して、ベースアドレス差分を取得します。

基本アドレス差分を取得するスクリーンショット

次に、基本再配置テーブル内の各再配置エントリーの仮想アドレスを決定するために、ハードコードされた再配置エントリー・アドレスに基本アドレス・デルタを追加して、仮想ビーコンDLL 内の再配置を決定します。

以下の画像では、ビーコン再配置エントリーがリトル・エンディアン形式で逆に書かれていることがわかります。

PE-Bearのスクリーンショットには、一部の再配置エントリーがリトルエンディアン形式で存在していることが示されています。

PE-Bearのスクリーンショットには、一部の再配置エントリーがリトルエンディアン形式で存在していることが示されています。

この再配置エントリーのハードコード化されたアドレスは次のとおりです0x1800341C8 .

このアドレスをベース・アドレス・デルタに追加して、仮想ビーコンDLLに存在する再配置の仮想アドレスを取得します。

仮想ビーコンDLL内に存在するリロケーション用の仮想アドレスを取得するため、アドレスをベースアドレスの差分に加算するスクリーンショット。

再配置のエントリーごとに、タイプが次のとおりであることを確認する必要があります。

<a title="https://learn.microsoft.com/jp-ja/windows/win32/debug/pe-format"href="https://learn.microsoft.com/jp-ja/windows/win32/debug/pe-format">IMAGE_REL_BASED_DIR64(0xA)</a>

。これが偽の場合は、再配置の作成をスキップします。

仮想ビーコンDLL内に存在する再配置の仮想アドレスを決定すると、ハードコード化された再配置エントリー・アドレスを保持するメモリー空間にそれを書き込みます。

PEの再配置方法についてもっと知りたい方は、公開されているBokuLoaderプロジェクトのdoRelocations関数コードをご覧ください。このブログ記事を公開する前に、再配置コードをアセンブリから人間が読み取れるCコードに変更しました。これは、この方法の技術的な詳細を知りたい他のユーザーを支援するためです。

フェーズ8:Beaconの実行

ビーコンの実行は、次の3つのステップに分類できます。

  • 仮想ビーコンDLLセクションに正しいメモリー許可が設定されていることを確認します。
  • 仮想ビーコンのDLLを初期化します。
  • 仮想ビーコンDLLのエントリー・ポイントを呼び出します。

仮想ビーコンを実行可能にする

仮想ビーコンDLLに割り当てたメモリーがREADWRITE_EXECUTE である場合、クラッシュすることなく適切に機能させるために、メモリー保護を変更する必要はありません。

仮想ビーコン・メモリーを非実行型(READWRITE )変更する必要があります .text 仮想ビーコンDLLのセクションを実行可能ファイルに変換します。場所と仮想サイズ.text セクションは事前に変数としてUDRLメイン関数内に保存されていなければなりません。

公開されているBokuLoaderプロジェクトでは、メモリー保護の変更は次のシステム・コールを直接呼び出すことで実行されます。NTProtectVirtualMemory 以下のコード例で示されるように、

.textの変更を示すBokuLoaderプロジェクトのコード・サンプルで仮想ビーコンDLLのセクションを実行可能に変更する方法を示しています。

.textの変更を示すBokuLoaderプロジェクトのコード・サンプルで仮想ビーコンDLLのセクションを実行可能に変更する方法を示しています。

 .data  仮想ビーコンDLLのセクションには許可が必要ですREADWRITE 。セクションが書き込み可能でない場合、ビーコンDLLは実行中にクラッシュする可能性があります。

仮想ビーコンDLLを初期化する

仮想ビーコンDLLを適切に実行するには、まず仮想ビーコンDLLのエントリー・ポイントを呼び出して初期化する必要があります。最初の引数は仮想ビーコンDLLのベースアドレスです。2番目の引数はfwdReason 次のように設定する必要がありますDLL_PROCESS_ATTACH (1) .

仮想ビーコンDLLを初期化するBookLoaderプロジェクトのコード・サンプル。

仮想ビーコンDLLを初期化するBookLoaderプロジェクトのコード・サンプル。

仮想ビーコンDLLを実行する

仮想ビーコンDLLを初期化した後、仮想ビーコンのエントリー・ポイントをReflective Loaderスタブ呼び出しに戻すか、fwdReason に設定0x4 .

最初の引数である典型的なDLLとは異なりhinstDLL から

<a href="https://learn.microsoft.com/jp-ja/windows/win32/dlls/dllmain">DLLMAIN</a>

仮想DLLのベースアドレスである場合、ビーコンはRawビーコンDLLのベースアドレスを期待します。これが供給されていない場合、一部のMalleable PE回避機能が失敗する可能性があります。

仮想ビーコンDLLを実行する2つの異なる方法を示すBokuLoaderプロジェクトのコード・サンプル

仮想ビーコンDLLを実行する2つの異なる方法を示すBokuLoaderプロジェクトのコード・サンプル

クロージング考え

ブログ記事が、レッド・チームとブルー・チームの両方がCobalt Strikeとリフレクティブ・ローディング・プロセスをより深く理解する上で役立つことを願っています。リフレクティブ・ローディングを通じて実現できる回避の機会はまだたくさんあります。これらの概念をより深く理解することで、組織はサイバー脅威に対する防御を成功させるための準備を強化できます。

このシリーズの今後の投稿では、UDRLと現在のCobalt Strikeの回避主要な機能の統合、公開されているBokuLoaderにすでに存在する文書化されていない回避主要な機能の掘り下げ、そしてまだ公開されていない高度な主要な機能に焦点を当てます。UDRL開発でCobalt Strikeゲームを高めるための詳細な情報やテクニックを、今後お伝えしてまいりますので、どうぞご期待ください。

オフィスでミーティングをするビジネスチーム

IBMお客様事例

お客様のビジネス課題(顧客満足度の向上、営業力強化、コスト削減、業務改善、セキュリティー強化、システム運用管理の改善、グローバル展開、社会貢献など)を解決した多岐にわたる事例のご紹介です。