查看原文
其他

浅谈EDR绕过

Drunkmars 跳跳糖社区 2022-11-05

点击蓝字 / 关注我们

前言

我们知道一般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. 1. 现有代码被恶意修改

  2. 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.pn


推荐阅读:
ETW的攻与防
浅谈Windows传统取证
Learning Linux kernel exploitation P1- aying the groundwork(译文)
JNDI注入分析
域内特权提升 - 将 Certifried 与 KrbRelay 结合利用



跳跳糖是一个安全社区,旨在为安全人员提供一个能让思维跳跃起来的交流平台。

跳跳糖持续向广大安全从业者征集高质量技术文章,可以是漏洞分析,事件分析,渗透技巧,安全工具等等。
通过审核且发布将予以500RMB-1000RMB不等的奖励,具体文章要求可以查看“投稿须知”。

阅读更多原创技术文章,戳“阅读全文

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存