这里主要想写一下fastjson这类万金油漏洞的具体打法 主要也是学习一些java的知识。对这个漏洞的停留我也只是停留在扫描器扫描 然后网上随便复制个poc直接打 迄今为止我也没复现成功过这玩意 所以想着先复现一次rce 然后跟两篇高质量文章 但估计也跟不完 明天还是得给客户完善一下答辩的事情 唉 好难

vulhub-1.2.24

用插件直接扫能发现洞 会自动检测所有数据包 然后进行探测

https://github.com/pmiaowu/BurpFastJsonScan/releases

image-20240713115233870

漏洞分析文章

https://forum.butian.net/share/3055

https://forum.butian.net/share/3096

fastjson的序列化与反序列化

package org.example;
import com.alibaba.fastjson.JSON;

public class Main {

public static void main(String[] args) {
// 将一个 Java 对象序列化为 JSON 字符串
Person person = new Person("Alice", 18);
String jsonString = JSON.toJSONString(person);
System.out.println(jsonString);

// 将一个 JSON 字符串反序列化为 Java 对象
String jsonString2 = "{\"age\":20,\"name\":\"Bob\"}";
Person person2 = JSON.parseObject(jsonString2, Person.class);
System.out.println(person2.getName() + ", " + person2.getAge());
}

// 定义一个简单的 Java 类
public static class Person {
private String name;
private int age;

public Person(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public int getAge() {
return age;
}
}
}

//{"age":18,"name":"Alice"}
//Bob, 20

fastjson的注释

@JSONField注解来指定Java类的属性和JSON字段之间的映射关系

@JSONType(orders = {"name", "age"})来指定属性的序列化顺序

@typefastjson中的一个特殊注解,用于标识JSON字符串中的某个属性是一个Java对象的类型。具体来说,当fastjsonJSON字符串反序列化为Java对象时,如果JSON字符串中包含@type属性,fastjson会根据该属性的值来确定反序列化后的Java对象的类型。

直接使用@type访问java.lang.Runtime来进行rce 这里要注意jdk的版本 太高了和太低了都不行 我这里是idea里面自己下的jdk11

由于fastjson1.2.24之后默认禁用@type,因此这里我们通过ParserConfig.getGlobalInstance().addAccept("java.lang");来开启,否则会报错autoType is not support

package org.example;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import java.io.IOException;

public class Main {
public static void main(String[] args) throws IOException {
String json = "{\"@type\":\"java.lang.Runtime\",\"@type\":\"java.lang.Runtime\",\"@type\":\"java.lang.Runtime\"}";
ParserConfig.getGlobalInstance().addAccept("java.lang");
Runtime runtime = (Runtime) JSON.parseObject(json, Object.class);
runtime.exec("calc.exe");
}
}

这里是调用了恶意类java.lang.Runtime 那正常的类是啥呢?

导入com.alibaba.fastjson.serializer.SerializerFeature并且在序列化的时候SerializerFeature.WriteClassName即可输出本身所属的类

调用toJSONString方法的时候,参数里面多了一个SerializerFeature.WriteClassName方法。传入SerializerFeature.WriteClassName可以使得Fastjson支持自省,开启自省后序列化成JSON的数据就会多一个@type,这个是代表对象类型的JSON文本。FastJson的漏洞就是他的这一个功能去产生的,在对该JSON数据进行反序列化的时候,会去调用指定类中对于的get/set/is方法

package org.example;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

public class Main {

public static void main(String[] args) {
// 将一个 Java 对象序列化为 JSON 字符串
Person person = new Person("Alice", 18);
String jsonString = JSON.toJSONString(person,SerializerFeature.WriteClassName);
System.out.println(jsonString);


}

// 定义一个简单的 Java 类
public static class Person {
private String name;
private int age;

public Person(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public int getAge() {
return age;
}
}
}

//{"@type":"org.example.Main$Person","age":18,"name":"Alice"}

三种反序列化字符串的方法

// 方法一(返回JSONObject对象):
Person user = new Person();
user.setAge(18);
user.setName("xiaoming");
String s1 = JSON.toJSONString(user, SerializerFeature.WriteClassName);
JSONObject jsonObject = JSON.parse(s1);
System.out.println(jsonObject);

// 方法二:
Person user = new Person();
user.setAge(18);
user.setName("xiaoming");
String s = JSON.toJSONString(user);
Person user1 = JSON.parseObject(s, Person.class);
System.out.println(user1);

// 方法三:
Person user = new Person();
user.setAge(18);
user.setName("xiaoming");
String s1 = JSON.toJSONString(user, SerializerFeature.WriteClassName);
Person user1 = JSON.parseObject(s1,Person.class);
System.out.println(user1);

JNDI

JNDIJava平台的一种API,它提供了访问各种命名和目录服务的统一方式。JNDI通常用于在JavaEE应用程序中查找和访问资源,如JDBC数据源、JMS连接工厂和队列等。需要安装一个tomcat

https://dlcdn.apache.org/tomcat/tomcat-11/v11.0.0-M22/bin/apache-tomcat-11.0.0-M22.zip

tomcat解压后把bin目录放到环境变量下

然后再新建一个名为CATALINA_HOME的路径,值为tomcat的根目录

JAVA_HOMEJRE_HOME的也要在用户变量中配置一下 放jdk的根目录

双击tomcatbin目录下的startup.bat,然后访问[http://localhost:8080/](http://localhost:8080/),就可以看到服务启动成功了