浅谈EDR绕过


前言

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

blockdlls

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

image-20220514200908639.png

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

image-20220514203519372.png

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

image-20220514203515093.png

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.png

自己通过代码实现

// Blockdlls.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

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

实现效果如下

image-20220514201702616.png

直接进行注入则报错

image-20220514201813898.png

SetProcessMitigationPolicy

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

image-20220514213957237.png

代码如下

void blockdll_thread()
{
    PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY sp = {};
    sp.MicrosoftSignedOnly = 1;
    SetProcessMitigationPolicy(ProcessSignaturePolicy, &sp, sizeof(sp));
}

生成一下可以看到

image-20220514202058247.png

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

image-20220514202841964.png

然后注入user32.dll成功

image-20220514202949912.png

NtCreateUserProcess直接创建进程

也可疑直接调用NtCreateUserProcess创建进程,通过设置参数为PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON来达到添加流程缓解措施的效果

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.png

检测

使用powershell可以看到当前MicrosoftSignedOnly标志的进程

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.png

弊端

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

image-20220514222640449.png

ACG

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

  1. 现有代码被恶意修改

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

为了实现这两个目标,ACG会强制执行这条规则:内存不能同时拥有写入权限和执行权限。更通俗点来说,开启了 ACG 保护的进程,就不能再用 VirtualProtectVirtualAlloc等来获得 PAGE_EXECUTE_READWRITE的内存,这里不赘述ACG的原理了,这里我们探究其实现

要开启ACG,使用到的是SetProcessMitigationPolicy这个API,第一个参数指定要设定的缓解策略类型,第二个参数根据第一个参数指定不同的 Policy 数据,第三个参数指定第二个参数的长度

BOOL SetProcessMitigationPolicy(
  [in] PROCESS_MITIGATION_POLICY MitigationPolicy,
  [in] PVOID                     lpBuffer,
  [in] SIZE_T                    dwLength
);

支持的缓解策略如下

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

PROCESS_MITIGATION_DYNAMIC_CODE_POLICY policy;
ZeroMemory(&policy, sizeof(policy));
policy.ProhibitDynamicCode = 1;

SetProcessMitigationPolicy(ProcessDynamicCodePolicy, &policy, sizeof(policy))

我们首先在不开启ACG之前用VirtualAlloc申请一块RWX内存,然后再开启ACG,再使用VitualAlloc申请一块内存能否申请成功,再使用VirtualProtect看能否更改内存属性,实现代码如下

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.png

我们知道一般EDR对可疑程序进行监控一般都会采用往程序里注入到想检测的进程中,通过hook一些敏感的3环API来判断程序是否进行一些恶意操作,我们知道一般内存不会拥有可执行权限,那么当EDR如果要想挂钩API函数,就需要通过VirtualProtect来更改内存属性,那么这时候如果将木马开启ACG保护,就可疑免受EDR的监控,即使EDR的dll拥有微软的签名

检测

这里用到GetProcessMitigationPolicy这个API

BOOL GetProcessMitigationPolicy(
  [in]  HANDLE                    hProcess,
  [in]  PROCESS_MITIGATION_POLICY MitigationPolicy,
  [out] PVOID                     lpBuffer,
  [in]  SIZE_T                    dwLength
);

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

image-20220514231058690.png

首先OpenProcess打开句柄

HANDLE pHandle = OpenProcess(PROCESS_QUERY_INFORMATION, false, pid);

然后调用GetProcessMitigationPolicy检测策略

    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加载策略同理

    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.png

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

image-20220514224129101.png

评论

Drunkmars

这个人很懒,没有留下任何介绍

twitter weibo github wechat

随机分类

神器分享 文章:71 篇
软件安全 文章:17 篇
Exploit 文章:40 篇
密码学 文章:13 篇
Ruby安全 文章:2 篇

扫码关注公众号

WeChat Offical Account QRCode

最新评论

Article_kelp

因为这里的静态目录访功能应该理解为绑定在static路径下的内置路由,你需要用s

N

Nas

师傅您好!_static_url_path那 flag在当前目录下 通过原型链污

Z

zhangy

你好,为什么我也是用windows2016和win10,但是流量是smb3,加密

K

k0uaz

foniw师傅提到的setfge当在类的字段名成是age时不会自动调用。因为获取

Yukong

🐮皮

目录