如何从 0 在不断调试中挖掘一条新利用链
字数 1699 2025-08-29 22:41:01
从零开始挖掘新利用链的完整指南
前言
本文详细记录了在挖掘hutool组件漏洞时,如何通过不断调试分析构造POC并最终实现RCE的完整过程。文章将涵盖从最初的失败调用到最终成功利用的每个关键步骤。
1. 初始发现与问题定位
1.1 失败的调用尝试
最初尝试构造POC时发现无法调用预期的getter方法,这促使了对利用链的深入调试分析。
1.2 关键sink点分析
PooledDSFactory类中的关键sink点位于:
getDataSource()方法- 调用父类的
getDataSource() - 进而调用
createDataSource()方法 - 实例化
PooledDataSource类 - 实例化过程中会触发JDBC连接
- 调用父类的
2. 问题分析与调试
2.1 初始POC失败原因
使用初始POC无法造成连接的原因是:
- 实例化
PooledDataSource对象时,while循环不会进入 initialSize-- > 0条件不满足- 这是一个后置递减运算符与比较运算的组合
- 先使用当前值比较,再减1
- 初始值为0,直接跳过循环
2.2 变量溯源
initialSize来源于DbConfigDbConfig的变量赋值在createDataSource()方法中- 最终来源于
poolSetting.getInt()方法- 默认返回0
3. 解决方案探索
3.1 尝试修改DbConfig
发现DbConfig没有继承反序列化接口,无法直接反射修改,因为:
- 即使修改了,反序列化过程中也不会自动应用修改
3.2 寻找被动修改点
分析poolSetting.getInt()方法逻辑:
- 默认返回0
- 如果有其他值则不会返回0
- 关键逻辑:
- 获取
value通过getByGroup - 使用
defaultIfNull方法- 如果
value不为空,则返回value
- 如果
- 获取
解决方案:
- 反射修改
poolSetting属性中的值,使value不为空
3.3 成功实现RCE
通过反射修改后成功触发H2数据库的RCE,调用栈显示触发了计算器。
4. 其他利用链分析
4.1 JndiDSFactory利用
利用点:
- 调用
getJndiDs()会直接触发JNDI连接
问题解决:
-
Assert.notNull异常:
- 实例化
JndiDSFactory时传入的class为null - 解决方案:反射实例化,绕过构造函数
- 实例化
-
AbstractDSFactory空指针:
getDataSource()过程中dsMap为空- 解决方案:反射修改属性
-
JDBC URL设置:
- 需要设置任意值的jdbc url
- sink核心在JNDI,url值不影响利用
4.2 SimpleDSFactory分析
- 调用
createDataSource()会实例化SimpleDataSource - 但整个过程不会触发JDBC连接
- 因此无法利用
5. 完整利用链总结
5.1 PooledDSFactory利用链
- 通过反射修改
poolSetting属性 - 确保
initialSize值不为0 - 触发
createDataSource()方法 - 实例化
PooledDataSource时触发JDBC连接 - 实现RCE
5.2 JndiDSFactory利用链
- 反射实例化
JndiDSFactory绕过构造函数限制 - 反射设置必要的属性值
- 调用
getJndiDs()触发JNDI连接 - 实现RCE
6. 关键代码片段
6.1 反射修改属性
// 反射修改poolSetting属性
Field poolSettingField = PooledDSFactory.class.getDeclaredField("poolSetting");
poolSettingField.setAccessible(true);
// 设置修改后的poolSetting对象
6.2 绕过构造函数限制
// 反射实例化JndiDSFactory
Constructor<JndiDSFactory> constructor = JndiDSFactory.class.getDeclaredConstructor();
constructor.setAccessible(true);
JndiDSFactory instance = constructor.newInstance();
6.3 设置必要属性
// 反射设置dsMap
Field dsMapField = AbstractDSFactory.class.getDeclaredField("dsMap");
dsMapField.setAccessible(true);
dsMapField.set(instance, new HashMap<>());
7. 经验总结
- 深入理解sink点:必须完全理解sink点的触发条件和执行路径
- 变量溯源:从失败点反向追踪变量来源,找到可控点
- 反射技巧:熟练掌握反射技术可以绕过许多限制
- 异常处理:每个异常都是线索,需要分析原因并找到解决方案
- 多路径尝试:一个利用链失败时,尝试寻找其他可能的利用路径
通过这种系统化的分析和调试方法,可以有效地从零开始挖掘新的利用链并构造有效的POC。