
シェルコードのメモリ保護をRW/NoAccessとRXの間で変動させ、その内容を暗号化/復号化する高度なインメモリ回避技術。
シェルコードの内容を周期的に暗号化・復号化し、メモリ保護を RW(または NoAccess)と RX の間で変動させる、もう一つのインメモリ逃避技術の PoC 実装です。
シェルコードが RW または NoAccess のメモリページに存在する場合、Moneta や pe-sieve などのスキャナは、それを追跡してさらなる解析のためにダンプすることができなくなります。
ThreadStackSpoofer をリリースした後、私は以下の README の点についていくつか質問を受けました:
Beacon のメモリページの保護を(RX/RWX から)RW に変更し、スリープ前にその内容を暗号化する(これにより Moneta や pe-sieve などのスキャナを回避できる可能性があります)
それまで、コミュニティはペイロードを暗号化・復号化し、メモリ保護を切り替えて、異常な実行可能領域を探すメモリスキャナを簡単に回避する方法をすでに知っているだろうと思っていました。 しかし質問によってそうではないことが分かったため、私はこの非武器化された PoC をリリースして、もう一つの回避戦略を文書化し、コミュニティが利用できるサンプル実装を提供することにしました。
この PoC は、攻撃的セキュリティコミュニティにはすでに知られている、かなり単純なテクニックのデモンストレーションです(つまり、ここで本当に新しいものを紹介しているわけではありません)。前述の両メモリスキャナを標的にした回避能力を実演するいくつかの商用フレームワークが示すマジックの背後にある秘密を明らかにしたいという思いからです。
以下は、RW に変動させた場合の比較です(別のオプションとして、PAGE_NOACCESS に変動させる方法もあります。後述):

この実装は、私の ThreadStackSpoofer とともに、商用 C2 製品が提供する機能に追いつくためのサンプル実装を Offensive Security コミュニティに提供します。私たちの Red Team ツールでも遜色ないものを実現できるようにするためです。💪
このプログラムは、シェルコードの自己インジェクションを実行します(おおよそ古典的な VirtualAlloc + memcpy + CreateThread を経由します)。
シェルコードが実行されると(この実装は特に Cobalt Strike の Beacon インプラントを対象としています)、Beacon がスリープする瞬間を傍受するために Windows 関数 kernel32!Sleep がフックされます。
フックされた MySleep 関数が呼び出されるたびに、メモリ割り当ての境界を特定し、その保護を RW に変更して、そこに保存されているすべてのバイトを xor32 で暗号化します。
期待される時間だけ待機した後、シェルコードが私たちの MySleep ハンドラに戻ってきたら、シェルコードのデータを復号化し、保護を RX に戻します。
PAGE_READWRITE への変動の仕組みkernel32!Sleep をフックして、私たちのコールバックを指すようにします。VirtualAlloc + memcpy + CreateThread を介してシェルコードを注入し起動します。ThreadStackSpoofer で行ったのとは異なり、ここではシェルコードを起動するために ntdll 内の何かをフックするのではなく、独自の関数からシェルコードにジャンプします。これにより、改変された ntdll メモリを指し示す単純な IOC をメモリに残すことを回避しようとしています。MySleep コールバックが呼び出されます。RW に変更されます。kernel32!Sleep のフックを解除して、Sleep がトランポリン化(インラインフック)されたことを示す単純な IOC をメモリに残さないようにします。::Sleep を呼び出して、さらなる通信を待つ間 Beacon をスリープさせます。RX に戻してから、kernel32!Sleep を再フックして後続のスリープを確実に傍受します。PAGE_NOACCESS への変動の仕組みkernel32!Sleep をフックして、私たちのコールバックを指すようにします。VirtualAlloc + memcpy + CreateThread を介してシェルコードを注入し起動します...MySleep コールバックが呼び出されます。PAGE_NOACCESS に変更されます。kernel32!Sleep のフックを解除して、Sleep がトランポリン化(インラインフック)されたことを示す単純な IOC をメモリに残さないようにします。::Sleep を呼び出して、さらなる通信を待つ間 Beacon をスリープさせます。kernel32!Sleep を再フックして後続のスリープを確実に傍受します。RX に戻し、シェルコードの実行が再開されます。このテクニックは真新しいものではなく、私自身が考案したものでもありません。コンセプトとその実用的な活用方法を示す実装に過ぎず、商用 C2 フレームワークが提供する機能に Offensive Security コミュニティが追いつけるようにするためのものです。
実際、シェルコードのメモリ保護を切り替えるというアイデアは、数年前に Josh Lospinoso 氏の素晴らしい Gargoyle を通じて知りました。
関連する背景情報は以下のとおりです:
Gargoyle は、自己認識型で自己変動するシェルコードのコンセプトをさらに進め、VirtualProtect を呼び出す ROP シーケンスを活用しています。
しかし、このテクニックは印象的ではありますが、Cobalt Strike の Beacon と組み合わせて利用するのは同様に難しく、スレッドを殺してメモリ上で Beacon を再初期化し続ける必要があります。
完璧とは程遠いですが、私たちはすでに独自の自己インジェクションローダープロセスを基盤として操作しているため、シェルコードが動作する環境に対して望むことを何でも行い、好きなように隠すことができます。このテクニック(および以前の ThreadStackSpoofer)は、この方法でシェルコードを実行することの利点を示しています。
PAGE_NOACCESS への変動の実装は、ORCA666 氏が彼の https://github.com/ORCA666/0x41 インジェクターで示した作品に触発されています。
彼は次のことを示しました:
この実装にはこのアイデアが実装されており、<fluctuate> のオプション 2 で利用できます。
彼の他のプロジェクトもぜひチェックしてください。
ツール ShellcodeFluctuation は3つのパラメータを受け付けます。1つ目はシェルコードへのパス、2つ目は機能の修飾子です。```
Usage: ShellcodeFluctuation.exe
:
-1 - Read shellcode but dont inject it. Run in an infinite loop.
0 - Inject the shellcode but don't hook kernel32!Sleep and don't encrypt anything
1 - Inject shellcode and start fluctuating its memory with standard PAGE_READWRITE.
2 - Inject shellcode and start fluctuating its memory with ORCA666's PAGE_NOACCESS.
### Moneta(一見誤検知)```
C:\> ShellcodeFluctuation.exe beacon64.bin -1
まず、Moneta64 スキャナが、怪しいことを何もせず単に無限ループを実行するプロセスについてどう見るかを見てみましょう:

見てのとおり、いくつかの誤検知(少なくとも私の考えでは)があり、Mismatching PEB module / Phantom image を検出しているとされています。
メモリ境界は ShellcodeFluctuate.exe モジュール自体を指しており、このモジュールは MEM_IMAGE タイプであるにもかかわらず、プロセスの PEB にリンクされていない可能性を示しています。これは異例で、かなり奇妙に聞こえます。
この IOC の理由は私には分かっておらず、より深く理解しようともしませんでしたが、実際に心配するようなものではありません。
この検出の理由をご存知の方がいらっしゃいましたら、ぜひお聞かせください。どうかご連絡ください。
C:> ShellcodeFluctuation.exe beacon64.bin 0
2番目のユースケースは、私たちのプロセス内で動作するBeaconのメモリIOCを示しています。これは、カスタマイズされた`Artifact Kits`や`User-Defined Reflective Loaders`(私の[`ElusiveMice`](https://github.com/mgeeky/ElusiveMice)など)を一切使用しておらず、結果を損なうような初期アクションも一切行っていません。

`Moneta64`が、シェルコードが存在する場所を指す`Abnormal private executable memory`を正しく認識していることがわかります。
これは非常に強力なメモリIOCであり、自動スキャナによるダンプと分析のためにシェルコードを露出させてしまいます。クールではありません。
### RW保護付き暗号化Beacon```
C:\> ShellcodeFluctuation.exe beacon64.bin 1
さて、この実装の観点から最も興味深い3つ目のユースケースは、_変動する_ビーコンです。

最初のIOCは別として(多少_誤検知_と考えられます)、kernel32.dllのメモリが改変されたことを示す新しいIOCが見られます。
ただし、今回はAbnormal private executable memory IOCはありません。私たちの変動(暗号化・復号化の繰り返しとメモリ保護の切り替え)が有効になっています。
ちなみに、pe-sieveも/data 3オプションを指定して使用すると、注入されたPEを検出します(このオプションを指定しない限り、検出は行われません)。

私の現在の仮説では、PE-SieveはMonetaと同じ特性を検出しています(下の_Modified code in kernel32.dll_で説明)—つまり、PEマップ済みモジュールのワーキングセットが空ではないという事実であり、これは何らかのコードインジェクションの明らかな証拠です。 これは_Implanted PE_ / _Implanted_としてラベル付けされます。その場合、結論はMonetaの観察に似ています。検出の観点では、そのIOCをそれほど気にする必要はないと思います。
現時点では、シェルコードの実行を途中でインターセプトする(今はCobalt Strikeの話です)ために、kernel32!Sleepをフックする以外に良い選択肢を思いつきませんでした。したがって、この種のIOCを残さざるを得ません。
でもね、それでもファイルシステム上にある(C:\Windows\System32\kernel32.dll)ものと比較して、どのバイトも違っていませんし、フックされている関数もありません。どういうことでしょう?😉