Java反序列化漏洞一
概述
序列化与反序列化
反序列化漏洞是web安全中老生常谈的问题,提及序列化与反序列化首先有几个问题可以探讨一下:
- 为什么需要序列化与反序列化?
- 序列化与反序列化支持传递哪些信息?
- 序列化后的数据以什么样的格式传递?以何种方式解析?
下面逐一回答。
1.为什么需要序列化与反序列化?
(1)持久化:对象是存储在内存中的堆区的,但是如果内存停止使用了,对象也不存在了。序列化可以将对象转化成字节序列,可以写进硬盘文件中实现持久化。在其它的内存空间中可以读取字节序列进行反序列化成对象。
(2)网络传输:网络直接传输数据,但是无法直接传输对象,可在传输前序列化,传输完成后反序列化成对象。所以所有可在网络上传输的对象都必须是可序列化的。
2.序列化与反序列化支持传递哪些信息?
主要包含了对象的类型信息、对象的数据等。一般来讲对于一个对象,可序列化的信息仅有成员属性以及变量类型,不能序列化方法。但有一个例外就是python的pickle模块,pickle序列化字节流中包含opcode,opcode可以实现方法,因此pickle序列化可以传递方法。
3.序列化后的数据以什么样的格式传递?以何种方式解析?
在不同语言环境下,对象经过序列化后可以有多种格式,简单列举几个:
- php序列化字符串
- Java序列化字节流
- pickle序列化字节流(opcode)
- json、yaml、xml
Java中的序列化与反序列化
writeObject()&readObject()
漏洞成因
原生反序列化
代码中出现了不安全的原生反序列化接口readObject()
就会造成反序列化漏洞,在不同的依赖环境下反序列化利用链也各不相同,常见的可能造成原生反序列化漏洞的危险依赖见项目:ysoserial。
1 |
|
实际中的反序列化漏洞例如shiro反序列化、weblogic反序列化、dubbo反序列化都属于原生反序列化漏洞。
dubbo需要开启NativeJava才能执行原生反序列化,开启的方式便是最新CVE的一个BUG。
其它协议
除了原生反序列化之外,还有一些项目支持面向对象的二进制序列化与反序列化,比如**Hessian协议**。它使用HessianInput/HessianOutput
、Hessian2Input/Hessian2Output
、BurlapInput/BurlapOutput
这些方法对对象进行封装自传输。
@TODO 关于Hessian反序列化后续会专门写个小文章学习。
CC链前置知识
xxxTransfomer
在org.apache.commons.collections.functors
这个package中有很多xxxTransformer,简单介绍几个。
- InvokerTransformer:利用反射的方式执行代码,需要调用
transform()
触发。 - ChainedTransformer:组合多个Transformer,前一个回调的结果作为后一个回调的输入。
- ConstantTransformer:将任一类转换为
ConstantTransformer
返回。 - InstantiateTransformer:其
transform()
会反射调用任意类构造器的newInstance()
。
利用xxxTransformer执行Runtime.getRuntime.exec('clac.exe')
的代码如下:
1 |
|
TemplateImpl
1 |
|
在ClassLoader中我们提到,利用defineClass()可以加载字节码,TemplateImpl.newTransfomer()
允许我们加载任意字节码,调用顺序是:
newTransformer()
getTransletInstance()
defineTransletClasses()
ClassLoader.defineClass()
具体实现需要注意:
_name
不为空_bytecodes
存放字节码_tfactory
必须是TransformerFactoryImpl类- 加载的类必须是AbstractTranslet的子类
动态代理
动态代理是设计模式中代理模式的一种,代理模式的定义是:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。动态代理使用反射机制在运行时为对象添加代理接口。
1 |
|
Common-Collections利用链
总览
对CC链做一个简单的汇总,入口指原生反序列化调用readObject()的类,出口指下一步链接至触发点的类,触发点指触发执行的类。
入口
- AnnotationInvocationHandler(CC1、CC3)
- PriorityQueue(CC2、CC4)
- BadAttributeValueExpException(CC5、CC11)
- HashMap(CC6、CCK1/2/3/4)
- HashTable(CC7、CC9)
- HashSet(CC6、CC10)
- TreeBag(CC8)
出口
- LazyMap.get() => x.transform()
- CC1、CC3、CC5、CC6、CC7、CC9、CC10、CC11、CCK1/2/3/4
- TransformingComparator.compare() => x.transform()
- CC2、CC4、CC8
触发点
Transformers
主要是触发xxxTransformer.transform()方法,不同xxxTransformer的transform()方法有不同的效果。
ChainedTransformer.Transform()
:反射执行Runtime.exec()。InstantiateTransformer.transform()
触发TrAXFilter实例化调用newTransformer() => templates- 见CC3
Templates
前面我们提到要利用TemplatesImpl加载字节码需要调用TemplatesImpl.newTransformer()
,除了借助TrAXFilter外,TemplatesImpl中还有一个getOutputProperties()方法,注意它是getattr方法,因此可以与一些利用链结合。
1 |
|
一些小点
LazyMap.decorate()方法可以修饰任意Map的子类,比如HashMap、TiedMapEntry。
1
public static java.util.Map decorate( Map map, Transformer factory)
- 注意:返回的LazyMap被调用get()方法后会触发map.put()、factory.transform()
对于ChainedTransformer要先置入fake-chain,构造完成再填入,以免在序列化的过程中本地触发。
CC1
- DEP:commons-collections:3.1
- IN:AnnotationInvocationHandler.readObject()
- OUT:LazyMap.get()
- SINK:transformers-chain
1
2
3
4
5
6AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()
ChainedTransformer.transform()
<transformers-chain>
CC2
- DEP:commons-collections4:4.0
- IN:PriorityQueue.readObject()
- OUT:TransformingComparator.compare()
- SINK:InvokerTransformer.transform() || Templates
1
2
3
4
5
6
PriorityQueue.readObject()
...
TransformingComparator.compare()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
CC3
- DEP:commons-collections:3.1
- IN:AnnotationInvocationHandler.readObject()
- OUT:LazyMap.get()
- SINK:Templates
1
2
3
4
5
6
7
8AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()
InstantiateTransformer.transform()
TrAXFilter.newInstance()
TransformerImpl.newTransformer()
<Templates>
CC4
- DEP:commons-collections4:4.0
- IN:PriorityQueue.readObject()
- OUT:TransformingComparator.compare()
- SINK:Templates
1
2
3
4
5
6
7PriorityQueue.readObject()
...
TransformingComparator.compare()
InstantiateTransformer.transform()
TrAXFilter.newInstance()
TransformerImpl.newTransformer()
<Templates>
CC5
- DEP:commons-collections:3.1
- IN:BadAttributeValueExpException.readObject()
- OUT:LazyMap.get()
- SINK:transformers-chain
1
2
3
4
5BadAttributeValueExpException.readObject()
TiedMapEntry.toString()
LazyMap.get()
ChainedTransformer.transform()
<transformers-chain>
CC6
- DEP:commons-collections:3.1
- IN:HashMap.readObject() || HashSet.readObject()
- OUT:LazyMap.get()
- SINK:transformers-chain
1.HashMap版本(Lite)
1
2
3
4
5
6
7
HashMap.readObject()
HashMap.hash()
TiedMapEntry.hashCode()
TiedMapEntry.getValue()
LazyMap.get()
ChainedTransformer.transform()
<transformers-chain>
2.HashSet版本
1
2
3
4
5
6
7
8
HashSet.readObject()
HashMap.put()
HashMap.hash()
TiedMapEntry.hashCode()
TiedMapEntry.getValue()
LazyMap.get()
ChainedTransformer.transform()
<transformers-chain>
CC7
- DEP:commons-collections:3.1
- IN:Hashtable.readObject()
- OUT:LazyMap.get()
- SINK:transformers-chain
1
2
3
4
5
6
7Hashtable.readObject()
Hashtable.reconstitutionPut()
AbstractMapDecorator.equals()
AbstractMap.equals()
LazyMap.get()
ChainedTransformer.transform()
<transformers-chain>
CC8
- DEP:commons-collections4:4.0
- IN:TreeBag.readObject()
- OUT:TransformingComparator.compare()
- SINK:Templates
1
2
3
4
5
6
7org.apache.commons.collections4.bag.TreeBag.readObject
org.apache.commons.collections4.bag.AbstractMapBag.doReadObject
java.util.TreeMap.put
java.util.TreeMap.compare
org.apache.commons.collections4.comparators.TransformingComparator.compare
InvokerTransformer.transform -> TransformerImpl.newTransformer()
<Templates>
CC9
- DEP:commons-collections:3.1
- IN:Hashtable.readObject()
- OUT:LazyMap.get()
- SINK:transformers-chain
1
2
3
4
5
6java.util.Hashtable.readObject
java.util.Hashtable.reconstitutionPut
org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
org.apache.commons.collections.map.LazyMap.get()
<transformers-chain>
CC10
- DEP:commons-collections:3.2.1
- IN:HashSet.readObject()
- OUT:LazyMap.get()
- SINK:Templates
1
2
3
4
5
6
7
8
java.util.HashSet.readObject()
java.util.HashMap.put()
java.util.HashMap.hash()
org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
org.apache.commons.collections.map.LazyMap.get()
InvokerTransformer.transform() -> TransformerImpl.newTransformer()
<Templates>
CC11
- DEP:commons-collections:3.2.1
- IN:BadAttributeValueExpException.readObject()
- OUT:LazyMap.get()
- SINK:Templates
modify from Payload CC5.
1
2
3
4
5BadAttributeValueExpException.readObject()
TiedMapEntry.toString()
LazyMap.get()
InvokerTransformer.transform() -> TransformerImpl.newTransformer()
<Templates>
CCK1/2
- DEP:commons-collections:<=3.2.1 || commons-collections4:4.0
- IN:HashMap.readObject()
- OUT:LazyMap.get()
- SINK:Templates
For shiro-exploit. MemShell Injection.
1
2
3
4
5
6HashMap.readObject
TiedMapEntry.hashCode
TiedMapEntry.getValue
LazyMap.get
InvokerTransformer.transform -> TransformerImpl.newTransformer()
<Templates>
CCK3/4
- DEP:commons-collections:<=3.2.1 || commons-collections4:4.0
- IN:HashMap.readObject()
- OUT:LazyMap.get()
- SINK:Templates
For shiro-exploit.
1
2
3
4
5HashMap.readObject
TiedMapEntry.hashCode
TiedMapEntry.getValue
LazyMap.get
<transformers-chain>