XMLDecoder 反序列化之 class 标签
在学习 CVE-2019-2725 时看到文章中说构造的 xml 需要在一行才能成功
1 |
|
那么为什么需要这样构造呢,这跟calss
标签特性有关,我们先来看下在 class 开始标签与 string 开始标签之间换行会怎样
1 |
|
这样使用 XMLDecoder 反序列化时会报错找不到这个类

跟踪调试,在解析 class 标签后,会调用com.sun.beans.decoder.DocumentHandler#characters
方法,来添加标签中的字符数据

可以看到这里将换行符和空格添加了进去(因为 xml 换行时编辑器会自动进行缩进,所以带了空格,如果把缩进删除,则只添加换行)

解析 string 闭合标签时,会进入com.sun.beans.decoder.ElementHandler#endElement
方法中

append 之后就变成了\n java.net.Socket

在解析 void 闭合标签时,会进入到com.sun.beans.decoder.ObjectElementHandler#getValueObject
方法,跟进getContextBean()
方法

最终会来到com.sun.beans.decoder.ClassElementHandler#getValue
方法中,可以很清晰看出为什么会报ClassNotFoundException

那么如果改成如下这样呢?
1 |
|
依然会报找不到类的错误

只要保证如下这样就可以了,因为 class 标签只会将其标签下的字符和其直接 string 子标签中的字符添加到与之对应的this.sb
中
1 |
|
那为什么包含类的 string 标签后面一定要添加void
标签呢?如果去掉后面的 void 标签,直接跟其他标签行不行?上面的 xml 好理解,如果将 void 标签去掉,那么这些 string 标签中的字符就会直接拼接在一起了,导致成如下这样

那么如果是下面这个 xml 呢?这个也是CVE-2019-2725
中用到的一个 poc
1 |
|
如果将 void 标签去掉,后面直接跟 property 标签呢,这样也杜绝了找不到类的问题
1 |
|
结果会报如下错误,简单来说就是在类中找不到 property 标签指定的方法

我们来跟踪调试一下
在解析 property 标签中的第一个 string 闭合标签时先进入com.sun.beans.decoder.ElementHandler#endElement
方法

继续往下跟进,进入com.sun.beans.decoder.PropertyElementHandler#setValue
方法中,在这里要先调用getContextBean
方法,来获取 property 父标签的 value

先跟进com.sun.beans.decoder.ElementHandler#getContextBean

这里会调用com.sun.beans.decoder.StringElementHandler#getValueObject
,因为 ClassElementHandler 没有这个方法,所以会调用其父类 StringElementHandler 的 getValueObject 方法,在这里面先调用com.sun.beans.decoder.StringElementHandler#getValue
方法查找类,然后返回 this.value

然后回到 getContextBean 方法中,返回 value 值

然后来到com.sun.beans.decoder.PropertyElementHandler#setPropertyValue
方法,在getContextBean
方法中返回的是个 Class 实例,并不是 JdbcRowSetImpl 类的实例,那么这里再 getClass 获取到的自然就是java.lang.Class

跟进com.sun.beans.decoder.PropertyElementHandler#findSetter
方法,该方法就是用来查找指定类与 property 标签中 name 属性值对应的 setter 方法

通过com.sun.beans.decoder.PropertyElementHandler#getProperty
方法来进行搜索,这里传入的是java.lang.Class
自然不可能查找到setDataSourceName
方法

那为什么添加 void 标签后就可以呢,原因在于getValueObject
方法的不同,添加后这里 property 标签的父标签就变成了 void

VoidElementHandler 类没有getValueObject
方法,所以会调用其间接父类 NewElementHandler 的无参 getValueObject
方法,然后在这里又会调用到其直接父类 ObjectElementHandler 的 getValueObject 方法

在com.sun.beans.decoder.ObjectElementHandler#getValueObject
方法中也是先调用com.sun.beans.decoder.NewElementHandler#getContextBean
方法

但是在该方法的下面会通过反射 new 一个 JdbcRowSetImpl 实例

此时在com.sun.beans.decoder.PropertyElementHandler#setPropertyValue
方法中 getClass 获得的就是com.sun.rowset.JdbcRowSetImpl
类而不是java.lang.Class
了
