二、XStream简介
三、历史漏洞
四、Gadget挖掘思路
五、总 结
XStream是一款针对XML和Java对象转换而开发的工具库,由于其本身的一些特点,成为了这类漏洞挖掘里一个很典型的例子,因此本文针对XStream进行Gadget挖掘分析。本文更多的是分享Gadget挖掘时的思路,进而可以在其他类型的序列化中进行类似的思考和尝试。
从Java对象到XML:
Source
→Gadget
→Sink
。
对于原生的Java反序列化来说,可以利用的Java类是任意实现了Serializable
的类,入口是ObjectInputStream
的readObject
方法。
对于XStream来说,可以利用的Java是任意类,入口是XStream的fromXML
方法。
以一个经典的CVE-2013-7285来说,PoC如下,执行的sink
是ProcessBuilder.start()
Converter
,用于对不同的标签进行转化。
sorted-set
标签对应的是TreeSetConverter
,它的代码逻辑中会调用TreeMap
的put
方法,而TreeMap
的put
方法会调用方法的compare
方法对传入Map
的对象进行判断对象应该存放的位置。
java.beans.EventHandler
对象。在 Java 中,动态代理对象是一种在运行时创建的代理对象,它实现了InvocationHandler
接口的invoke
方法,从而将方法调用按照动态代理的invoke
的实现逻辑进行转发。
hashCode
、equals
以及toString
这些方法时,会执行其target
属性的对象及指定方法。因此在执行到这个动态代理对象的compare
方法时,它实际上会执行其target
属性ProcessBuilder
的start
方法,也就是任意命令执行。
XStream对于此漏洞的补丁也很简单,默认禁用了java.beans.EventHandler
这个动态代理类。
java.beans.EventHandler
这个动态代理类被禁,那么是否存在其他动态代理类,也具有这样的这样可以执行任意方法的类,毕竟动态代理类本身设计就是用来在一个方法调用时,定向调用其他的方法,如果invoke
方法中对传入方法或者类检测不严格,那么就很容易产生任意方法执行,并且jdk中有非常多的动态代理实现,因此可以尝试挖掘。
把jdk的类利用tabby生成图,执行如下语句:
invoke
方法为source
,可控的Method.invoke
为sink
,并且不包含com.sun.org.glassfish
包中的类,因为这个包中存在多个实现类似的invoke
方法,对传入类做了严格限制,无法利用,为了方便排查,屏蔽这个包的结果:
sun.reflect.annotation.AnnotationInvocationHandler
类。看过Java反序列化的同学一定认识这个类,因为这个类就是原生反序列化中很经典的一条利用链Jdk7u21
。
Jdk7u21
的利用链路,既然它在原生反序列化中可以使用,那么对于适用范围更广的XStream来说,也很有可能可以用。
验证起来也很简单,直接将jdk7u21
生成的对象toXML
,然后再调用fromXML
,就会发现是可以触发代码执行的。
TemplatesImpl.getOutputProperties
方法进行字节码加载。当然这里也可以选择和CVE-2013-7285一样的ProcessBuilder.start
,只需要把TemplatesImp
标签替换为ProcessBuilder
就可以了。
sorted-set
标签,而是linked-hash-set
,原因是这条链中用到的AnnotationInvocationHandler
必须触发其equals
方法才能进入equalsImpl
,最终触发任意代码执行。
AnnotationInvocationHandler
和EventHandler
的特性非常相似,那么Java原生反序列化链是否也可以使用EventHandler
?答案是否定的,原因也很简单,就是前面提到的可以被反序列化的类的类型存在限制。
Method.invoke
调用,会发现这些method
都不可控或者无法利用,因此继续分析3层利用:
com.sun.corba.se.spi.orbutil.proxy.CompositeInvocationHandlerImpl
。
CompositeInvocationHandlerImpl
其实就是一个动态代理的封装。
可以看到此时出现了一个sun.tracing.ProviderSkeleton
,并且浅看代码,在invoke
中是没有过滤的。
invoke
函数代码如下:
var2.invoke
和下方的triggerProbe
,var2
是动态代理的方法,因此是不可控的,仅剩的一条利用链调用如下:
implementing_method
和proxy
都是DTraceProbe
的属性,符合反序列化的要求,因此只需要按照需要构造出一个ProviderSkeleton
对象就可以。构造的代码如下, 使用Unsafe
构造对象可以避免生成很多用不到的属性,从而污染输出的xml。
linkedhashset
,即可在其实现中调用对象的hashCode
方法,从而进入动态代理的invoke
函数,最终触发任意方法执行。在本例中选择的是processBuilder.start
,同样的也可以使用TemplatesImpl.getOutputProperties
进行代码代码执行。事实上这就是XStream CVE-2021-39149的挖掘过程。
CompareTo
、toString
、hashCode
等方法进行展开,找到一些潜在的特定格式的特定参数可控的方法执行后,再进一步填入不同的类,进行漏洞的完整利用。这种寻找Gadget的方法在Weblogic、Websphere、Dubbo hessian等固定存在序列化入口,通过黑名单进行防御的漏洞挖掘中常常被用到,不同的只是对类的限制、要求不同,在看清真实的需求的情况下,利用自动化工具一步步寻找,就可以有效地进行Gadget挖掘。
未经作者同意,不得转载
原文始发于微信公众号(奇安信天工实验室):Java XStream 反序列化:Gadget 挖掘思路分享
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论