免责声明:该⽂章仅供安全学习和技术分享,请勿将该⽂章和⽂章中提到的技术⽤于违法活动上,切勿在⾮授权状态 下对其他站点进⾏测试,如产⽣任何后果皆由读者本⼈承担,与猩红实验室⽆关!如有侵权,联系删除,转载请注明出处,感谢!
0x00 从上海回来之后一直在忙着考驾驶证等等乱七八糟的生活琐事,在加上hvv的不顺心,一直没有静下心来写点东西,最近秋招陆续开始了,担心找不到工作的我下定决心开始发奋图强,从今天开始恢复更新了。
0x01 Java反序列化漏洞产生背景
0x01-1 什么是序列化和反序列化
序列化:将对象的状态信息转换为字节序列,写入临时或持久性存储区。
反序列化:从存储区中读取该数据,并将其还原为对象。
简单举个例子,两个进程通信的时候彼此之间需要传递数据,无论传递哪种类型的数据,最终都会以二进制的形式在网络上发送,发送放要将这个java对象序列化,接收方收到数据后把数据反序列化为java对象。Web程序通常是使用已经写好的类和方法进行反序列化的,那么我们让它使用我们恶意构造的类进行反序列化,或者通过构造特定的恶意对象序列化后的字节流,让目标进行反序列化,是不是就可以达到一些不可告人的目的呢?
0x01-2 序列化和反序列化的方法
在原生java中,使用writeObject()进行序列化,使用readObject()进行反序列化。writeObject()允许开发者在序列化流中插入一些自定义数据,与php中的魔术方法_wakeup类似。
0x01-3 为什么会存在这么多反序列化漏洞
1、很多站点和RMI仓库等接口的地方都会有反序列化的功能。
2、Apache commons collections包的广泛使用。
0x02 关于序列化和反序列化的实现
想要实现对象反序列化,首先要实现java.io.Serializable接口(内部序列化),还有一个名为java.io.Externalizable接口,它继承了Serializable接口,实现Externalizable接口的类完全由自身来控制反序列化的行为,而实现 Serializable 接口的类既可以采用默认的反序列化方式,也可以自定义反序列化方式。那么从这里也可以看的出来,想要利用反序列化的前提是参与序列化的所有类都要继承了Serializable,即所有类都是可序列化的。
但是我在复现的时候发现,这个java.io.Serializable接口是一个空接口,代码如下:
public interface Serializable {
}
那么这个接口存在的意义是什么呢,百度发现这个接口仅仅表示这个类可以进行序列化,而实现了Serializable接口的类原则上都需要生产一个serialVersionUID常量,反序列化时如果双方的serialVersionUID不一致会导致InvalidClassException 异常。如果可序列化类未显式声明 serialVersionUID,则序列化运行时将基于该类的各个方面计算该类的默认serialVersionUID值。
0x03 InvokerTransformer反射链
在 commons-collections包中存在 InvokerTransformer类,其中存在一个 transform()函数,它可以将一个对象转换成另一个对象,利用该函数可以执行任意方法。同时该包还实现了一个TransformedMap类,该类是对Java标准数据结构Map接口的一个扩展。该类可以在一个元素被加入到集合内时,自动对该元素进行特定的修饰变换,具体的变换逻辑由Transformer类定义,Transformer在TransformedMap实例化时作为参数传入。
当TransformedMap中的任意项的Key或者Value被修改,相应的Transformer的transform()方法就会被调用。除此以外,多个Transformer还能串起来,形成ChainedTransformer。 于是我们可以利用java的反射机制,通过该反射链获得Runtime类来执行系统命令。
0x03-1 重写readObject()方法直接触发
如果某个可序列化的类重写了readObject()方法,并且在readObject()中对Map类型的变量进行了键值修改操作,并且这个Map参数是可控的,就可以实现我们的攻击目标了。
AnnotationInvocationHandler ,这个类有一个成员变量 memberValues 是Map<String,Object>类型,并且在重写的 readObject() 方法中有 memberValue.setValue() 修改Value的操作。于是我们可以实例化一个AnnotationInvocationHandler类(使用TransformedMap类的decorate方法来实例化),将其成员变量memberValues赋值为精心构造的恶意TransformedMap对象。然后将其序列化,提交给未做安全检查的Java应用。Java应用在进行反序列化操作时,执行了readObject()函数,修改了Map的Value,则会触发TransformedMap的变换函数transform(),再通过反射链调用了Runtime.getRuntime.exec("XXX") 命令,最终就可以执行我们的任意代码了。
0x04 CC链的区别
CC链的修改基本上都类似CC1和CC6,大多是在入口类与执行类上做的改变。
0x04-1 ysoserial中的CC1
大致思路与上面意义,区别在于开头处,通过动态代理的方式,调用了 AnnotationInvocationHandler.invoke()→ LazeMap.get()从而调用了 transform()方法。
0x04-2 CC2
TransformingComparator.compare()→InvokerTransformer.transform()→ TemplatesImpl.newTransformer()的调用链,没有使用 Transform数组。
0x04-3 CC3
CC3将执行方式有反射换成了动态类加载,将ChainedTransformer.transform()→ InvokerTransformer.transform()换成了ChainedTransformer.transform()→ InstantiateTransformer.transform()。
0x04-4 CC4
CC4是在Common-Collections4使用的链,它和CC2使用相同的入口类:PriorityQueue.readObject()→ TransformingComparator.compare()。
0x04-5 CC6
CC5和CC7差不多,而且不好用,就不写了
CC6CC6这条链是 ysoserial中泛用性最好的一条链,它不受 jdk版本的限制,而且他的入口类HashMap是一个很常用的类,几乎不会被过滤。它与CC1的区别在于后半段,入口类与CC1相同,将LazeMap作为TiedMapEntry的map,TiedMapEntry作为HashMap的key,但调用反序列化时,便会调用到 LazeMap.get()。
——————end——————
欢迎各位师傅添加本人微信一起交流学习
微信号Jiuwandd
原文始发于微信公众号(猩红实验室):浅谈JAVA反序列化原理
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论