XStream反序列化组件攻击分析

0x00 XStream组件功能

XStream可以轻易的将Java对象和xml文档相互转换,而且可以修改某个特定的属性和节点名称,而且也支持json的转换。

值得注意的是:

  • 它转换对象时,不需要对象继承Serializable接口。 这极大的方便了反序列化攻击。

XStream简单序列化代码如下:

可以看到,XStream可以很方便地java对象转换为xml文件,生成文件如下:

亦可方便的将xml文件反序列化为java对象:

0x01 Groovy-CVE-2015-3253漏洞(影响范围1.7.0-2.4.3)

使用了XStream库的应用有很多,Jenkins是其中一个,于是就有了CVE-2016-0792。而这个CVE使用了Groovy进行payload攻击。即:CVE-2016-0792的攻击方式。详细分析请看参考资料1,分析的非常好。

下面是我简要调试梳理的简要过程:

1.发现Sink

对于一个漏洞利用,必然有一个敏感的Sink。它可以类或者函数等,它的作用是执行命令或者读写文件等敏感操作。可以被攻击者所利用,去做一些事情。这个漏洞的Sink就是一个MethodClosure闭包类:

看一下类的描述,可以知道是可以使用其调用对象的方法,并且继承了Closure类。而其doCall方法,它直接使用反射机制调用了我们的任意对象方法。并且对象和方法名都是我们可以通过构造函数传入的。继续看父类(Closure):

调用父类(Closure)的call方法即可自动调用子类的doCall方法。于是,如下代码即可执行弹出计算器:

说明:

  • 无法控制方法的参数(args),只能通过调用call(参数)来实现,因此利用的局限性比较大。只能找寻一个对象具有无参方法,来进行利用。

2.自动触发

Expando类中,发现了Closure.call方法的调用。而且是在hashCode方法中:

现在只要想办法进行自动调用hashCode方法即可:

这里用到了Map数据结构的特性:

Map是一种key-value类型的数据结构,所以Map集合不允许有重复key。
所以每次在往集合中添加键值对时会去判断key是否相等,那么在判断是否相等时会调用key的hashCode方法。

注:hashCode方法是返回一个独一无二的hash值(int型),去代表这个唯一对象。如果返回值相等,则说明两个对象一样。

于是,我们只需要反序列化一个Map对象即可,然后像里面put构造好的恶意key。可以看到常用的HashMap类中,即存在调用hashCode方法:

于是有了以下测试自动触发的程序:

这样即可顺利弹出计算器。

0x02 XStream反序列化触发及生成payload

首先通过以上测试代码,可以得到一个执行链,使用上述分析生成payload,我还是进行封装了下,可以使用如下代码:

上述代码,可以返回一个String类型的xml格式payload,代表之前生成的对象。将字符串写到文件中,即可看到生成的payload:

使用简单的读取操作,然后单步调试,即可看到:
在XStream反序列过程中,正好对于Map的处理存在put操作,默认即为使用HashMap实现类。这是跟踪调试payload到最后的代码时的情况:

当XStream处理到map.put(expando, 123)操作时,即执行calc命令。弹出计算机,测试结果为:

0x03 动态代理payload

直接看代码:

这段代码只有两行,运行后,即可执行calc命令,弹出计算器。详细来源请看参考资料3。

  • 首先,我们了解到TreeSet这个数据结构是有序排列的。

如果我们自己定义的一个类的对象要加入到TreeSet当中,那么这个类必须要实现Comparable接口。

通过实现Comparable接口的compareTo方法,来进行对象比较操作。而我们正是传入了Comparable接口,有了compareTo方法。

  • 这里使用了java.beans.EventHandler这个对象,它实现了InvocationHandler接口,可用于动态代理。

它在这里代理了Comparable接口,对其的所有方法操作,均转换为执行EventHandlerinvoke方法。
说明:

  • XStream不需要对象继承Serializable接口的特性,再次发挥了作用。这里EventHandler依然没有实现序列化接口。

跟踪源码,可以看到其调用任意对象方法的过程,这里不赘述。记住java有这么个好用的类即可。

然而,XStream在这个payload出现后,对EventHandler进行特殊检查,新版本的XStream无法使用。

给出payload:

新版本需要显式反序列化以上payload,才能成功执行命令,代码如下:

0x04 Jenkins利用

Jenkins满足以上两个漏洞产生条件:

  • 使用了XStream和Groovy
  • 存在使用XStream反序列化的接口

于是,就有了漏洞攻击,简单来看payload,即可明白:

使用payload_1:


对于payload_2,我试了下,并没有成功。会抛出异常。报错,不支持<dynamic-proxy>:

0x05 参考及源码

http://drops.wooyun.org/papers/13243
https://www.contrastsecurity.com/security-influencers/serialization-must-die-act-2-xstream?platform=hootsuite
http://www.pwntester.com/blog/2013/12/23/rce-via-xstream-object-deserialization38/
https://github.com/angelwhu/XStream_unserialization