Drunkmars's Blog

浅谈EDR绕过

字数统计: 1.6k阅读时长: 7 min
2022/05/14

我们知道一般EDR对可疑程序进行监控一般都会采用往程序里注入到检测的进程中,通过hook一些敏感的3环API来判断程序是否进行一些恶意操作,那么我们可以通过添加流程缓解措施和漏洞利用保护参考来实现保护,从而防止EDR的dll注入对进程进行检测

blockdlls

在cs3.14版本过后引入了blockdlls命令,用于保护beacon生成的任何子进程不加载非 Microsoft 签名的 dll

image-20220514200908639

这里使用监听新增一个子会话

image-20220514203519372

可以看到rundll32.exe进程有了Signatures restricted (Microsoft only)标志

image-20220514203515093

UpdateProcThreadAttribute

cs的实现在UpdateProcThreadAttribute函数,UpdateProcThreadAttributeAttribute参数0x20007实际上解析为PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY,而0x100000000000解析为PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON。因此,cs 在这里所做的是使用CreateProcessAPI 调用以及STARTUPINFOEX包含缓解策略的结构,在这种情况下,用于阻止非 Microsoft 签名的 DLL

image-20220514200234928

自己通过代码实现

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
// Blockdlls.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <windows.h>

int main()
{
STARTUPINFOEXA si;
PROCESS_INFORMATION pi;
SIZE_T size = 0;
BOOL ret;

// Required for a STARTUPINFOEXA
ZeroMemory(&si, sizeof(si));
si.StartupInfo.cb = sizeof(STARTUPINFOEXA);
si.StartupInfo.dwFlags = EXTENDED_STARTUPINFO_PRESENT;

// Get the size of our PROC_THREAD_ATTRIBUTE_LIST to be allocated
InitializeProcThreadAttributeList(NULL, 1, 0, &size);

// Allocate memory for PROC_THREAD_ATTRIBUTE_LIST
si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(
GetProcessHeap(),
0,
size
);

// Initialise our list
InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &size);

// Enable blocking of non-Microsoft signed DLLs
DWORD64 policy = PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON;

// Assign our attribute
UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &policy, sizeof(policy), NULL, NULL);

// Finally, create the process
ret = CreateProcessA(
NULL,
(LPSTR)"C:\\Windows\\System32\\cmd.exe",
NULL,
NULL,
true,
EXTENDED_STARTUPINFO_PRESENT,
NULL,
NULL,
reinterpret_cast<LPSTARTUPINFOA>(&si),
&pi
);
}

实现效果如下

image-20220514201702616

直接进行注入则报错

image-20220514201813898

SetProcessMitigationPolicy

这个api可以给当前线程添加 Signatures restricted (Microsoft only) 标识

image-20220514213957237

代码如下

1
2
3
4
5
6
void blockdll_thread()
{
PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY sp = {};
sp.MicrosoftSignedOnly = 1;
SetProcessMitigationPolicy(ProcessSignaturePolicy, &sp, sizeof(sp));
}

生成一下可以看到

image-20220514202058247

首先还是注入一下cs的dll失败

image-20220514202841964

然后注入user32.dll成功

image-20220514202949912

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);

image-20220514233952740

检测

使用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} } }

image-20220514203145571

弊端

有一些EDR拥有微软签名,其dll就能够注入到开启了blockdlls保护的进程,如@SEKTOR7 Institute发现的Crowdstrike Falcon就可以不受影响,那么我们还可以通过ACG来进行保护

image-20220514222640449

ACG

ACG即漏洞利用保护参考,其作为一个可选功能添加进了Windows操作系统中,它可以用来检测和防止下列情况的出现

  1. 现有代码被恶意修改

  2. 向一个数据段中写入并执行代码

为了实现这两个目标,ACG会强制执行这条规则:内存不能同时拥有写入权限和执行权限。更通俗点来说,开启了 ACG 保护的进程,就不能再用 VirtualProtectVirtualAlloc 等来获得 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也不能够更改内存属性

image-20220514221816379

我们知道一般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
);

主要看第二个参数,我们这里检测ProcessDynamicCodePolicyProcessSignaturePolicy

image-20220514231058690

首先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

image-20220514224105140

看一下我们之前写的ACG程序,也是开启了保护的

image-20220514224129101

CATALOG
  1. 1. blockdlls
    1. 1.1. UpdateProcThreadAttribute
    2. 1.2. SetProcessMitigationPolicy
    3. 1.3. NtCreateUserProcess直接创建进程
    4. 1.4. 检测
    5. 1.5. 弊端
  2. 2. ACG
    1. 2.1. 检测