定时任务功能点绕过黑白名单执行任意sql语句
字数 1232 2025-08-27 12:33:23

若依后台管理系统定时任务功能SQL注入漏洞分析

环境说明

  • 目标系统:RuoYi-fast v4.7.3(前后端不分离版本)
  • 漏洞组件:Quartz定时任务组件

Quartz组件基础

RuoYi-fast使用Quartz作为定时任务组件,基本使用方式如下:

  1. 依赖配置
<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
  </dependency>
</dependencies>
  1. Job实现
public class DateTimeJob extends QuartzJobBean {
    @Override
    protected void executeInternal(JobExecutionContext context) {
        String msg = (String) context.getJobDetail().getJobDataMap().get("msg");
        System.out.println("current time :"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "---" + msg);
    }
}
  1. 配置Job和Trigger
@Configuration
public class QuartzConfig {
    @Bean
    public JobDetail printTimeJobDetail(){
        return JobBuilder.newJob(DateTimeJob.class)
                .withIdentity("DateTimeJob")
                .usingJobData("msg", "Hello Quartz")
                .storeDurably()
                .build();
    }

    @Bean
    public Trigger printTimeJobTrigger() {
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/1 * * * * ?");
        return TriggerBuilder.newTrigger()
                .forJob(printTimeJobDetail())
                .withIdentity("quartzTaskService")
                .withSchedule(cronScheduleBuilder)
                .build();
    }
}

漏洞分析

定时任务执行逻辑

  1. 执行流程

    • AbstractQuartzJob抽象类实现了模版方法设计模式
    • 包含beforedoExecuteafter方法
    • execute方法组合上述三个方法
  2. 关键执行点

    • 通过反射调用目标方法
    • 支持两种调用方式:
      • Spring容器中注册的bean
      • 指定class名称(需有无参构造函数)
  3. 方法调用限制

    • 方法不能是private修饰
    • 参数类型仅支持:String、Boolean、Long、Double、Integer

定时任务添加/修改逻辑

  1. 黑白名单检查

    • 添加定时任务前会检查黑白名单
    • 但存在检测遗漏:仅对全限定类名(class)检测,未对Spring容器中的对象检测
  2. 漏洞产生原因

    • 可以绕过黑名单检测
    • 可以逃过白名单检测

寻找可利用对象

在Spring容器中寻找满足以下条件的对象:

  1. 存在于Spring容器中
  2. 方法至少不是private修饰
  3. 参数类型为String、Boolean、Long、Double、Integer

发现可利用对象

  • JdbcTemplate类中的executeupdate方法
    • 都是public方法
    • 参数为String类型
    • 可以执行任意SQL语句

绕过方法参数截取

  1. 问题

    • 方法参数值是从目标字符串第一个(和第一个)中间的字符串获取
    • jdbcTemplate.execute("insert into sys_user_role values(7,7);"),实际获取的是"insert into sys_user_role values(7,7"
  2. 绕过方案

    • 使用MySQL预处理和hex编码
    • 确保参数值内容中不出现)
  3. 示例攻击流程

    • 将SQL语句hex编码:insert into sys_user_role values(7,7);0x696E7365727420494E544F207379735F757365725F726F6C652076616C75657328372C293B
    • 构造无)的payload:
      set @a=0x696E7365727420494E544F207379735F757365725F726F6C652076616C75657328372C293B;
      prepare stmt from @a;
      execute stmt;
      
    • 通过定时任务执行上述SQL

漏洞修复方案

  1. 完善黑白名单检测机制
  2. 对Spring容器中的对象也进行白名单检测
  3. 严格限制可执行的方法和参数类型

总结

该漏洞利用Quartz定时任务组件的反射执行机制,结合Spring容器中JdbcTemplate的可执行SQL特性,通过精心构造的SQL语句绕过参数截取限制,最终实现任意SQL语句执行。修复时应全面考虑各种调用方式和参数处理场景。

若依后台管理系统定时任务功能SQL注入漏洞分析 环境说明 目标系统:RuoYi-fast v4.7.3(前后端不分离版本) 漏洞组件:Quartz定时任务组件 Quartz组件基础 RuoYi-fast使用Quartz作为定时任务组件,基本使用方式如下: 依赖配置 : Job实现 : 配置Job和Trigger : 漏洞分析 定时任务执行逻辑 执行流程 : AbstractQuartzJob 抽象类实现了模版方法设计模式 包含 before 、 doExecute 、 after 方法 execute 方法组合上述三个方法 关键执行点 : 通过反射调用目标方法 支持两种调用方式: Spring容器中注册的bean 指定class名称(需有无参构造函数) 方法调用限制 : 方法不能是private修饰 参数类型仅支持:String、Boolean、Long、Double、Integer 定时任务添加/修改逻辑 黑白名单检查 : 添加定时任务前会检查黑白名单 但存在检测遗漏:仅对全限定类名(class)检测,未对Spring容器中的对象检测 漏洞产生原因 : 可以绕过黑名单检测 可以逃过白名单检测 寻找可利用对象 在Spring容器中寻找满足以下条件的对象: 存在于Spring容器中 方法至少不是private修饰 参数类型为String、Boolean、Long、Double、Integer 发现可利用对象 : JdbcTemplate 类中的 execute 和 update 方法 都是public方法 参数为String类型 可以执行任意SQL语句 绕过方法参数截取 问题 : 方法参数值是从目标字符串第一个 ( 和第一个 ) 中间的字符串获取 如 jdbcTemplate.execute("insert into sys_user_role values(7,7);") ,实际获取的是 "insert into sys_user_role values(7,7" 绕过方案 : 使用MySQL预处理和hex编码 确保参数值内容中不出现 ) 示例攻击流程 : 将SQL语句hex编码: insert into sys_user_role values(7,7); → 0x696E7365727420494E544F207379735F757365725F726F6C652076616C75657328372C293B 构造无 ) 的payload: 通过定时任务执行上述SQL 漏洞修复方案 完善黑白名单检测机制 对Spring容器中的对象也进行白名单检测 严格限制可执行的方法和参数类型 总结 该漏洞利用Quartz定时任务组件的反射执行机制,结合Spring容器中JdbcTemplate的可执行SQL特性,通过精心构造的SQL语句绕过参数截取限制,最终实现任意SQL语句执行。修复时应全面考虑各种调用方式和参数处理场景。