Java XMLDecoder反序列化分析
简介
XMLDecoder是一套用于对XML进行序列化或反序列化的一套API,它在JDK1.4就已经被开发了出来,它对XML的解析模式并不是更为人所知的DOM解析,而是SAX解析 DOM解析在解析XML时会读取所有数据然后生成DOM树来解析,而SAX则是线性读取XML,所以SAX解析XML性能消耗相对较小
两者区别如下:

在Weblogic中由于was-wast、wls9_async等多个组件使用了XMLDecoder来进行解析xml文档,导致了攻击者可以构造恶意的XML文档来触发反序列化去执行命令,修复方式为采用黑名单禁用object等标签,本文使用jdk7u21去分析XMLDecoder是如何进行操作的
反序列化分析
测试代码如下:
|
|
直接跟进到xmlDecoder.readObject()中,这里的readObject()并非原生的反序列化readObject()只是同名而已
|
|
继续跟进到java.beans.XMLDecoder#parsingComplete中,通过XMLDecoder.this.handler.parse()去解析XMLDecoder.this.input,而input就是封装过的传进去的xml


在com.sun.beans.decoder.DocumentHandler#parse中通过SAX的工厂模式创建实例并且调用SAX解析,而其中DocumentHandler.this是xml对应标签进行解析处理的Handler


继续跟进到com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl#parse中,先进行设置Handler然后继续解析

进入到重载的SAXParserImpl#parse中继续跟进

在AbstractSAXParser#parse中,将inputSource的值赋值给xmlInputSource并继续解析

在XMLParser#parse中,然后使用XML11Configuration#parse继续对其解析

继续往下走然后在XML11Configuration#parse(boolean)中进入到fCurrentScanner.scanDocument()中

在XMLDocumentFragmentScannerImpl#scanDocument中通过do while循环对xml文档进行遍历解析,其中主要解析方法是在next()中实现

最后在com.sun.beans.decoder.DocumentHandler#startElement中开始对第一个开始标签进行解析,第一个标签为<java>,通过this.getElementHandler(var3).newInstance()得到JavaElementHandler的实例并赋值给this.handler,然后对Owner和Parent的值进行设置,接下来在for循环中对标签内部的属性进行遍历取值,其中var4.getQName(var6)是对标签内部的属性名称取值,var4.getValue(var6)是对属性的值进行取值,最后调用addAttribute设置属性


然后继续对object标签进行遍历进行取值赋值,接下来以同样的操作对其它的开始标签进行遍历解析,直到开始解析第一个结束标签</string>

在解析第一个结束标签</string>时,调用this.handler.endElement(),其中StringElementHandler中没有会根据继承关系调用父类ElementHandler的endElement()中

最后在StringElementHandler#getValueObject中将值取出,然后返回一个存有该值的ValueObjectImpl实例

然后在ElementHandler#endElement中,将ValueObjectImpl的值取出交给this.parent的Handler也就是VoidElementHandler,这里是交给<string>的父标签也就是<void>标签,其中是将值存储在了数组中
|
|


接下来处理</void>标签,最后在ObjectElementHandler#getValueObject中,通过this.getContextBean()得到var3的类String,在通过var4 = var2.length == 2 ? "set" : "get"将set赋值给var4,最后new了一个Expression实例,并执行var5.getValue()将存有calc的数组set进去


然后继续处理</array>标签,通过this.parent.addArgument,将存有calc的数组添加到上一标签也就是<object>标签的handlerObjectElementHandler中


接下来处理<void method="start"></void>,在处理</void>时,在ObjectElementHandler#getValueObject中先通过this.getContextBean()得到了new ProcessBuilder("calc"),然后在通过var4 = this.method != null && 0 < this.method.length() ? this.method : "new";的判断将start赋值给var4,然后new了一个Expression实例,最后执行var5.getValue(),此时拼接为new ProcessBuilder("calc").start(),通过反射弹出计算器
