原理
反序列化,核心就是从一个字节流中还原为对象的状态。
JDK 是存储着每一个成员变量的值、类型。反序列化时,递归获取出来,然后调用。
JSON 同理,只不过类似 Jackson 之类的框架,将 Class
作为序列化类型的一部分,从而间接支持多态。
由于扩大了反序列化的范围,因此,当无法限制反序列化的具体类型时,就容易出现问题。
JDK 漏洞
低级漏洞-利用当前存在问题的反序列化类
利用存在的类,直接反序列化对应的类时,触发相关的方法。
高级漏洞-传入不存在的 class
通过已经存在的逻辑中会调用 ObjectInputStream
的类,构造攻击链,来传入不存在的 class
从而进行攻击
com.fr.third.org.apache.commons.collections4.bag.TreeBag#readObject
com.fr.third.org.apache.commons.collections4.bag.AbstractMapBag#doReadObject(Map<>)
- 传入TreeMap
java.util.TreeMap#put
java.util.TreeMap#compare
java.util.Comparator#compare
com.fr.base.core.serializable.SerialableLocalCollator#compare
com.fr.json.JSONArray#toString
- 调用getXXX
java.security.SignedObject#getObject
public Object getObject()
throws IOException, ClassNotFoundException
{
// creating a stream pipe-line, from b to a
ByteArrayInputStream b = new ByteArrayInputStream(this.content);
ObjectInput a = new ObjectInputStream(b);
Object obj = a.readObject();
b.close();
a.close();
return obj;
}
JDK 反序列化漏洞的危险性是成倍提升的。 因为支持了 inputstream
所以可以传入不存在的 class
进行攻击。
总结
JDK 漏洞当满足下面的任意条件即可出现
- 当前存在可以直接利用 逻辑存在问题的
readObject
的类 - 存在 调用
ObjectInputStream
的类
JSON 漏洞
这里只分析 Jackson 的问题, FastJson 直接忽略。臭名昭著。完全不考虑。
原理 enableDefaultTyping
- 操作
com.fasterxml.jackson.databind.ObjectMapper#enableDefaultTyping()
- 输出信息
{'object':["classpath", {"value": "test"}]}
- 反序列化时,就会通过 classpath 反序列化对应的类,并触发
setXX
- 服务端 classpath 存在 任意可以利用的类 ,如
com.sun.rowset.JdbcRowSetImpl
,target 类满足setXX
可控触发 JDNI 注入利用。
原理 JsonTypeInfo.Id.CLASS
com.fr.third.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector
这个类中会获取到该类型的 TypeResolverBuilder
如果这个类是支持 JsonTypeInfo.Id.CLASS
, 那么就会用这个方法去处理对象。
此时存在两种类型。
1、属性不为 Object 类时
要进行反序列化的类的属性所属类的构造函数或 setter 方法本身存在漏洞,当然这种场景开发几乎不会这么写。
2、属性为 Object 类时
当属性类型为 Object 时,因为 Object 类型是任意类型的父类,因此扩大了我们的攻击面,我们只需要寻找出在目标服务端环境中存在的且构造函数或 setter 方法存在漏洞代码的类即可进行攻击利用。
见上面的 Person 中存在对 Object 的反序列化支持。所以 通过下面的方式,即可模拟调用出计算器。
总结
Jackson 满足下面三个条件之一即可开启 Jackson 反序列化漏洞:
- 调用了 ObjectMapper.enableDefaultTyping()函数;
- 存在可以利用的类,比如
com.sun.rowset.JdbcRowSetImpl
- 存在可以利用的类,比如
- 对要进行反序列化的类的属性使用了值为
JsonTypeInfo.Id.CLASS
的@JsonTypeInfo
注解; - 对要进行反序列化的类的属性使用了值为
JsonTypeInfo.Id.MINIMAL_CLASS
的@JsonTypeInfo
注解;
和 JDK 的对比
可以看到,JDK 是从 all classes
里面找到可能存在的漏洞(逻辑漏洞+自定义漏洞)。
但是 Jackson 是有分层的。
如果
- 开启
enableDefaultTypeing
- 对 Object 开启
JsonTypeInfo.Id
的注解 那么等同于 “JDK + 利用存在的逻辑漏洞“
如果仅对必须的业务对象开启开启 JsonTypeInfo.Id
的注解,那么整个的范围立刻就缩小到开启注解的 biz-classes
。整个漏洞的范围一下就缩小到可控。
只要开发人员不对业务逻辑的 get/set/construct
做后门,一般不会存在问题。
最坏最坏,就是将相关的业务对象改写为不需要开启 JsonTypeInfo.Id
的逻辑即可。
综上可以发现,0 << json-属性不为 object << json-属性为 object << JDK
参考
JavaSec Jackjson反序列化漏洞利用原理 | thonsun’s blog
Jackson 反序列化漏洞基本原理 [ Mi1k7ea]
JDK反序列化漏洞