浅谈JAVA反序列化原理

admin 2022年7月22日01:18:25评论44 views字数 3146阅读10分29秒阅读模式
              猩红实验室  欢迎投稿分享交流
         浅谈JAVA反序列化原理
免责声明:该⽂章仅供安全学习和技术分享,请勿将该⽂章和⽂章中提到的技术⽤于违法活动上,切勿在⾮授权状态 下对其他站点进⾏测试,如产⽣任何后果皆由读者本⼈承担,与猩红实验室⽆关!如有侵权,联系删除,转载请注明出处,感谢!


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类定义,TransformerTransformedMap实例化时作为参数传入。


TransformedMap中的任意项的Key或者Value被修改,相应的Transformertransform()方法就会被调用。除此以外,多个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()函数,修改了MapValue,则会触发TransformedMap的变换函数transform(),再通过反射链调用了Runtime.getRuntime.exec("XXX") 命令,最终就可以执行我们的任意代码了


0x04 CC链的区别

CC链的修改基本上都类似CC1CC6,大多是在入口类与执行类上做的改变。


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

CC5CC7差不多,而且不好用,就不写了

CC6CC6这条链是 ysoserial中泛用性最好的一条链,它不受 jdk版本的限制,而且他的入口类HashMap是一个很常用的类,几乎不会被过滤。它与CC1的区别在于后半段,入口类与CC1相同,将LazeMap作为TiedMapEntrymapTiedMapEntry作为HashMapkey,但调用反序列化时,便会调用到 LazeMap.get()



        ——————end——————


            欢迎各位师傅添加本人微信一起交流学习

                           浅谈JAVA反序列化原理

                 微信号Jiuwandd



原文始发于微信公众号(猩红实验室):浅谈JAVA反序列化原理

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年7月22日01:18:25
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   浅谈JAVA反序列化原理http://cn-sec.com/archives/1192881.html

发表评论

匿名网友 填写信息