尝试CodeQL自动化分析2025 SUCTF SU_Solon
字数 1718 2025-08-29 08:30:31
CodeQL自动化分析Hessian反序列化漏洞利用链
1. Hessian反序列化基础
1.1 Hessian简介
Hessian是一个轻量级的remoting onhttp工具,提供Java序列化/反序列化功能,特点包括:
- 二进制RPC协议
- 比WebService更简单快捷
- 适合发送二进制数据
1.2 Hessian反序列化流程
- 客户端发起请求并按照RPC协议格式填充信息
- 将二进制格式文件转化为流并通过传输协议传输
- 服务端接收流并转换为二进制格式文件
- 按照RPC协议格式获取请求信息并处理
- 处理结果写入二进制格式文件并返回
1.3 与原生反序列化的区别
- 不需要实现Serializable接口:显著扩展了可利用类的范围
- 起始方法限制:只能为hashCode/equals/compareTo方法
- 成员变量限制:不能为transient修饰
- 不依赖:不依赖readObject逻辑和getter/setter逻辑
2. SUCTF SU_Solon题目分析
2.1 题目特点
- 明确的Hessian反序列化题目
- 触发任意类的toString方法作为入口点
- 依赖包含:
- Fastjson 1.2.83
- H2数据库
2.2 利用思路
- 利用Fastjson的JSONArray/JSONObject类的toString方法触发任意类的getter方法
- 结合H2数据库的JDBC连接实现RCE
3. CodeQL分析过程
3.1 分析目标
寻找getter方法触发JDBC连接的途径
3.2 分析方法选择
- 全局污点分析(TaintTracking2):复杂但全面
- 构造查询谓词:相对简单,本文采用此方法
3.3 查询构建
- 通过
getASupertype*()递归超类实现全面覆盖 - 查询静态调用目标和动态调度目标
- 使用
Callable作为查询子集(包含Method和Constructor)
3.4 查询结果
发现14种可能的触发点,重点关注:
- UnpooledDataSource
- JdbcDataSource
- DataSourceWrapper
- DataSourceProxy
4. 利用链分析
4.1 UnpooledDataSource利用
- 方法:
getConnection() - 利用步骤:
- 本地启动HTTP服务器存放恶意SQL文件
- 绕过SecurityManager限制:
System.setSecurityManager(null); - 生成反序列化对象触发RCE
4.2 JdbcDataSource利用
- 验证:可以触发JDBC连接
- 问题:
org.h2.jdbcx.JdbcDataSource在Hessian黑名单中 - 黑名单范围:
org.h2.jdbcx全限定类名前缀被过滤
4.3 其他利用点分析
-
DataSourceWrapper:
- 包装类,本质仍是触发DataSource接口的
getConnection() - 与直接触发UnpooledDataSource/JdbcDataSource无本质区别
- 包装类,本质仍是触发DataSource接口的
-
DataSourceProxy:
- 效果与DataSourceWrapper类似
-
UnixPrintService:
- JDK低版本可利用getter实现命令注入
- 仍在sofa-hession黑名单中
5. 防御与绕过
5.1 SecurityManager绕过
- 直接关闭:
System.setSecurityManager(null);
5.2 黑名单绕过策略
- 寻找不在黑名单中的类似功能类
- 利用包装类间接调用
- 寻找新的利用链
6. 总结与最佳实践
6.1 最佳利用链
- 首选:UnpooledDataSource的
getConnection()方法 - 原因:不在黑名单中且功能完整
6.2 CodeQL分析价值
- 快速定位潜在利用链
- 全面覆盖可能的调用路径
- 辅助识别黑名单外的可用类
6.3 扩展思考
- 结合Fastjson漏洞扩大攻击面
- 探索更多不在黑名单中的DataSource实现
- 研究其他toString到getter的触发方式
附录:关键QL查询示例
// 基本查询结构示例
from Callable c
where c.getName() = "getConnection"
and c.getDeclaringType().getASupertype*().hasQualifiedName("javax.sql", "DataSource")
select c
// 更全面的查询示例
from Method m
where m.getName().matches("get%")
and m.getDeclaringType().getASupertype*().hasQualifiedName("javax.sql", "DataSource")
and not m.getDeclaringType().getQualifiedName().matches("org.h2.jdbcx.%")
select m