AV_EDR对抗痛点
参考链接:https://www.bilibili.com/video/BV1dH8GzDErF/
思路来源: @RedCore_Moriaty
前言
EDR的主要闪光点并非在拦截,而是它的行为分析和语言模型。面对EDR的围追堵截,维持权限已经越做越难。
对应C2 开发-二线红队与EDR的对抗强度决定一线红队的驻扎与打点
在现在这种对抗强度巨大的情景下,一线红队的目标应该专注于既定目标的完成,而不是权限的长期维持。 可以类比你上去给特朗普一巴掌,就要做好被安保摁住的准备,但其实在这样的防护下,能够完成这个艰巨的任务就已经很不容易了
因此二线红队,也就是C2开发,本身要做的核心任务就是给一线创造空间,破开最大的时间窗口。 也就是将精力投入到对抗的环节上
详叙
核心概念:进程注入链_延迟对抗
这里其实本质上就是在讲一件事情:如何用”奇技淫巧”(误)拖缓EDR的分析时间并达成为一线拉开达成既定目标的时间窗口
手法概览
传统思路1:内存加载
C2通过远程控制下发 Beacon Payload,利用内存函数申请内存,将加密的 Payload 解密后直接放进内存,并执行,实现 无落地、隐蔽执行。
1. 利用内存分配函数 API
常见的 Windows API 有:
VirtualAlloc
VirtualAllocEx
NtAllocateVirtualMemory
HeapAlloc
它们的目的是:在当前或远程进程的内存空间中申请一块内存区域,并指定权限(如 PAGE_EXECUTE_READWRITE
)以支持后续执行。
2. 为 Beacon 分配内存并加载解密
一般 Beacon 是一个加密后的 Payload(二进制 blob),有如下特征:
- 已加密(避免特征被查杀)
- 存在于内存中,未写入磁盘(避免落地)
- 加载时需要解密(如 XOR/AES/RC4)
过程是:
- 用
VirtualAlloc
申请内存 - 将加密的 Beacon(Shellcode)解密成明文
- 把解密后的内容写入申请的内存地址
3. 修改执行(跳转)
内存中的 Payload 并不会自动执行,需要控制程序流跳转到它那执行:
常见方式:
CreateThread
/NtCreateThreadEx
QueueUserAPC
+Sleep
SetTimer
Callback- 栈迁移 +
jmp
指令跳转(Reflective DLL 中常见)
目标:让程序跳到你申请的内存执行 Beacon 逻辑。
这个现在根本行不通,不用说后期日志取证,在执行阶段就已经被拦截。
为何行不通,阐述防御机制
这和windows内存页自身的属性有关
正常进程加载的模块是 MEM_IMAGE 类型内存页,自己手动申请的内存则是 MEM_PRIVATE。这个差异是蓝队检测内存注入、反射加载的基础指标之一。
只需要能监控到你执行的代码区域,只需要你落到MEM_PRIVATE,因此软件基于非MEM_IMAGE 类型内存页执行代码的判断就非常迅速且简单
这也叫做代码执行区审查
传统思路2:Module Stomping对抗代码合法审查检测
将合法模块加载到内存中后,覆盖其 .text
段或入口函数处的代码,用作恶意代码的跳板执行,借助其 MEM_IMAGE
属性掩盖真正 Payload 的行为
1. 调用 LoadLibraryA("合法模块.dll")
- 加载合法 DLL(比如
mshtml.dll
、duser.dll
等) - 一般我们会使用**
LoadLibrary
函数去动态加载DLL
,但是这会出现一个问题,通过LoadLibrary
函数去动态加载DLL
会受到控制流(CFG
)的限制。**CFG
会组织执行未经过签名的和验证的代码。 - 这里可以使用
NtCreateSection
函数以及NtMapViewOfSection
函数来手动映射DLL
文件,这可以确保映射的内存区域为SEC_IMAGE
权限。
2. 用 VirtualProtect
修改其 .text
段权限为 PAGE_EXECUTE_READWRITE
- 或者直接操作 PEB 中的模块入口点(更隐蔽)
3. 将你的 Beacon Shellcode 写入 .text
区段 或 DllMain
典型鸠占鹊巢,把合法模块原始逻辑覆盖掉
.text
部分是通常包含了可执行的代码,那么攻击者就可以覆盖其这部分代码,将合法的代码替换为shellcode
。
4. 调用模块导出函数 或 创建线程执行入口点
- 实际执行的就是你写入的 Beacon,而看上去是合法模块的行为
对应传统思路1的进阶反制
之前我们是通过VirtualAlloc
申请私有内存,将shellcode
写入到其中。而MoudleStumping是通过劫持合法dll, 篡改合法内存执行代码。.text
区域是 MEM_IMAGE
类型 → 不在 EDR 的 MEM_PRIVATE+EXECUTE
监控范围内
没有新模块注入 → dlllist
, pe-sieve
, memory scanner
等不会报告新增模块
所在模块路径存在于磁盘 → 无法一眼看出是注入模块
EDR防御机制(基于业务与排查方法阐述
通过EDR的角度来看,从日志而言是肯定能观测到鸠占鹊巢的行为,但正常系统的dll也存在这种覆写行为,因此EDR需要结合其他维度进行排查
因此免杀的目的,其实就是拉高EDR的阈值,为一线人员争取更多完成既定任务的时间。
这里拿出一个情境来说:
作为EDR而言,在一个生产环境中,用户每日都在进行打开WPS,Outlook,打印等流程。基于这个行为制作出了对应的行为模型和标杆,那么你作为外部测试人员,哪怕是通过Moudle Stumping鸠占鹊巢的手段达成了驻留的目的,但你在里面执行命令的操作势必就犹如在地铁上刷登机牌一样,对于EDR日常模拟出的用户行为模型是明显的不正常行为
EDR with VBS防御机制
在 Win10 之后(特别是 Win11 默认开启),VBS 指的是:
通过 Hyper-V 虚拟化技术实现的内核隔离机制,用来强化 Credential Guard、HVCI、Kernel Code Integrity 等功能。
VBS 与 EDR 的整合:做了哪些事?
功能 | 作用 | 对红队影响 |
---|---|---|
HVCI(Hypervisor-protected Code Integrity) | 强制所有内核代码都要经过签名校验 | 拒绝加载未签名或伪造签名的驱动 |
Credential Guard | 将LSASS运行在 VSM(隔离容器)中 | 传统 mimikatz 无法直接读内存 |
Kernel-mode Code Integrity(KMCI) | 加强驱动签名验证与运行时保护 | 加载 BYOVD 驱动时易被拦截/拒绝 |
KDP(Kernel Data Protection) | 保护某些内核对象和结构不可修改 | Patch SSDT/EDR回调 失败率提高 |
EDR Sensor Isolation | 将 EDR 驱动模块运行在 VTL1(虚拟化信任层) | 红队无法在 VTL0(正常内核)中卸载/patch 探针 |
简言之:你即使能提权拿到 SYSTEM,甚至能注入内核,也无法“真正”对抗驻留在 VBS 支持下的 EDR 内核组件。
在 Windows 11 中,EDR 利用 VBS 做的最关键事情是**:加强对内核代码完整性、驱动签名验证、EDR 模块隔离的控制**,导致红队几乎无法使用传统 BYOVD 手法加载未签名或漏洞驱动来做内核 Patch 或卸载。
注解:BYOVD手法 也就是利用内核驱动关闭杀软进程
参考:奇安信攻防社区-BYOVD技术实战:利用内核驱动关闭杀软进程
因此在VBS机制的保护下,硬刚EDR其实是损耗比很大的工作,与其追求卸载杀软进程,不如从绕和驻留的角度进一步入手这个思路。
EDR_ETW订阅机制
引申:为什么我们在当前用户权限下,无法影响/patch EDR 内部的 ETW 订阅机制?
ETW是**一种Windows内核和应用程序级的事件跟踪机制,允许捕获系统运行时的事件,**EtwEventWrite函数是用于生成这些事件的函数。当恶意软件或攻击者希望隐藏进程的活动或避免监控时,他们可能会使用ETW补丁技术来拦截或修改EtwEventWrite函数,从而阻止进程生成ETW事件。
相对的 阐述一下BypassETW手法(如图)
但在当前情境下,我们通过BypassETW实现规避监控是很困难的,可以通过logman query providers观察到微软内置的一些监控进程。 可以看到哪怕没有EDR,微软自带的威胁情报也能监控的死死的。