如何从 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 变量溯源

  1. initialSize来源于DbConfig
  2. DbConfig的变量赋值在createDataSource()方法中
  3. 最终来源于poolSetting.getInt()方法
    • 默认返回0

3. 解决方案探索

3.1 尝试修改DbConfig

发现DbConfig没有继承反序列化接口,无法直接反射修改,因为:

  • 即使修改了,反序列化过程中也不会自动应用修改

3.2 寻找被动修改点

分析poolSetting.getInt()方法逻辑:

  1. 默认返回0
  2. 如果有其他值则不会返回0
  3. 关键逻辑:
    • 获取value通过getByGroup
    • 使用defaultIfNull方法
      • 如果value不为空,则返回value

解决方案:

  • 反射修改poolSetting属性中的值,使value不为空

3.3 成功实现RCE

通过反射修改后成功触发H2数据库的RCE,调用栈显示触发了计算器。

4. 其他利用链分析

4.1 JndiDSFactory利用

利用点

  • 调用getJndiDs()会直接触发JNDI连接

问题解决

  1. Assert.notNull异常

    • 实例化JndiDSFactory时传入的class为null
    • 解决方案:反射实例化,绕过构造函数
  2. AbstractDSFactory空指针

    • getDataSource()过程中dsMap为空
    • 解决方案:反射修改属性
  3. JDBC URL设置

    • 需要设置任意值的jdbc url
    • sink核心在JNDI,url值不影响利用

4.2 SimpleDSFactory分析

  • 调用createDataSource()会实例化SimpleDataSource
  • 但整个过程不会触发JDBC连接
  • 因此无法利用

5. 完整利用链总结

5.1 PooledDSFactory利用链

  1. 通过反射修改poolSetting属性
  2. 确保initialSize值不为0
  3. 触发createDataSource()方法
  4. 实例化PooledDataSource时触发JDBC连接
  5. 实现RCE

5.2 JndiDSFactory利用链

  1. 反射实例化JndiDSFactory绕过构造函数限制
  2. 反射设置必要的属性值
  3. 调用getJndiDs()触发JNDI连接
  4. 实现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. 经验总结

  1. 深入理解sink点:必须完全理解sink点的触发条件和执行路径
  2. 变量溯源:从失败点反向追踪变量来源,找到可控点
  3. 反射技巧:熟练掌握反射技术可以绕过许多限制
  4. 异常处理:每个异常都是线索,需要分析原因并找到解决方案
  5. 多路径尝试:一个利用链失败时,尝试寻找其他可能的利用路径

通过这种系统化的分析和调试方法,可以有效地从零开始挖掘新的利用链并构造有效的POC。

从零开始挖掘新利用链的完整指南 前言 本文详细记录了在挖掘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 来源于 DbConfig DbConfig 的变量赋值在 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 反射修改属性 6.2 绕过构造函数限制 6.3 设置必要属性 7. 经验总结 深入理解sink点 :必须完全理解sink点的触发条件和执行路径 变量溯源 :从失败点反向追踪变量来源,找到可控点 反射技巧 :熟练掌握反射技术可以绕过许多限制 异常处理 :每个异常都是线索,需要分析原因并找到解决方案 多路径尝试 :一个利用链失败时,尝试寻找其他可能的利用路径 通过这种系统化的分析和调试方法,可以有效地从零开始挖掘新的利用链并构造有效的POC。