Fastjson 1.2.22-1.2.24 反序列化漏洞学习
fastjson 是一个性能很好的 Java 语言实现的 JSON 解析器和生成器,来自阿里巴巴的工程师开发
Fastjson 1.2.22-1.2.24 反序列化漏洞
首先构建项目,我使用的是 Maven,在 pom.xml 里添加以下字段,并进行同步操作。为了方便分析,这里使用 Maven 将代码和文档也下载下来。
<dependencies>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.24</version>
</dependency>
</dependencies>
构造 Luna 类
public class Luna {
private String name;
private String position;
public Luna() {
System.out.println("Luna class constructor");
}
public String getName() {
System.out.println("Luna getName()");
return name;
}
public void setName(String name) {
System.out.println("Luna setName()");
this.name = name;
}
public String getPosition() {
System.out.println("Luna getPosition()");
return position;
}
public void setPosition(String position) {
System.out.println("Luna setPosition()");
this.position = position;
}
}
使用 JSON.toJSONString
将其序列化数据输出
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
public class Ser {
public static void main(String[] args) {
Luna luna = new Luna();
luna.setName("露娜一号");
luna.setPosition("打野");
String jsonString = JSON.toJSONString(luna, SerializerFeature.WriteClassName);
System.out.println(jsonString);
}
}

SerializerFeature.WriteClassName
是toJSONString
设置的一个属性值,设置之后在序列化的时候会多写入一个@type
,即写上被序列化的类名,type
可以指定反序列化的类,并且调用其getter/setter/is
方法
当没添加 SerializerFeature.WriteClassName
时

接下来将他们解析,不含 WriteClassName
时

包含 WriteClassName
时

可以看到,第一个在不加上类型的时候不会触发构造方法、get/set
方法;而在第二个中 parse
触发了 set
方法,parseObject
触发了 get/set
两个方法。那么如果在 get/set
中加入恶意操作呢?
将 Luna 类中的 setName() 作如下更改
public void setName(String name) {
System.out.println("Luna setName() 此方法中有恶意操作");
try {
Runtime.getRuntime().exec("/System/Applications/Calculator.app/Contents/MacOS/Calculator");
} catch (IOException e) {
throw new RuntimeException(e);
}
this.name = name;
}
再将得到的序列化数据进行解析,成功触发命令执行

对 JSON.parse(jsonString)
下断点,进入调试,关键处
if (key == JSON.DEFAULT_TYPE_KEY && !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {
String typeName = lexer.scanSymbol(symbolTable, '"');
Class<?> clazz = TypeUtils.loadClass(typeName, config.getDefaultClassLoader());
此处 typeName
为 @type
中定义的 类,接着调用 TypeUtils.loadClass
加载类型

首先会在 mapping
中获取传入的 @type
这里找不到,于是使用 ClassLoader
加载类
try {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
if (contextClassLoader != null) {
clazz = contextClassLoader.loadClass(className);
mappings.put(className, clazz);
return clazz;
}
} catch (Throwable e) {
// skip
}
调试如下

完成后进行如下操作:
ObjectDeserializer deserializer = config.getDeserializer(clazz);
return deserializer.deserialze(this, clazz, fieldName);
虽然 getDeserializer
有黑名单机制,但仅过滤了 Thread

继续跟进到 getContext()
后调用了 Luna
的方法

随后进入 setName
执行了定义好的命令

在调试过程中发现了 L
、[
这样的检测字符,与后面的1.2.25 - 1.2.47各种绕过姿势有关。本文大量参考:[https://xz.aliyun.com/t/8979](