我们知道一般EDR对可疑程序进行监控一般都会采用往程序里注入到检测的进程中,通过hook一些敏感的3环API来判断程序是否进行一些恶意操作,那么我们可以通过添加流程缓解措施和漏洞利用保护参考来实现保护,从而防止EDR的dll注入对进程进行检测
blockdlls
在cs3.14版本过后引入了blockdlls
命令,用于保护beacon
生成的任何子进程不加载非 Microsoft
签名的 dll
这里使用监听新增一个子会话
可以看到rundll32.exe
进程有了Signatures restricted (Microsoft only)
标志
UpdateProcThreadAttribute
cs的实现在UpdateProcThreadAttribute
函数,UpdateProcThreadAttribute
的Attribute
参数0x20007
实际上解析为PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY
,而0x100000000000
解析为PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON
。因此,cs 在这里所做的是使用CreateProcess
API 调用以及STARTUPINFOEX
包含缓解策略的结构,在这种情况下,用于阻止非 Microsoft
签名的 DLL
自己通过代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
|
#include <iostream> #include <windows.h>
int main() { STARTUPINFOEXA si; PROCESS_INFORMATION pi; SIZE_T size = 0; BOOL ret;
ZeroMemory(&si, sizeof(si)); si.StartupInfo.cb = sizeof(STARTUPINFOEXA); si.StartupInfo.dwFlags = EXTENDED_STARTUPINFO_PRESENT;
InitializeProcThreadAttributeList(NULL, 1, 0, &size);
si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc( GetProcessHeap(), 0, size );
InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &size);
DWORD64 policy = PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON;
UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &policy, sizeof(policy), NULL, NULL);
ret = CreateProcessA( NULL, (LPSTR)"C:\\Windows\\System32\\cmd.exe", NULL, NULL, true, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, reinterpret_cast<LPSTARTUPINFOA>(&si), &pi ); }
|
实现效果如下
直接进行注入则报错
SetProcessMitigationPolicy
这个api可以给当前线程添加 Signatures restricted (Microsoft only)
标识
代码如下
1 2 3 4 5 6
| void blockdll_thread() { PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY sp = {}; sp.MicrosoftSignedOnly = 1; SetProcessMitigationPolicy(ProcessSignaturePolicy, &sp, sizeof(sp)); }
|
生成一下可以看到
首先还是注入一下cs的dll失败
然后注入user32.dll
成功
NtCreateUserProcess直接创建进程
也可疑直接调用NtCreateUserProcess
创建进程,通过设置参数为PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON
来达到添加流程缓解措施的效果
1 2 3 4 5 6 7 8
| DWORD64 policy = PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON;
AttributeList->Attributes[0].Attribute = PS_ATTRIBUTE_MITIGATION_OPTIONS; AttributeList->Attributes[0].Size = sizeof(DWORD64); AttributeList->Attributes[0].ValuePtr = &policy;
HANDLE hProcess, hThread = NULL; NtCreateUserProcess(&hProcess, &hThread, PROCESS_ALL_ACCESS, THREAD_ALL_ACCESS, NULL, NULL, NULL, NULL, ProcessParameters, &CreateInfo, AttributeList);
|
检测
使用powershell
可以看到当前MicrosoftSignedOnly
标志的进程
1
| get-process | select -exp processname -Unique | % { Get-ProcessMitigation -ErrorAction SilentlyContinue -RunningProcesses $_ | select processname, Id, @{l="Block non-MS Binaries"; e={$_.BinarySignature|select -exp MicrosoftSignedOnly} } }
|
弊端
有一些EDR拥有微软签名,其dll就能够注入到开启了blockdlls
保护的进程,如@SEKTOR7 Institute发现的Crowdstrike Falcon
就可以不受影响,那么我们还可以通过ACG
来进行保护
ACG
ACG即漏洞利用保护参考,其作为一个可选功能添加进了Windows操作系统中,它可以用来检测和防止下列情况的出现
现有代码被恶意修改
向一个数据段中写入并执行代码
为了实现这两个目标,ACG会强制执行这条规则:内存不能同时拥有写入权限和执行权限。更通俗点来说,开启了 ACG 保护的进程,就不能再用 VirtualProtect
、VirtualAlloc
等来获得 PAGE_EXECUTE_READWRITE
的内存,这里不赘述ACG的原理了,这里我们探究其实现
要开启ACG
,使用到的是SetProcessMitigationPolicy
这个API,第一个参数指定要设定的缓解策略类型,第二个参数根据第一个参数指定不同的 Policy 数据,第三个参数指定第二个参数的长度
1 2 3 4 5
| BOOL SetProcessMitigationPolicy( [in] PROCESS_MITIGATION_POLICY MitigationPolicy, [in] PVOID lpBuffer, [in] SIZE_T dwLength );
|
支持的缓解策略如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| typedef enum _PROCESS_MITIGATION_POLICY { ProcessDEPPolicy = 0, ProcessASLRPolicy = 1, ProcessDynamicCodePolicy = 2, ProcessStrictHandleCheckPolicy = 3, ProcessSystemCallDisablePolicy = 4, ProcessMitigationOptionsMask = 5, ProcessExtensionPointDisablePolicy = 6, ProcessControlFlowGuardPolicy = 7, ProcessSignaturePolicy = 8, ProcessFontDisablePolicy = 9, ProcessImageLoadPolicy = 10, MaxProcessMitigationPolicy = 11 } PROCESS_MITIGATION_POLICY, *PPROCESS_MITIGATION_POLICY;
|
这里我们通过代码开启ACG
1 2 3 4 5
| PROCESS_MITIGATION_DYNAMIC_CODE_POLICY policy; ZeroMemory(&policy, sizeof(policy)); policy.ProhibitDynamicCode = 1;
SetProcessMitigationPolicy(ProcessDynamicCodePolicy, &policy, sizeof(policy))
|
我们首先在不开启ACG
之前用VirtualAlloc
申请一块RWX
内存,然后再开启ACG
,再使用VitualAlloc
申请一块内存能否申请成功,再使用VirtualProtect
看能否更改内存属性,实现代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| BOOL ACG() { STARTUPINFOEX si; DWORD oldProtection;
PROCESS_MITIGATION_DYNAMIC_CODE_POLICY policy; ZeroMemory(&policy, sizeof(policy)); policy.ProhibitDynamicCode = 1;
void* mem = VirtualAlloc(0, 1024, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (mem == NULL) { printf("[!] RMX memory alloc failed!\n"); } else { printf("[*] RWX memory address : %p\n", mem); }
printf("[*] Now running SetProcessMitigationPolicy to apply PROCESS_MITIGATION_DYNAMIC_CODE_POLICY\n");
if (SetProcessMitigationPolicy(ProcessDynamicCodePolicy, &policy, sizeof(policy)) == false) { printf("[!] SetProcessMitigationPolicy failed\n"); return FALSE; }
mem = VirtualAlloc(0, 1024, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (mem == NULL) { printf("[!] RMX memory alloc failed!\n"); } else { printf("[*] RWX memory address : %p\n", mem); }
void* ntAllocateVirtualMemory = GetProcAddress(LoadLibraryA("ntdll.dll"), "NtAllocateVirtualMemory");
if (!VirtualProtect(ntAllocateVirtualMemory, 4096, PAGE_EXECUTE_READWRITE, &oldProtection)) { printf("[!] Failed change memory to RMX!\n"); } else { printf("[*] Changed memory to RMX successfully!\n"); } }
|
可以看到在没有开启ACG
的情况下内存可以申请成功,开启ACG
之后申请内存失败,使用VirtualProtect
也不能够更改内存属性
我们知道一般EDR对可疑程序进行监控一般都会采用往程序里注入到想检测的进程中,通过hook一些敏感的3环API来判断程序是否进行一些恶意操作,我们知道一般内存不会拥有可执行权限,那么当EDR如果要想挂钩API函数,就需要通过VirtualProtect
来更改内存属性,那么这时候如果将木马开启ACG
保护,就可疑免受EDR的监控,即使EDR的dll拥有微软的签名
检测
这里用到GetProcessMitigationPolicy
这个API
1 2 3 4 5 6
| BOOL GetProcessMitigationPolicy( [in] HANDLE hProcess, [in] PROCESS_MITIGATION_POLICY MitigationPolicy, [out] PVOID lpBuffer, [in] SIZE_T dwLength );
|
主要看第二个参数,我们这里检测ProcessDynamicCodePolicy
和ProcessSignaturePolicy
首先OpenProcess
打开句柄
1
| HANDLE pHandle = OpenProcess(PROCESS_QUERY_INFORMATION, false, pid);
|
然后调用GetProcessMitigationPolicy
检测策略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| GetProcessMitigationPolicy(pHandle, ProcessDynamicCodePolicy, &dynamicCodePolicy, sizeof(dynamicCodePolicy)); if (dynamicCodePolicy.ProhibitDynamicCode) { printf("[%s] - ProhibitDynamicCode\n", exe); }
if (dynamicCodePolicy.AllowRemoteDowngrade) { printf("[%s] - AllowRemoteDowngrade\n", exe); }
if (dynamicCodePolicy.AllowThreadOptOut) { printf("[%s] - AllowThreadOptOut\n", exe); }
|
检查DLL加载策略同理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| GetProcessMitigationPolicy(pHandle, ProcessSignaturePolicy, &signaturePolicy, sizeof(signaturePolicy));
if (signaturePolicy.AuditMicrosoftSignedOnly) { printf("[%s] AuditMicrosoftSignedOnly\n", exe); }
if (signaturePolicy.AuditStoreSignedOnly) { printf("[%s] - AuditStoreSignedOnly\n", exe); }
if (signaturePolicy.MicrosoftSignedOnly) { printf("[%s] - MicrosoftSignedOnly\n", exe); }
if (signaturePolicy.MitigationOptIn) { printf("[%s] - MitigationOptIn\n", exe); }
if (signaturePolicy.StoreSignedOnly) { printf("[%s] - StoreSignedOnly\n", exe); }
|
这里通过进程名获取PID、提权的函数在这里就不赘述了,看下实现效果,可以看到有一些进程启用了ProcessDynamicCodePolicy
看一下我们之前写的ACG
程序,也是开启了保护的