Java反序列化漏洞研究
漏洞原理
java序列化就是把对象转换成字节流,便于保存在内存、文件、数据库中;反序列化即逆过程,由字节流还原成对象。当反序列化的输入来源于程序外部,可以被用户控制,恶意用户便可以构造恶意的字节流,经反序列化之后得到精心构造的恶意对象。
也就是说一些java应用的数据在网络中传输,我们把里面的序列化数据抠出来,替换成我们的payload,应用程序接收之后把payload进行反序列化,构造的恶意功能(如命令执行)就被执行了。
实际过程中直接忽略PCA的存在,hacker先于PCB完成一些前期的交互。
那么问题来了,如何构造能执行任意命令的payload?需要依赖于什么条件? 哪些应用会在网络中传输序列化的数据?这些数据有什么特征?
序列化与反序列化原理
上述代码说明了反序列化的过程,Java中通过writeObject()函数对对象进行序列化,将有结构的数据转换成为无结构的二进制串。 通过readObject()函数将二进制串反序列化还原成对象。
执行到12行时生成了一个String类型的对象。
执行到16行已经将序列化数据写入到object.db中了,前面的四个字节AC ED 00 05开头,后面会经常用到。
执行到24行,完成反序列化过程,将对象还原,此时对象id不同,其他均相同。
构造执行任意命令的payload
下面的代码能快速的体验payload是如何构造的。
程序1的RunInvo类,构造了一个MAP,再用它构造AnnotationInvocationHandler对象,将这个对象序列化到payload.bin中。
程序2读取payload.bin中的数据,通过readObject()进行反序列化,代码得到执行,弹出计算器,如下图所示。
代码执行关键点:AnnotationInvocationHandler,TransformedMap.decorate,InvokerTransformer
从序列化数据传入readObject()开始解释为什么要依赖于这几个关键点。
首先在序列化和反序列化的过程中,每个类都有自己的readObject与writeObject对应,很多类自己重写了自己的readObject与writeObject函数,String类有自己的readObject方式,AnnotationInvocationHandler也有自己的readObject方式。而AnnotationInvocationHandler的readObject实现调用了setValue()函数,这一特性将会被TransformedMap.decorate用到。
TransformedMap的decorate(Map map, Transformer keyTransformer, Transformer valueTransformer)函数,会将Map {key:value}按后面两个传入的函数参数来进行转换,当key或者value改变时(调用setValue),decorate就会被触发,而Transformer可以是一个调用链ChainedTransformer。这个调用链是通过InvokerTransformer精心构造的。
InvokerTransformer函数只需要传入方法名,参数类型,参数即可调用任意函数。通过这个函数构造调用链能够执行Runtime.exec()。而InvokerTransformer存在于Apache Commons Collections的库中,因此目标应用中需要有这个库,函数才能得到执行。
Payload的构造与触发流程如下图所示
几个关键函数原型可参考http://www.slideshare.net/codewhitesec/exploiting-deserialization-vulnerabilities-in-java-54707478
只有InvokerTransformer依赖于Apache Commons Collections库,其他函数都是通用的java库,所以payload执行的依赖条件只需目标java应用包含了Apache Commons Collections库即可。
jboss下的利用
jboss的问题存在于/invoker/JMXInvokerServlet ,利用比较简单直接发送payload即可
POC代码如下
执行结果
其特征即为AC ED 00 05(序列化的标识)
Jenkins下的利用
Jenkins有远程接口调用的地方存在jenkins-cli.jar对远程命令的下发处。
通过抓包分析出交互流程如下
1)客户端向服务器请求端口列表
2)服务器返回一些端口列表
3)客户端与X-Jenkins-CLI-Port端口建立连接
4)客户端与服务器协商连接类型,X-Jenkins-CLI-Port为非安全模式,X-Jenkins-CLI2-Port为安全模式,数据用密文传输,默认使用X-Jenkins-CLI2-Port。 这里POC程序需要修改为CLI模式
5)服务器返回welcome,与base64编码的序列化对象
6)客户端发送这个对象,在服务端执行,这里就是利用点,将payload用base64编码,替换掉原来的base64序列化对象
7)客户端发送后续的一些附加数据(可抓包获得后面的数据)
因此POC需要构造前面的交互,获取cli端口(服务器随机生成的),选择Protocol:CLI-connect模式(明文传输),然后发送<===[JENKINS REMOTING CAPACITY]===> + base64(payload) + append数据。
简单的弹计算器POC效果
其特征为rO0AB(AC ED 00 05的base64值)
检测特征&其他应用漏洞挖掘方法
Java的序列化数据在报文中的特征比较明显,开头一定会带上AC ED 00 05。但是有些报文是加密传输的,对于复杂的加密无法直接从报文提取特征。但是对用base64这种简单的编码也是能提取出特征的。
Base64加密的可能情况(base64将3字节转换为4字节3×8bit=4×16bit):
AC ED 00 05在开头,按3,1分组ACED00=>rO0A,05xxxx=>BXXX 结果rO0ABxxx 特征:rO0AB
AC ED 00 05在中间,按2,2分组xxACED=>xxzt, 0005xx=>AAxx, 结果xxztAAxx 特征:ztAA
AC ED 00 05在中间,按1,3分组xxxxAC=>xxxs, ED0005=>7QAF 结果xxxs7QAF 特征:s7QAF
目前大部分情况ACED0005在开头,特征为rO0AB。 也有可能序列化对象与其他字段混合在一起再进行base64加密,所以理论来说后面两种情况也是有可能的,只是概率较低。
对于有流量的厂商可以监控数据中是否包含AC ED 00 05,或者rO0AB等特征。如果发现序列化特征可以进行攻击防护,或者0day挖掘。思路如下: