登陆 注册

fastjson 1.2.68 反序列化漏洞 gadget 的一种挖掘思路

CDL路飞 2020-06-10 安全文摘漏洞挖掘

关于漏洞

fastjson 的这个新漏洞在 1.2.68 及之前版本的 autotype 关闭的情况下仍然可以绕过限制反序列化,相比 1.2.47 版本的漏洞来讲这个版本的漏洞还是有一些限制的(关于 1.2.47 漏洞可以参考我的另一篇文章《Java 反序列化漏洞始末(3)— fastjson》[1]),例如 1.2.47 是可以绕过黑名单的限制的,而这个漏洞则无法绕过黑名单,并且需要类实现 AutoCloseable 接口。目前主要的 JNDI gadget 已经进了黑名单,还不允许反序列化类实现了 ClassLoader、DataSource、RowSet 接口,这就导致了绝大部分的 JNDI gadget 无法利用,所以本篇文章主要分享一下 gadget 的挖掘思路和漏洞的原理分析。

漏洞分析

这个漏洞的的成因和我的另一篇文章《fastjson 1.2.68 最新版本有限制 autotype bypass》[2]一致,都是由于期望类(expectClass)导致的,这个漏洞的期望类范围更大,更容易找到具有危害的 gadget。

首先看 DefaultJSONParser#parseObject 这里将 @type 指定的类作为条件去获取 Deserialzer 对象。

ParserConfig#getDeserializer 方法中不满足条件所以到了最后一步通过ParserConfig#createJavaBeanDeserializer 方法来构造 JavaBeanDeserializer

在这一步创建了 JavaBeanDeserializer 对象,而漏洞也就发生在 JavaBeanDeserializer 类中。

现在回到 DefaultJSONParser#parseObject 应该走下一步 JavaBeanDeserializer#deserialze 方法。

用期望类的思路,可以找到此处有两个方法使用了 ParserConfig#checkAutoType 且指定了期望类。

一个是 deserialzeArrayMapping() 另一个是 deserialze()

在 deserialze() 方法中又做了一次 checkAutoType 检测,此处直接将第二个 @type 的类名,和前面构造 JavaBeanDeserializer 对象时指定的期望类直接传了进来。

我在 《fastjson 1.2.68 最新版本有限制 autotype bypass》[2] 这篇文章提到过,当 checkAutoType(String typeName, Class<?> expectClass, int features) 方法的 typeName 实现或继承自 expectClass,就会通过检验。

但还有三个问题,会阻碍 gadget 的触发。

boolean expectClassFlag;
if (expectClass == null) {
    expectClassFlag = false;
} else if (expectClass != Object.class && expectClass != Serializable.class && expectClass != Cloneable.class && expectClass != Closeable.class && expectClass != EventListener.class && expectClass != Iterable.class && expectClass != Collection.class) {
    expectClassFlag = true;
} else {
    expectClassFlag = false;
}

第一个问题是期望类的黑名单,里面包括了大部分常用的父接口和父类,却唯独少了一个 java.lang.AutoCloseable。这也就是为什么 AutoCloseable 为什么可以通过校验的第一个原因,第二个原因是TypeUtils#mappings里有 AutoCloseable 类。

第二个问题是黑名单类,fastjson 在 denyHashCodes 里几乎把常见的容易造成漏洞的类都加进了黑名单,这就造成了攻击成本变高,如果要利用漏洞,只能花费更多的时间去寻未被发现的常用库 gadget。

第三个问题是父类、父接口黑名单,fastjson 在判断期望类之前将继承自 ClassLoader、DataSource、RowSet 的类直接抛出异常。

if (ClassLoader.class.isAssignableFrom(clazz) || DataSource.class.isAssignableFrom(clazz) || RowSet.class.isAssignableFrom(clazz)) {
    throw new JSONException("autoType is not support. " + typeName);
}

而常用的 JNDI RCE 类基本上都继承自 DataSource 和 RowSet,所以能找到的 JNDI gadget 基本都无法在这个漏洞中使用。

以上三点足够让大部分常见的 gadget 无法使用,所以需要换一种 gadget 挖掘思路。

gadget

关于 gadget 的挖掘思路我主要是寻找关于输入输出流的类来写文件,IntputStream 和 OutputStream 都是实现自 AutoCloseable 接口的,而且也没有被列入黑名单,所以只要找到合适的类,还是可以进行文件读写等高危操作的。

JNDI

前面说到,这个漏洞基本无法使用 JNDI,实际上并不完全是,当 fastjson 小于 1.2.51 时,还是可以通过实现了 RowSet 接口的类进行 JNDI 反序列化,但实际上已经没有什么危害,大多数都更新到了 1.2.60+ 版本。

文件读写

我寻找 gadget 时的条件是这样的。

•需要一个通过 set 方法或构造方法指定文件路径的 OutputStream•需要一个通过 set 方法或构造方法传入字节数据的 OutputStream,并且可以通过 set 方法或构造方法传入一个 OutputStream,最后可以通过 write 方法将传入的字节码 write 到传入的 OutputStream•需要一个通过 set 方法或构造方法传入一个 OutputStream,并且可以通过调用 toString、hashCode、get、set、构造方法调用传入的 OutputStream 的 flush 方法

以上三个组合在一起就能构造成一个写文件的利用链,最终我挑选出了三个符合条件的类作为演示。

漏洞复现

漏洞的原理摸清了,接下来我将用本地的模拟环境复现一遍漏洞。

写文件

复制文件

总结

本文旨在给安全研究者提供一种反序列化漏洞 gadget 挖掘思路,文中 POC 代码暂不公开。

修复方案

•更新到 1.2.69 或更高版本•未能更新到 1.2.69 版本的请将 AutoCloseable、OutputStram、InputStream、RowSet 列入反序列化黑名单

References

[1] 《Java 反序列化漏洞始末(3)— fastjson》: https://b1ue.cn/archives/184.html
[2] 《fastjson 1.2.68 最新版本有限制 autotype bypass》: https://b1ue.cn/archives/348.html

文由微信公众号安全档案

请发表您的评论
请关注微信公众号
微信二维码
不容错过
Powered By HeiBaiTeam.