Java反序列化漏洞研究

Java反序列化漏洞研究

  1. 漏洞原理

    java序列化就是把对象转换成字节流,便于保存在内存、文件、数据库中;反序列化即逆过程,由字节流还原成对象。当反序列化的输入来源于程序外部,可以被用户控制,恶意用户便可以构造恶意的字节流,经反序列化之后得到精心构造的恶意对象。

    也就是说一些java应用的数据在网络中传输,我们把里面的序列化数据抠出来,替换成我们的payload,应用程序接收之后把payload进行反序列化,构造的恶意功能(如命令执行)就被执行了。

    实际过程中直接忽略PCA的存在,hacker先于PCB完成一些前期的交互。

    那么问题来了,如何构造能执行任意命令的payload?需要依赖于什么条件? 哪些应用会在网络中传输序列化的数据?这些数据有什么特征?

    1. 序列化与反序列化原理

    上述代码说明了反序列化的过程,Java中通过writeObject()函数对对象进行序列化,将有结构的数据转换成为无结构的二进制串。 通过readObject()函数将二进制串反序列化还原成对象。

    执行到12行时生成了一个String类型的对象。

    执行到16行已经将序列化数据写入到object.db中了,前面的四个字节AC ED 00 05开头,后面会经常用到。

    执行到24行,完成反序列化过程,将对象还原,此时对象id不同,其他均相同。

    1. 构造执行任意命令的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库即可。

  2. jboss下的利用

    jboss的问题存在于/invoker/JMXInvokerServlet ,利用比较简单直接发送payload即可

     

    POC代码如下

    执行结果

     

    其特征即为AC ED 00 05(序列化的标识)

  3. 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值)

  4. 检测特征&其他应用漏洞挖掘方法

    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挖掘。思路如下:

posted @ 2016-01-12 16:04  alert123  阅读(2275)  评论(0编辑  收藏  举报