Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
EDRSandblast — 脆弱性のある署名済みドライバを悪用して、EDRカーネルコールバック、オブジェクトコールバック、ETW TIプロバイダ、ユーザーランドフックをバイパスし、LSASSメモリダンプと資格情報抽出を行います。 | Kitploit
ツール/GitHubGitHub/wavestone-cdt/edrsandblast
防御ツール特権昇格エクスプロイトペネトレーションテストレッドチーミング
GitHubwavestone-cdt/edrsandblast

EDRSandblast

脆弱性のある署名済みドライバを悪用して、EDRカーネルコールバック、オブジェクトコールバック、ETW TIプロバイダ、ユーザーランドフックをバイパスし、LSASSメモリダンプと資格情報抽出を行います。

リポジトリを見る
1.8k3202年前Kitploit レビュー済み

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

EDRSandBlast

EDRSandBlast は、C で書かれたツールであり、脆弱性のある署名済みドライバを悪用して、EDR の検出(Notify Routine コールバック、Object Callbacks、ETW TI プロバイダー)や LSASS の保護をバイパスします。また、ユーザーランド監視を回避するための複数のユーザーランドアンフック技術も実装されています。

リリース時点では、ユーザーランド(--usermode)とカーネルランド(--kernelmode)の技術を組み合わせて、EDR の監視下で LSASS メモリをダンプし、ブロックされることなく、また製品(クラウド)コンソールに「OS Credential Dumping」関連のイベントを生成することもありませんでした。テストは 3 種類の異なる EDR 製品に対して実施され、それぞれ成功しました。

説明

カーネル Notify Routines の削除による EDR バイパス

EDR 製品は、Windows のカーネル「Notify Routines」コールバックを使用して、プロセスやスレッドの作成、イメージ(exe / DLL)のロードなど、システムアクティビティの通知をカーネルから受け取ります。

これらのカーネルコールバックは、カーネルランドから定義され、通常はコールバックを実装するドライバが、文書化されたいくつかの API(nt!PsSetCreateProcessNotifyRoutine、nt!PsSetCreateThreadNotifyRoutine など)を使用して定義します。これらの API は、ドライバが提供するコールバックルーチンを、カーネル空間内の文書化されていないルーチンの配列に追加します:

  • PspCreateProcessNotifyRoutine(プロセス作成用)
  • PspCreateThreadNotifyRoutine(スレッド作成用)
  • PspLoadImageNotifyRoutine(イメージロード用)

EDRSandBlast は、これらの配列で定義されたルーチンを列挙し、事前定義された EDR ドライバのリスト(1000 以上のセキュリティ製品のドライバに対応、EDR ドライバ検出セクション を参照)に関連するコールバックルーチンをすべて削除します。この列挙と削除は、脆弱性のあるドライバの悪用によって提供される任意のカーネルメモリ読み取り/書き込みプリミティブの悪用を通じて可能になります(脆弱性のあるドライバセクション を参照)。

前述の配列のオフセットは、複数の技術を使用して復元されます。詳細は オフセットセクション を参照してください。

オブジェクトコールバックの削除による EDR バイパス

EDR(および EPP)製品は、多くの場合、nt!ObRegisterCallbacks カーネル API を使用して「オブジェクトコールバック」を登録します。これらのコールバックにより、セキュリティ製品は、特定のオブジェクトタイプ(Windows ではプロセス、スレッド、デスクトップに関連するオブジェクトコールバックがサポートされています)に対するハンドル生成のたびに通知を受け取ることができます。ハンドル生成は、オブジェクトのオープン(OpenProcess、OpenThread などの呼び出し)だけでなく、ハンドルの複製(DuplicateHandle などの呼び出し)でも発生する可能性があります。

これらの各操作についてカーネルから通知を受けることで、セキュリティ製品はハンドル作成の正当性を分析し((例:未知のプロセスが LSASS を開こうとしている))、脅威が検出された場合はブロックすることもできます。

ObRegisterCallbacks を使用したコールバック登録のたびに、コールバックの影響を受けるオブジェクトのタイプ(プロセス、スレッド、デスクトップのいずれか)を記述する _OBJECT_TYPE オブジェクト内に存在する二重リンクリスト CallbackList に新しい項目が追加されます。残念ながら、これらの項目は Microsoft によって文書化されておらず、シンボルファイルにも公開されていない構造体で記述されています。しかし、さまざまな ntoskrnl.exe バージョンから調査したところ、この構造体は(少なくとも)Windows 10 ビルド 10240 から 22000(2015 年から 2022 年)の間で変更されていないようです。

言及された、オブジェクトコールバック登録を表す構造体は次のとおりです:```C typedef struct OB_CALLBACK_ENTRY_t { LIST_ENTRY CallbackList; // linked element tied to _OBJECT_TYPE.CallbackList OB_OPERATION Operations; // bitfield : 1 for Creations, 2 for Duplications BOOL Enabled; // self-explanatory OB_CALLBACK* Entry; // points to the structure in which it is included POBJECT_TYPE ObjectType; // points to the object type affected by the callback POB_PRE_OPERATION_CALLBACK PreOperation; // callback function called before each handle operation POB_POST_OPERATION_CALLBACK PostOperation; // callback function called after each handle operation KSPIN_LOCK Lock; // lock object used for synchronization } OB_CALLBACK_ENTRY;

root@kitploit:~
上記の`OB_CALLBACK`構造体も未文書化であり、以下のように定義されています:```C
typedef struct OB_CALLBACK_t {
    USHORT Version;                           // usually 0x100
    USHORT OperationRegistrationCount;        // number of registered callbacks
    PVOID RegistrationContext;                // arbitrary data passed at registration time
    UNICODE_STRING AltitudeString;            // used to determine callbacks order
    struct OB_CALLBACK_ENTRY_t EntryItems[1]; // array of OperationRegistrationCount items
    WCHAR AltitudeBuffer[1];                  // is AltitudeString.MaximumLength bytes long, and pointed by AltitudeString.Buffer
} OB_CALLBACK;

EDRに登録されたオブジェクトコールバックを無効にするために、EDRSandblastには3つの手法が実装されていますが、現時点では1つだけが有効になっています。

OB_CALLBACK_ENTRY の Enabled フィールドを使用する

これはEDRSandblastでデフォルトで有効になっている手法です。EDR関連のオブジェクトコールバックを検出して無効にするために、ProcessおよびThreadタイプに関連付けられた_OBJECT_TYPEオブジェクト内にあるCallbackListリストを参照します。両方の_OBJECT_TYPEは、カーネル内の公開グローバルシンボルPsProcessTypeとPsThreadTypeによって指されています。

リストの各項目は、上記で説明したOB_CALLBACK_ENTRY構造体に適合すると想定されています(少なくとも執筆時点のすべてのWindows 10ビルドで成立すると思われる仮定)。PreOperationフィールドとPostOperationフィールドで定義された関数が、EDRドライバに属するかどうかをチェックするために特定され、該当する場合は、Enabledフラグを切り替えるだけでコールバックが無効になります。

かなり安全な手法ですが、文書化されていない構造に依存するという不便さがあります。この構造の安全でない操作のリスクを軽減するために、いくつかのフィールドが期待される値を持っていることを検証する基本的なチェックが実行されます:

  • Enabled は TRUE または FALSE のいずれかである(笑わないでください。BOOL は int なので、1 や 0 以外の値になる可能性があります)。
  • Operations は OB_OPERATION_HANDLE_CREATE、OB_OPERATION_HANDLE_DUPLICATE、またはその両方である。
  • ObjectType は PsProcessType または PsThreadType を指す。

スレッドとプロセスのCallbackListをアンリンクする

文書化されていない構造に依存しない別の戦略(したがって理論的にはNTカーネルの変更に対してより堅牢)は、プロセスとスレッドの両方に対するCallbackList全体をアンリンクすることです。_OBJECT_TYPEオブジェクトは以下の通りです:```C struct _OBJECT_TYPE { LIST_ENTRY TypeList; UNICODE_STRING Name; [...] _OBJECT_TYPE_INITIALIZER TypeInfo; [...] LIST_ENTRY CallbackList; }

root@kitploit:~
Making the `Flink` and `Blink` pointers of the `CallbackList` `LIST_ENTRY` point to
the `LIST_ENTRY` itself effectively make the list empty. Since the `_OBJECT_TYPE` structure
is published in the kernel' symbols, the technique does not rely on hardcoded offsets/structures.
However, it has some drawbacks.

The first being not able to only disable callbacks from EDR; indeed, the technique affects
all object callbacks that could have been registered by "legitimate" software. It should
nevertheless be noted that object callbacks are not used by any pre-installed component
on Windows 10 (at the time of writing) so disabling them should not affect the machine
stability (even more so if the disabling is only temporary).

The second drawback is that process or thread handle operation are really frequent (nearly
continuous) in the normal functioning of the OS. As such, if the kernel write primitive used
cannot perform a `QWORD` write "atomically", there is a good chance that the
`_OBJECT_TYPE.CallbackList.Flink` pointer will be accessed by the kernel in the middle
of its overwriting. For instance, the MSI vulnerable driver `RTCore64.sys` can only perform
a `DWORD` write at a time, so 2 distinct IOCTLs will be needed to overwrite the pointer, between
which the kernel has a high probability of using it (resulting in a crash). On the other hand,
the vulnerable DELL driver `DBUtil_2_3.sys` can perform writes of arbitrary sizes in one
IOCTL, so using this method with it does not risk causing a crash.

#### Disabling object callbacks altogether
One last technique we found was to disable entirely the object callbacks support for thread
and processes. Inside the `_OBJECT_TYPE` structure corresponding to the process and
thread types resides a `TypeInfo` field, following the documented `_OBJECT_TYPE_INITIALIZER`
structure. The latter contains a `ObjectTypeFlags` bit field, whose `SupportsObjectCallbacks`
flag determines if the described object type (Process, Thread, Desktop, Token, File, etc.)
supports object callback registering or not. As previously stated, only Process, Thread and
Desktop object types supports these callbacks on a Windows installation at the time of writing.

Since the `SupportsObjectCallbacks` bit is checked by `ObpCreateHandle` or
`ObDuplicateObject` before even reading the `CallbackList` (and before executing
callbacks, of course), flipping the bit at kernel runtime effectively disable all object callbacks
execution.

The main drawback of the method is simply that *KPP* ("*PatchGuard*") monitors the integrity
of some (all ?) `_OBJECT_TYPE` structures, and triggers a [`0x109 Bug Check`](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/bug-check-0x109---critical-structure-corruption)
with parameter 4 being equal to `0x8`, meaning an object type structure has been altered.

However, performing the disabling / re-enabling (and "malicious" action in-between) quickly
enough should be enough to "race" *PatchGuard* (unless you are unlucky and a periodic
check is performed just at the wrong moment).

### EDR bypass through minifilters' callbacks unlinking
The Windows Filter Manager system allows an EDR to load a "minifilter" driver and
register callbacks in order to be notified of I/O operations, such as file opening,
reading, writing, etc. 

Here is a quick sum-up of different internal structures used by the filter manager:
- The Filter Manager establishes a "frame" (`_FLTP_FRAME`) as its root structure;
- A "volume" structure (`_FLT_VOLUME`) is instanciated for each "disk" managed by the
Filter Manager (can be partitions, shadow copies, or special ones corresponding to
named pipes or remote file systems);
- To each registered minifilter driver corresponds a "filter" structure (`_FLT_FILTER`),
describing various properties such as its supported operations;
- These minifilters are not all attached to each volume; an "instance" (`_FLT_INSTANCE`)
structure is created to mark each of the 
		filter<->volume associations;
- Minifilters register callback functions that are to be executed before and/or after
 specific operations (file open, write, read, etc.). These callbacks are described in
`_CALLBACK_NODE` structures, and can be accessed by different ways:
  - An array of all `_CALLBACK_NODE`s implemented by an instance of a minifilter
    can be found in the `_FLT_INSTANCE` structure; the array is indexed by the IRP 
    "major function" code, a constant representing the operations handled by the 
    callbacks (`IRP_MJ_CREATE`, `IRP_MJ_READ`, etc.).
  - Also, all `_CALLBACK_NODE`s implemented by instances linked to a specific volume
  are regrouped in linked lists, stored in the `_FLT_VOLUME.Callbacks.OperationLists`
  array indexed by IRP major function codes.

These different structures are browsed by `EDRSandblast` to detect filters that are 
associated with EDR-related drivers, and the callback nodes containing monitoring 
functions are enumerated. To disable their effect, the nodes are unlinked from their 
lists, making them temporarily invisible from the filter manager.

This way, during a specified period, the EDR can be completely unaware of any file 
operations. A basic example would be the creation of an lsass memory dump file on disk,
that would not trigger any analysis from the EDR, and thus no detection based on the 
file itself.

### EDR bypass through deactivation of the ETW Microsoft-Windows-Threat-Intelligence provider

The `ETW Microsoft-Windows-Threat-Intelligence` provider logs data about the
usages of some Windows API commonly used maliciously. This include the
`nt!MiReadWriteVirtualMemory` API, called by `nt!NtReadVirtualMemory` (which is
used to dump `LSASS` memory) and monitored by the `nt!EtwTiLogReadWriteVm`
function.

EDR products can consume the logs produced by the `ETW TI` provider through
services or processes running as, respectively,
`SERVICE_LAUNCH_PROTECTED_ANTIMALWARE_LIGHT` or
`PS_PROTECTED_ANTIMALWARE_LIGHT`, and associated with an `Early Launch Anti
Malware (ELAM)` driver.

As published by
[`slaeryan` in a `CNO Development Labs` blog post](https://public.cnotools.studio/bring-your-own-vulnerable-kernel-driver-byovkd/exploits/data-only-attack-neutralizing-etwti-provider),
the `ETW TI` provider can be disabled altogether by patching, in kernel memory,
its `ProviderEnableInfo` attribute to `0x0`. Refer to the great aforementioned
blog post for more information on the technique.

Similarly to the Kernel callbacks removal, the necessary `ntoskrnl.exe` offsets
(`nt!EtwThreatIntProvRegHandleOffset`, `_ETW_REG_ENTRY`'s `GuidEntry`, and
`_ETW_GUID_ENTRY`'s `ProviderEnableInfo`) are computed in the
`NtoskrnlOffsets.csv` file for a number of the Windows Kernel versions.

### EDR bypass through userland hooking bypass
#### How userland hooking works
In order to easily monitor actions that are performed by processes, EDR products often
deploy a mechanism called *userland hooking*. First, EDR products register a kernel
callback (usually *image loading* or *process creation* callbacks, see above) that allows
them to be notified upon each process start.


When a process is loaded by Windows, and before it actually starts, the EDR is able to
inject some custom DLL into the process address space, which contains its monitoring
logic. While loading, this DLL injects "*hooks*" at the start of every function that is to
be monitored by the EDR. At runtime, when the monitored functions are called by the
process under surveillance, these hooks redirect the control flow to some supervision code
present in the EDR's DLL, which allows it to inspect arguments and return values of these
calls.

Most of the time, monitored functions are system calls (such as `NtReadVirtualMemory`,
`NtOpenProcess`, etc.), whose implementations reside in `ntdll.dll`. Intercepting calls to
`Nt*` functions allows products to be as close as possible to the userland / kernel-land
boundary (while remaining in userland), but functions from some higher-level DLLs may also
be monitored as well.

Bellow are examples of the same function, before and after beeing hooked by the EDR product:```assembly
NtProtectVirtualMemory   proc near
	mov r10, rcx
	mov eax, 50h
	test byte ptr ds:7FFE0308h, 1
	jnz short loc_18009D1E5
	syscall
	retn
loc_18009D1E5:
	int 2Eh
	retn
NtProtectVirtualMemory   endp			
  • アクティブスキャンモードでは、すべての予期しないイベントに対してアラートが生成されます。
  • バックグラウンドスキャンモードでは、システムの脆弱性が検出された場合にのみアラートが生成されます。
  • 初回のスキャンでは、システムのすべてのシグネチャとCVEが検査されます。これには時間がかかる場合があります。```assembly NtProtectVirtualMemory proc near jmp sub_7FFC74490298 ; --> "hook", jump to EDR analysis function int 3 ; overwritten instructions int 3 ; overwritten instructions int 3 ; overwritten instructions test byte_7FFE0308, 1 ; <-- execution resumes here after analysis jnz short loc_7FFCB44AD1E5 syscall retn loc_7FFCB44AD1E5: int 2Eh retn NtProtectVirtualMemory endp
root@kitploit:~
#### フック検出

ユーザーランドフックには、ユーザーランドメモリに存在するという「弱点」があり、そのため監視対象プロセスから直接観測可能で変更可能です。プロセスのアドレス空間内のフックを自動的に検出するには、ディスク上の元のDLLと、EDRによって変更された可能性のあるメモリ内のライブラリとの差異を比較することが主なアイデアです。この比較を行うために、EDRSandblastは以下の手順を実行します。

* ロードされたすべてのDLLのリストは、`PEB`内の`InLoadOrderModuleList`を使って列挙されます(監視や疑わしい可能性のあるAPI呼び出しを避けるため)。
* ロードされた各DLLについて、ディスク上の内容が読み取られ、ヘッダが解析されます。メモリ内の対応するライブラリも解析され、セクションやエクスポートなどが特定されます。
* DLLの再配置が解析され、対応するロード済みライブラリのベースアドレスを考慮して適用されます。これにより、メモリ内のライブラリとディスク上のDLLの内容が(再配置が適用されるセクションで)完全に同じになり、比較が信頼できるものになります。
* エクスポート関数が列挙され、「メモリ内」と「ディスク上」のバージョンの最初のバイトが比較されます。差異がある場合は、DLLがロードされた後に何らかの変更が加えられたことを示し、そのためほぼ間違いなくEDRフックです。

注:このプロセスは、書き込み不可セクション内のどこにでも差異を見つけるように一般化できます(エクスポート関数の先頭だけでなく、例えばEDR製品が関数の途中にフックを適用し始めた場合など)。そのため、このツールでは使用されていませんが、`findDiffsInNonWritableSections`に実装されています。

これらのフックによる監視をバイパスするためには、複数の手法が可能であり、それぞれに利点と欠点があります。

#### フックバイパス...アンフックによる方法

フックベースの監視をバイパスする最も直感的な方法は、フックを削除することです。フックはプロセス自身がアクセス可能なメモリに存在するため、フックを削除するには、プロセスは単に以下を行います。

* フックが配置されているページのアクセス許可を変更する(RX -> RWX または RW)
* ディスク上のDLLの内容からわかっている元のバイトを書き込む
* アクセス許可をRXに戻す

この方法は比較的単純で、検出されたすべてのフックを一度に削除するために使用できます。攻撃ツールの初期に実行することで、残りのコードはフックメカニズムをまったく意識せずに、監視されることなく通常通り動作できます。

しかし、2つの大きな欠点があります。EDRはおそらく`NtProtectVirtualMemory`の使用を監視しているため、それを使ってフックがインストールされたページのアクセス許可を変更することは(少なくとも概念的には)悪い考えです。また、EDRによってスレッドが実行され、定期的にフックの整合性をチェックする場合、これも検出を引き起こす可能性があります。

実装の詳細については、`unhook()`関数のコードパスで`unhook_method`が`UNHOOK_WITH_NTPROTECTVIRTUALMEMORY`の場合を確認してください。

**重要な注意:簡略化のため、この手法はEDRSandblastにおいて、他のバイパス手法を*示す*ための基本手法として実装されています。各手法は監視されないバージョンの`NtProtectVirtualMemory`を取得する方法を示していますが、その後は同じ操作(特定のフックのアンフック)を実行します。**

#### カスタムトランポリンによるフックバイパス

特定のフックをバイパスするには、単に「飛び越えて」関数の残りの部分をそのまま実行することが可能です。まず、監視対象の関数の元のバイト(EDRがフックをインストールするために上書きしたもの)をDLLファイルから復元する必要があります。前述のコード例では、これは以下の命令に対応するバイトになります。```assembly
mov r10, rcx
mov eax, 50h

これらのバイトの特定は、前述の通り、メモリ上とディスク上のライブラリの両方のバージョンをクリーンな差分で比較できるため、簡単な作業です。次に、フックの直後にあるコード、すなわちアドレス NtProtectVirtualMemory + sizeof(overwritten_instructions) に制御フローをリダイレクトするように構築されたジャンプ命令をアセンブルします。```assembly jmp NtProtectVirtualMemory+8

root@kitploit:~
最後に、これらのオペコードを連結し、(新しく)実行可能なメモリに格納して、それらへのポインタを保持します。このオブジェクトは「*トランポリン*」と呼ばれ、元の `NtProtectVirtualMemory` 関数と完全に同等の関数ポインタとして使用できます。

このテクニックの主な利点は、以下に示すすべてのテクニックと同様に、フックが決して消去されないことです。そのため、EDR によってフックに対して実行されるすべての整合性チェックはパスするはずです。ただし、書き込み可能なメモリを割り当ててから実行可能にする必要があり、これはシェルコードの割り当てに典型的であり、EDR の監視を引き付けることになります。

実装の詳細については、`unhook_method` が `UNHOOK_WITH_INHOUSE_NTPROTECTVIRTUALMEMORY_TRAMPOLINE` の場合の `unhook()` 関数のコードパスを確認してください。このテクニックは私たちの実装でのみ示されており、結局のところ、以下に示すすべてのテクニックと同様に、メモリからフックを**削除**するために使用されることを覚えておいてください。

#### EDR 自身のトランポリンを使用したフックバイパス
EDR 製品は、そのフックが機能するために、自身が削除したオペコードをメモリのどこかに保存する必要があります。最悪の場合(*または攻撃者の視点からは「より良い」場合*)、元の命令を効果的に使用するために、EDR はおそらくどこかに*トランポリン*を割り当てて、呼び出しを傍受した後で元の関数を実行します。

このトランポリンは検索して、フックされた関数の代わりとして使用できます。実行可能メモリを割り当てたり、`VirtualQuery` 以外の API を呼び出す必要はありません。`VirtualQuery` は無害な関数であるため、おそらく監視されていません。

メモリ内のトランポリンを見つけるには、`VirtualQuery` を使用してアドレス空間全体をブラウズし、コミットされ実行可能なメモリを探します。そのようなメモリ領域ごとに、上書きされた命令の後のアドレス(前の例では `NtProtectVirtualMemory+8`)をターゲットとするジャンプ命令をスキャンします。その後、トランポリンを使用して、フックをトリガーせずにフックされた関数を呼び出すことができます。

このテクニックは驚くほど効果的で、テストした EDR のほぼすべてのトランポリンを回復します。実装の詳細については、`unhook_method` が `UNHOOK_WITH_EDR_NTPROTECTVIRTUALMEMORY_TRAMPOLINE` の場合の `unhook()` 関数のコードパスを確認してください。

#### 重複 DLL を使用したフックバイパス
監視されていないバージョンの `NtProtectVirtualMemory` 関数にアクセスする別の簡単な方法は、`ntdll.dll` ライブラリの重複バージョンをプロセスアドレス空間にロードすることです。2 つの同一の DLL に異なる名前が付けられていれば、同じプロセスにロードできるため、正当な `ntdll.dll` ファイルを別の場所にコピーし、`LoadLibrary`(またはロードプロセスを再実装)を使用してロードし、`GetProcAddress` などを使用して関数にアクセスできます。

このテクニックは理解と実装が非常に簡単で、成功する可能性も十分にあります。ほとんどの EDR 製品は、プロセスが実行されている間、新しくロードされた DLL にフックを再インストールしないためです。ただし、大きな欠点は、署名付き Microsoft バイナリを別の名前でコピーすること自体が、多くの場合、EDR 製品によって疑わしいと見なされることです。

それでも、このテクニックは `EDRSandblast` に実装されています。実装の詳細については、`unhook_method` が `UNHOOK_WITH_DUPLICATE_NTPROTECTVIRTUALMEMORY` の場合の `unhook()` 関数のコードパスを確認してください。

#### ダイレクトシステムコールを使用したフックバイパス
システムコール関連の機能を使用するために、プログラムは(アセンブリで)システムコールを再実装して、`ntdll.dll` のコードに実際に触れることなく対応する OS 機能を呼び出すことができます。これにより、EDR によって監視される可能性がある `ntdll.dll` 内のシステムコール関数に対するすべてのユーザーランドフックを完全にバイパスします。

ただし、これにはいくつかの欠点があります。まず、プログラムが必要とする関数のシステムコール番号のリストを知っている必要があることを意味し、これは Windows のバージョンごとに異なります。ただし、これは、過去のすべてのバージョンの Windows NT で機能することが知られている複数のヒューリスティック(`ntdll` の `Zw*` エクスポートの並べ替え、関連する `ntdll` 関数内の `mov rax, #syscall_number` 命令の検索など)を実装し、それらすべてが同じ結果を返すことを確認することで軽減されます(詳細については `Syscalls.c` を参照)。

また、技術的にはシステムコールではない関数(例:`LoadLibraryX`/`LdrLoadDLL`)も監視される可能性があり、単にシステムコールを使用して再実装することはできません。

ダイレクトシステムコールのテクニックは EDRSandblast に実装されています。前述のように、これは `NtProtectVirtualMemory` を安全に実行し、検出されたすべてのフックを削除するためにのみ使用されます。

実装の詳細については、`unhook_method` が `UNHOOK_WITH_DIRECT_SYSCALL` の場合の `unhook()` 関数のコードパスを確認してください。

### 脆弱なドライバーの悪用
前述のように、カーネルメモリの読み取りまたは書き込みを必要とするすべてのアクションは、このプリミティブを提供する脆弱なドライバーに依存しています。EDRSanblast では、読み取り/書き込みプリミティブを提供する新しいドライバーのサポートを追加することは「簡単に」行え、3 つの関数のみを実装する必要があります。
* `ReadMemoryPrimitive_DRIVERNAME(SIZE_T Size, DWORD64 Address, PVOID Buffer)` 関数:`Size` バイトをカーネルアドレス `Address` からユーザーランドバッファ `Buffer` にコピーします。
* `WriteMemoryPrimitive_DRIVERNAME(SIZE_T Size, DWORD64 Address, PVOID Buffer)` 関数:`Size` バイトをユーザーランドバッファ `Buffer` からカーネルアドレス `Address` にコピーします。
* `CloseDriverHandle_DRIVERNAME()`:ドライバーへのすべてのハンドルが閉じられていることを確認します(現在のところ、ドライバーに依存しないアンインストール操作の前に必要です)。

例として、現在 EDRSandblast では 2 つのドライバーがサポートされています。`RTCore64.sys`(SHA256: `01AA278B07B58DC46C84BD0B1B5C8E9EE4E62EA0BF7A695862444AF32E87F1FD`) と `DBUtils_2_3.sys`(SHA256: `0296e2ce999e67c76352613a718e11516fe1b0efc3ffdb8918fc999dd76a73a5`) です。使用する脆弱なドライバーを変更する場合、または新しいドライバーを実装する場合は、`KernelMemoryPrimitives.h` の次のコードを更新する必要があります。```C
#define RTCore 0
#define DBUtil 1
// Select the driver to use with the following #define
#define VULN_DRIVER RTCore

#if VULN_DRIVER == RTCore
#define DEFAULT_DRIVER_FILE TEXT("RTCore64.sys")
#define CloseDriverHandle CloseDriverHandle_RTCore
#define ReadMemoryPrimitive ReadMemoryPrimitive_RTCore
#define WriteMemoryPrimitive WriteMemoryPrimitive_RTCore
#elif VULN_DRIVER == DBUtil
#define DEFAULT_DRIVER_FILE TEXT("DBUtil_2_3.sys")
#define CloseDriverHandle CloseDriverHandle_DBUtil
#define ReadMemoryPrimitive ReadMemoryPrimitive_DBUtil
#define WriteMemoryPrimitive WriteMemoryPrimitive_DBUtil
#endif

EDRドライバとプロセスの検出

特定のドライバやプロセスがEDR製品に属するかどうかを判断するために、現在複数の手法が使用されています。

まず、ドライバの名前をその目的に使用できます。実際、Microsoftはカーネル内でコールバックを挿入する必要があるすべてのドライバに対して「Altitude」と呼ばれる特定の番号を割り当てています。これにより、コールバックの実行順序が登録順序に依存せず、ドライバの用途のみに基づいた決定論的な順序が可能になります。特定のaltitudeを予約したドライバの(ベンダーの)リストは、MSDNで見つけることができます。その結果、セキュリティ製品に関連するセキュリティドライバ名のほぼ包括的なリストがMicrosoftによって提供されており、主に「FSFilter Anti-Virus」および「FSFilter Activity Monitor」リストに含まれています。これらのドライバ名のリストは、追加の貢献とともにEDRSandblastに組み込まれています。

さらに、EDRの実行可能ファイルやDLLは、多くの場合、ベンダーの署名証明書を使用してデジタル署名されています。したがって、プロセスに関連付けられた実行可能ファイルやDLLの署名者を確認することで、EDR製品を迅速に特定できる可能性があります。

また、ドライバはカーネル空間にロードされるためにMicrosoftによって直接署名される必要があります。ドライバのベンダーがドライバ自体の署名者ではないとしても、ベンダー名は署名の属性内に含まれているようです。しかし、この検出手法はまだ調査および実装されていません。

最後に、EDRSandblastにとって未知のEDRに直面した場合、最善のアプローチはツールを「監査」モードで実行し、カーネルコールバックを登録したドライバのリストを確認することです。その後、ドライバ名をリストに追加し、ツールを再コンパイルして再実行できます。

RunAsPPLバイパス

Windows 8.1およびWindows Server 2012 R2で初めて導入された「Local Security Authority (LSA) Protection」メカニズムは、「Protected Process Light (PPL)」テクノロジを活用してLSASSプロセスへのアクセスを制限します。PPL保護は、SeDebugPrivilege権限を持つプロセスからであっても、保護されたプロセスに対するメモリインジェクションやメモリダンプなどの操作を規制および制限します。プロセス保護モデルでは、より高い保護レベルで実行されているプロセスのみが保護されたプロセスに対して操作を実行できます。

Windowsカーネルがプロセスをカーネルメモリ内で表現するために使用する_EPROCESS構造体には、Type(_PS_PROTECTED_TYPE)属性とSigner(_PS_PROTECTED_SIGNER)属性を通じてプロセスの保護レベルを定義する_PS_PROTECTIONフィールドが含まれています。

カーネルメモリに書き込むことにより、EDRSandblastプロセスは自身の保護レベルをPsProtectedSignerWinTcb-Lightにアップグレードできます。このレベルは、RunAsPPLメカニズムで実行されているLSASSプロセスの保護レベルであるPsProtectedSignerLsa-Lightを「支配」するため、LSASSプロセスメモリをダンプするのに十分です。

EDRSandBlastは自己保護を次のように実装しています:

  • 現在のプロセスへのハンドルを開く
  • NtQuerySystemInformationを使用してすべてのシステムハンドルを漏洩し、現在のプロセスに対する開かれたハンドルと、カーネルメモリ内の現在のプロセスのEPROCESS構造体のアドレスを見つける
  • 脆弱なドライバの任意読み取り/書き込み脆弱性を使用して、カーネルメモリ内の現在のプロセスの_PS_PROTECTIONフィールドを上書きします。_PS_PROTECTIONフィールドのEPROCESS構造体に対するオフセット(使用中のntoskrnlバージョンによって定義される)は、NtoskrnlOffsets.csvファイルで計算されます。

Credential Guardバイパス

MicrosoftのCredential Guardは、仮想化ベースの分離テクノロジであり、MicrosoftのWindows 10(Enterpriseエディション)で導入され、LSASSプロセスに保存された資格情報への直接アクセスを防ぎます。

Credential Guardが有効になると、LSAIso(LSA Isolated)プロセスがVirtual Secure Modeで作成されます。これは、CPUの仮想化拡張機能を活用してメモリ内のデータに追加のセキュリティを提供する機能です。LSAIsoプロセスへのアクセスは、NT AUTHORITY\SYSTEMセキュリティコンテキストを持つアクセスであっても制限されます。ハッシュを処理する際、LSAプロセスはLSAIsoプロセスに対してRPC呼び出しを行い、LSAIsoの結果を待って続行します。したがって、LSASSプロセスにはシークレットは含まれず、代わりにLSA Isolated Dataが保存されます。

N4kedTurtleによる最初の研究で述べられているように、「Credential Guardが有効なシステムで、メモリ内のg_fParameter_useLogonCredentialとg_IsCredGuardEnabledの値をパッチすることで、Wdigestを有効にすることができます」。Wdigestの有効化により、新しい対話型ログオンに対してクリアテキストの資格情報がLSASSメモリに保存されるようになります(システムの再起動は不要)。この手法の詳細については、元の研究ブログ記事を参照してください。

EDRSandBlastは、元のPoCをOpSecの観点から少し改善し、いくつかのwdigest.dllバージョンをサポートします(g_fParameter_useLogonCredentialとg_IsCredGuardEnabledの計算されたオフセットを通じて)。

オフセットの取得

カーネル監視バイパス操作を確実に実行するために、EDRSandblastはカーネルメモリの読み取りおよび書き込み場所を正確に知る必要があります。これは、対象イメージ(ntoskrnl.exe、wdigest.dll)内のグローバル変数のオフセット、およびシンボルファイルでMicrosoftによって定義が公開されている構造体内の特定フィールドのオフセットを使用して行われます。これらのオフセットは対象イメージの各ビルドに固有であり、特定のプラットフォームバージョンに対して少なくとも1回収集する必要があります。

EDRSandblastが使用する構造体や変数の位置を特定するためにパターン検索ではなく「ハードコードされた」オフセットを使用する選択は、カーネルコールバックの追加/削除を担当する未公開APIが変更される可能性があり、誤ったアドレスでカーネルメモリを読み書きしようとするとBug Check(Blue Screen of Death)が発生する可能性がある(そして多くの場合発生する)という事実によって正当化されます。マシンのクラッシュは、レッドチーミングや通常のペネトレーションテストのシナリオでは許容されません。クラッシュしたマシンは防御側に非常に可視性が高く、攻撃時にメモリ内に残っていた資格情報が失われるためです。

Windowsの各バージョンごとにオフセットを取得するために、2つのアプローチが実装されています。

手動によるオフセットの取得

必要なntoskrnl.exeおよびwdigest.dllのオフセットは、提供されているExtractOffsets.py Pythonスクリプトを使用して抽出できます。このスクリプトはradare2とr2pipeを使用してPDBファイルからシンボルをダウンロードして解析し、必要なオフセットを抽出します。オフセットは後でEDRSandblastが使用するためにCSVファイルに保存されます。

多くのWindowsビルドをすぐにサポートするために、Winbindexによって多数のntoskrnl.exeおよびwdigest.dllバイナリバージョンが参照されており、ExtractOffsets.pyによって自動的にダウンロード(およびオフセット抽出)できます。これにより、Windows更新パッケージで公開されたほぼすべてのファイルからオフセットを抽出できます(現在までに450以上のntoskrnl.exeと30以上のwdigest.dllバージョンが利用可能で、事前計算されています)。

自動オフセットの取得と更新

EDRSandBlastには、プログラムが必要な.pdbファイルをMicrosoftシンボルサーバーから自動的にダウンロードし、必要なオフセットを抽出し、対応する.csvファイルが存在する場合はそれを更新できるオプションが追加で実装されています。

--internetオプションを使用すると、ツールの実行がはるかに簡単になりますが、プロセス中に.pdbファイルがダウンロードされてディスクにドロップされるため、追加のOpSecリスクが生じます。これはシンボルデータベースの解析に使用されるdbghelp.dll関数に必要ですが、将来的に完全なメモリ内PDB解析を実装することでこの要件を取り除き、ツールのフットプリントを削減できる可能性があります。

使用方法

脆弱なドライバ

EDRSandblastは、少なくとも3つの脆弱なドライバ(gdrv.sys(デフォルト)、RTCore64.sys、DBUtil_2_3.sys)のサポートを公開して実装しています。実際に使用されるドライバは、ツールのコンパイル前に決定されます(includes/KernelMemoryPrimitive.hの#define VULN_DRIVER <driver name>を参照)。カーネル操作を機能させるために、脆弱なドライバのコピーをダウンロードしてEDRSandblastに提供する必要があります。

テスト済みドライバのハッシュは、EDRSanblastが使用するカーネルメモリ読み取り/書き込みプリミティブを実装する各Driver<name>.cファイルの先頭に記載されています。これらのハッシュを使用して、特にhttps://www.loldrivers.ioでドライバサンプルを簡単に見つけることができます。

以下は、サポートされている脆弱なドライバとダウンロードリンクのリストです:

クイック使用方法```

Usage: EDRSandblast.exe [-h | --help] [-v | --verbose] <audit | dump | cmd | credguard | firewall | load_unsigned_driver> [--usermode] [--unhook-method ] [--direct-syscalls] [--add-dll ]* [--kernelmode] [--dont-unload-driver] [--no-restore] [--nt-offsets <NtoskrnlOffsets.csv>] [--fltmgr-offsets <FltmgrOffsets.csv>] [--wdigest-offsets <WdigestOffsets.csv>] [--ci-offsets <CiOffsets.csv>] [--internet] [--vuln-driver <RTCore64.sys>] [--vuln-service <SERVICE_NAME>] [--unsigned-driver <evil.sys>] [--unsigned-service <SERVICE_NAME>] [--no-kdp] [-o | --dump-output <DUMP_FILE>]

root@kitploit:~
### オプション```
-h | --help             Show this help message and exit.
-v | --verbose          Enable a more verbose output.

Actions mode:

        audit                     Display the user-land hooks and / or Kernel callbacks without taking actions.
        dump                      Dump the process specified by --process-name (LSASS process by default), as '<process_name>' in the current directory or at the
                                  specified file using -o | --output <DUMP_FILE>.
        cmd                       Open a cmd.exe prompt.
        credguard                 Patch the LSASS process' memory to enable Wdigest cleartext passwords caching even if
                                  Credential Guard is enabled on the host. No kernel-land actions required.
        firewall                  Add Windows firewall rules to block network access for the EDR processes / services.
        load_unsigned_driver      Load the specified unsigned driver, bypassing Driver Signature Enforcement (DSE).
                                  WARNING: currently an experimental feature, only works if KDP is not present and enabled.

--usermode              Perform user-land operations (DLL unhooking).
--kernelmode            Perform kernel-land operations (Kernel callbacks removal and ETW TI disabling).


Hooking-related options:

--add-dll <dll name or path>            Loads arbitrary libraries into the process' address space, before starting
                                        anything.This can be useful to audit userland hooking for DLL that are not
                                        loaded by default by this program. Use this option multiple times to load
                                        multiple DLLs all at once.
                                        Example of interesting DLLs to look at: user32.dll, ole32.dll, crypt32.dll,
                                        samcli.dll, winhttp.dll, urlmon.dll, secur32.dll, shell32.dll...

--unhook-method <N>                     Choose the userland un-hooking technique, from the following:

        0                               Do not perform any unhooking (used for direct syscalls operations).
        1 (Default)                     Uses the (probably monitored) NtProtectVirtualMemory function in ntdll to remove all
                                        present userland hooks.
        2                               Constructs a 'unhooked' (i.e. unmonitored) version of NtProtectVirtualMemory, by                                        allocating an executable trampoline jumping over the hook, and remove all present
                                        userland hooks.
        3                               Searches for an existing trampoline allocated by the EDR itself, to get an 'unhooked'
                                        (i.e. unmonitored) version of NtProtectVirtualMemory, and remove all present userland
                                        hooks.
        4                               Loads an additional version of ntdll library into memory, and use the (hopefully                                        unmonitored) version of NtProtectVirtualMemory present in this library to remove all
                                        present userland hooks.
        5                               Allocates a shellcode that uses a direct syscall to call NtProtectVirtualMemory,                                        and uses it to remove all detected hooks

--direct-syscalls       Use direct syscalls to dump the selected process memory without unhooking unserland hooks.


BYOVD options:

--dont-unload-driver                    Keep the vulnerable driver installed on the host
                                        Default to automatically unsinstall the driver.
--no-restore                            Do not restore the EDR drivers' Kernel Callbacks that were removed.
                                        Default to restore the callbacks.
--vuln-driver <gdrv.sys>                Path to the vulnerable driver file.
                                        Default to 'gdrv.sys' in the current directory.
--vuln-service <SERVICE_NAME>           Name of the vulnerable service to intall / start.


Driver sideloading options:

--unsigned-driver <evil.sys>            Path to the unsigned driver file.
                                        Default to 'evil.sys' in the current directory.
--unsigned-service <SERVICE_NAME>       Name of the unsigned driver's service to intall / start.
--no-kdp                                Switch to g_CiOptions patching method for disabling DSE (default is callback swapping).


Offset-related options:

--nt-offsets <NtoskrnlOffsets.csv>      Path to the CSV file containing the required ntoskrnl.exe's offsets.
                                        Default to 'NtoskrnlOffsets.csv' in the current directory.
--fltmgr-offsets <FltmgrOffsets.csv>    Path to the CSV file containing the required fltmgr.sys's offsets
                                        Default to 'FltmgrOffsets.csv' in the current directory.
--wdigest-offsets <WdigestOffsets.csv>  Path to the CSV file containing the required wdigest.dll's offsets
                                        (only for the 'credguard' mode).
                                        Default to 'WdigestOffsets.csv' in the current directory.
--ci-offsets <CiOffsets.csv>            Path to the CSV file containing the required ci.dll's offsets
                                        (only for the 'load_unsigned_driver' mode).
                                        Default to 'WdigestOffsets.csv' in the current directory.
-i | --internet                         Enables automatic symbols download from Microsoft Symbol Server
                                        If a corresponding *Offsets.csv file exists, appends the downloaded offsets to the file for later use
                                        OpSec warning: downloads and drops on disk a PDB file for the corresponding image

Dump options:

-o | --dump-output <DUMP_FILE>          Output path to the dump file that will be generated by the 'dump' mode.
                                        Default to 'process_name' in the current directory.
--process-name <NAME>                   File name of the process to dump (defaults to 'lsass.exe')

Build

EDRSandBlast(x64のみ)は Visual Studio 2019(Windows SDK バージョン: 10.0.19041.0、プラットフォームツールセット: Visual Studio 2019 (v142))でビルドされました。

ExtractOffsets.pyの使い方

ExtractOffsets.py は Windows 上でのみテストされていることに注意してください。```

Installation of Python dependencies

pip.exe install -m .\requirements.txt

Script usage

ExtractOffsets.py [-h] -i INPUT [-o OUTPUT] [-d] mode

positional arguments: mode ntoskrnl or wdigest. Mode to download and extract offsets for either ntoskrnl or wdigest

optional arguments: -h, --help show this help message and exit -i INPUT, --input INPUT Single file or directory containing ntoskrnl.exe / wdigest.dll to extract offsets from. If in download mode, the PE downloaded from MS symbols servers will be placed in this folder. -o OUTPUT, --output OUTPUT CSV file to write offsets to. If the specified file already exists, only new ntoskrnl versions will be downloaded / analyzed. Defaults to NtoskrnlOffsets.csv / WdigestOffsets.csv in the current folder. -d, --download Flag to download the PE from Microsoft servers using list of versions from winbindex.m417z.com.

root@kitploit:~
## 検出
防御側(EDRベンダー、Microsoft、SOCアナリストがEDRのテレメトリを分析するなど)の観点から、この種の手法を検出または防止するために複数の指標を使用できます。

### ドライバーのホワイトリスト登録
ツールがカーネルモードメモリで実行するすべての動作は、脆弱なドライバーに依存して任意のコンテンツを読み書きするため、ドライバーの読み込みイベントはEDR製品(またはSOCアナリスト)によって厳重に精査され、異常なドライバーの読み込み時にアラートを発生させるか、既知の脆弱なドライバーをブロックする必要があります。後者のアプローチは、[Microsoft自身が推奨しています](https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-application-control/microsoft-recommended-driver-block-rules)。HVCI(*Hypervisor-protected code integrity*)が有効なWindowsデバイスにはドライバーのブロックリストが組み込まれており、これはWindows(Windows 11では既にデフォルト)で徐々に標準動作になりつつあります。

### カーネルメモリの整合性チェック
攻撃者が未知の脆弱なドライバーを使用してメモリ内で同じアクションを実行する可能性があるため、EDRドライバーは定期的に、カーネルコールバックがまだ登録されているかどうかを、カーネルメモリを直接検査するか(このツールと同様)、単にイベント(プロセス作成、スレッド作成、イメージ読み込みなど)をトリガーし、コールバック関数が実行カーネルによって実際に呼び出されているかを確認することでチェックできます。

余談ですが、この種のデータ構造は、最近の[Kernel Data Protection (KDP)](https://www.microsoft.com/security/blog/2020/07/08/introducing-kernel-data-protection-a-new-platform-security-technology-for-preventing-data-corruption/)メカニズム(Virtual Based Securityに依存)によって保護でき、正しいAPIを呼び出さずにはカーネルコールバック配列を書き込み不可能にすることができます。

同様のロジックは、このツールがETW Threat Intelligenceイベント生成を無効にするために悪用する、`ProviderEnableInfo`などの機密性の高いETW変数にも適用できます。

### ユーザーモードでの検出
プロセスがユーザーランドフックを回避しようとしている初期の指標は、ロードされたモジュールに対応する各DLLへのファイルアクセスです。通常の実行では、ユーザーランドプロセスが`LoadLibrary`呼び出し以外で、特に`ntdll.dll`のようなDLLファイルを読み取る必要はほとんどありません。

APIフックがバイパスされるのを防ぐために、EDR製品は各監視対象プロセス内のメモリ内でフックが変更されていないかを定期的にチェックできます。

最後に、フックの削除を伴わないフッキングバイパス(トランポリンの悪用、直接システムコールの使用など)を検出するために、EDR製品は悪用されたシステムコールに関連するカーネルコールバック(例:`NtCreateProcess`システムコールの`PsCreateProcessNotifyRoutine`、`NtOpenProcess`システムコールの`ObRegisterCallbacks`など)に依存し、ユーザーモードのコールスタック分析を実行して、システムコールが通常のパス(`kernel32.dll` -> `ntdll.dll` -> システムコール)からトリガーされたのか、異常なパス(例:`program.exe` -> 直接システムコール)からトリガーされたのかを判断できます。

## 謝辞

- カーネルコールバックの列挙と削除:
  https://github.com/br-sn/CheekyBlinder

- 脆弱な`Micro-Star MSI Afterburner`ドライバーを通じたカーネルメモリの読み取り/書き込みプリミティブ:
  https://github.com/Barakat/CVE-2019-16098/

- ETW Threat Intelligenceプロバイダーの無効化:
  https://public.cnotools.studio/bring-your-own-vulnerable-kernel-driver-byovkd/exploits/data-only-attack-neutralizing-etwti-provider

- ドライバーのインストール/アンインストール:https://github.com/gentilkiwi/mimikatz

- EDRドライバー名の初期リスト:
  https://github.com/SadProcessor/SomeStuff/blob/master/Invoke-EDRCheck.ps1

- `LSASS`メモリパッチによる`Wdigest`の再有効化によるCredential Guardのバイパス:
  https://teamhydra.blog/2020/08/25/bypassing-credential-guard/

## 著者

[Thomas DIOT (Qazeer)](https://github.com/Qazeer/)
[Maxime MEIGNAN (themaks)](https://github.com/themaks)

## 貢献者への感謝
- [v1k1ngfr](https://github.com/v1k1ngfr): Driver Signature Enforcementのバイパス(`g_CiOptions`パッチによる)とGDRV.sysドライバーのサポート
- [Windy Bug](https://github.com/0mWindyBug): KDP対応のDriver Signature Enforcementバイパス(*コールバックスワッピング*による)とミニフィルターバイパス機能への多大な貢献

## ライセンス

CC BY 4.0 ライセンス - https://creativecommons.org/licenses/by/4.0/
ツールをダウンロード
サポートされているドライバダウンロードリンクSHA256
GDRV.sysLOLDriversリンク31f4cfb4c71da44120752721103a16512444c13c2ac2d857a7e6f13cb679b427
RTCore64.sysLOLDriversリンク01aa278b07b58dc46c84bd0b1b5c8e9ee4e62ea0bf7a695862444af32e87f1fd
DBUtil_2_3.sysLOLDriversリンク0296e2ce999e67c76352613a718e11516fe1b0efc3ffdb8918fc999dd76a73a5