Weblogic曾出现过多个与XMLDecoder相关的漏洞(CVE-2017-3506、CVE-2017-10352、CVE-2019-2725)。
Weblogic针对于漏洞的修复,往往是增加黑名单以限制xml中的元素,例如针对CVE-2017-3506的修复方法,限制了object标签使得攻击者无法创建指定类的实例。但是攻击者可以使用void、new来代替object,从而造成了CVE-2017-10352漏洞的产生。
那为什么在XMLDecoder解析时,可以使用void、new来代替object呢?本文将详细的分析XMLDecoder代码,以找到答案。
基础概念
XMLDecoder用于将XMLEncoder创建的xml文档内容反序列化为一个Java对象,简单的案例可见官方给出的下图代码
首先,我们写一个简单的测试demo TestStudent类
TestStudent类中将完成从XMLEncoder到XMLDecoder的过程
Student类如下图
首先看下XML编码以及写入文件过程,见下图红框处
在程序将Student类的实例在XML编码后写入student.xml,student.xml文件如下图
接下来,程序将读取student.xml文件内容,并进行xml反序列化
XMLDecoder反序列化
为了研究xmldecoder是如何进行反序列化,以及文章开头我们提出的问题,我们跟一下代码
在readObject方法上下断,如下图
之后的调用栈特别的深,从我们TestStudent中的readObject到DocumentHandler类中的startElement方法。具体可以见下图
之所以我们要在DocumentHandler中的startElement下断点,是因为DocumentHandler继承自DefaultHandler。DefaultHandler是使用SAX进行XML解析的默认Handler。因此DocumentHandler相当于xml解析的开始,而startElement是开始标签处理函数,包括属性的添加,因此我们从开始标签处理处着手跟入。
但是在正式跟入前,我们首先看下DocumentHandler的构造方法
上图构造方法中,定义了可以解析的xml元素以及它们对应的使用的解析器
关于这点,我们正式跟入startElement中的断点,从下图startElement中看到
在this.handlers列表中,这里包含了各个xml元素对应使用的解析器
对照我们的xml文件
第一个标签是java
Java对应的解析器是JavaElementHandler
我们先跳过java标签不看,直接看下一个解析的标签
下一个标签就是重要的object标签
根据对应关系,进入ObjectElementHandler解析器,见下图
从上图可见ObjectElementHandler 继承了 NewElementHandler类
而NewElementHandler类,正是new标签对应的解析类,见下图
这里已经涉及到为什么可以用new代替object的部分原因了,但是不是确凿证据。记住这里的继承关系,我们接着看ObjectElementHandler类
在ObjectElementHandler类中,存在一个addAttribute方法,见下图
addAttribute的作用是在解析对应标签时,为标签对象添加相应的属性
这里我们解析的时object标签,而我们的object标签中只有class属性,见下图
当然,如果有idref、field、index、method等等,程序会将其值赋值给对应的属性
例如下图,object有index属性
那么这里的this.index就为”xxx”
回到正文,由于上述属性我们的xml object里都没有,直接进入下图断点
调用父类NewElementHandler的addAttribute方法
NewElementHandler中addAttribute方法为对象添加class属性
这样看来,需要继承NewElementHandler类的标签解析器,对应的标签默认都有class属性。因为这些标签默认都有class属性,因此直接继承NewElementHandler类中addAttribute方法对class属性进行赋值,这样就不用一一重写了。
为了证明一下我的观点,我们跳出测试xml文件,直接看一下weblogic的漏洞利用的poc
可见java标签、object标签、array标签,都有class属性
array标签的解析器也继承了NewElementHandler
由于java标签要处理version,于是自己实现了class标签处理而非继承NewElementHandler
回到调试流程中,在NewElementHandler的addAttribute方法中进行class属性赋值
经过class赋值后,object标签的tpye属性已经为我们的Student类对象
接下来,程序将逐一为我们的xml里的标签进行addAttribute属性赋值操作,从java标签到object标签到void等等
当DocumentHandler中的startElement这个过程结束后,程序将调用endElement方法,endElement方法为结束标签处理函数,见下图
endElement方法中将调用getValueObject方法获取每一个标签所产生的对象对应的ValueObject实例
我们直接跳转到对object标签的处理
从上图可见,程序直接通过Expression生成实例。这里对应生成了我们的Student类的实例
XMLDecoder反序列化的流程就是上文分析的这样,那为什么可以用new、代替object呢?
为什么可以用new替换object
还记不记得上文这里
当解析object标签,用的是ObjectElementHandler解析器,而ObjectElementHandler继承了NewElementHandler。更重要的是,在将object中class属性进行赋值时,用的仍然是NewElementHandler中的addAttribute方法。New标签直接使用NewElementHandler进行解析。因此二者最终进行我们Student类实例的赋值操作是完全一样的
为什么可以用void替换object
经过上文的分析,这个问题更简单了,我们看下void标签的解析器
void标签的解析器是VoidElementHandler,这个解析器直接继承了ObjectElementHandler,并且仅仅重写了isArgument方法,而这个方法在我们反序列化利用的过程中并无影响
为什么可以用method替换object
这个问题也比较简单,我们看下method标签的解析器
可见与object一样,也是继承了NewElementHandler类。与此同时,于object类中的addAttribute方法中类似,也是使用NewElementHandler类中的addAttribute方法处理class属性。Method与Object标签解析器实现的方式几乎一摸一样,因此可以用method代替object