ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

从一次 SQL 注入到权限失控,中间到底差了什么?

从一次 SQL 注入到权限失控,中间到底差了什么?

SQL 注入是渗透测试里最常被提到的漏洞之一,但很多人只停留在“能注入、能回显、能拖库”这一层。真正危险的地方在于:SQL 注入往往不是终点,它可能成为攻击者进入内网、获取更高权限、最终控制业务系统的入口。

这篇博客不追求炫技,而是把一条常见的攻击链路拆开来,看看从数据库到主机权限之间,攻击者可能经过哪些环节,以及防守方应该在哪里布防。


为什么 SQL 注入不只是“数据库问题”?

数据库通常不是孤立存在的。它往往连接着:

  • 应用服务器;
  • 文件存储;
  • 内网其他服务;
  • 域账号或运维账号;
  • 定时任务、备份脚本;
  • 配置文件和连接串。

如果 Web 应用使用高权限数据库账号运行,或者数据库服务器本身存在配置缺陷,攻击者就可能从 SQL 注入逐步扩大到主机权限,甚至内网横向移动。

一条典型链路可能是:

SQL 注入 → 读取配置/凭据 → 获取数据库高权限 → 执行系统命令 → 控制数据库服务器 → 进一步横向移动。


攻击链路拆解

1. 发现注入点

攻击者首先会寻找用户可控的参数,例如搜索框、ID 参数、登录接口、排序字段等。

例如一个商品查询接口:

GET /product?id=1001 HTTP/1.1

如果后端直接把参数拼接到 SQL 中,就可能出现注入风险。

需要注意的是,真实环境中攻击者不会只靠手工猜测,而是结合自动化扫描、报错信息、时间延迟、布尔变化等方式判断注入类型。

2. 判断数据库类型和权限

不同数据库的利用方式差别很大。攻击者通常会尝试判断:

  • 是 MySQL、PostgreSQL、SQL Server 还是 Oracle;
  • 当前数据库账号权限有多高;
  • 是否支持文件读写;
  • 是否支持执行系统命令;
  • 是否能访问其他库、其他表。

例如在 MySQL 中,如果存在文件读取能力,攻击者可能尝试读取配置文件:

' UNION SELECT 1, load_file('/etc/passwd'), 3 --

当然,这要求数据库账号具备相应权限,且目标文件路径可读。很多安全加固场景下,这类操作会被直接阻断。

3. 读取敏感配置

应用服务器和数据库之间通常存在连接配置,例如:

  • 数据库账号密码;
  • Redis、RabbitMQ、Elasticsearch 地址;
  • 内网 API 地址;
  • 对象存储密钥;
  • 后台管理账号哈希。

如果攻击者能够通过 SQL 注入读取配置文件、日志文件、备份文件,或者查询数据库中的用户表,就可能获得进一步突破的凭据。

例如查询管理员账号:

' UNION SELECT id, username, password FROM admin_users --

如果密码没有加盐哈希,或者使用了弱哈希算法,后续还可能被离线破解。

4. 尝试执行系统命令

并不是所有数据库都能直接执行系统命令。能否做到这一点,取决于数据库类型、版本、配置和权限。

以 SQL Server 为例,如果xp_cmdshell被开启且账号权限足够,攻击者可能尝试执行命令:

EXEC xp_cmdshell 'whoami';

在 MySQL 中,如果满足非常苛刻的条件,例如高权限账号、可写目录、secure_file_priv 配置不当,攻击者可能尝试写入 Webshell:

SELECT '<?php system($_GET["cmd"]); ?>' INTO OUTFILE '/var/www/html/shell.php';

这类利用方式在现实环境中并不容易成功,但一旦应用和数据库都缺乏加固,风险会显著上升。

5. 从数据库服务器跳到内网

即使攻击者只能控制数据库服务器,也可能继续向内网扩展。例如:

  • 查看内网网卡信息;
  • 扫描同网段主机;
  • 读取应用连接串中的其他服务地址;
  • 利用数据库服务器上的凭据访问其他系统;
  • 通过计划任务、启动项、脚本实现持久化。

到这里,SQL 注入已经不再只是 Web 层漏洞,而可能演变成主机权限丢失和内网渗透入口。


防守方应该看什么?

1. 参数化处理所有输入

最根本的修复方式是使用参数化查询或 ORM,避免字符串拼接 SQL。

例如在 Python 中,应避免:

query = "SELECT * FROM products WHERE id = " + user_input

而应使用参数化方式:

cursor.execute("SELECT * FROM products WHERE id = %s", (user_input,))

参数化查询能把用户输入当作数据处理,而不是当作 SQL 语句的一部分执行。

2. 最小权限原则

数据库账号不应使用sa、root、dba这类高权限账号运行 Web 应用。

应用账号通常只需要:

  • 对指定库的查询权限;
  • 必要的增删改权限;
  • 不需要文件读写权限;
  • 不需要执行系统命令权限;
  • 不需要访问其他库或系统表。

如果 Web 应用只需要查询商品数据,就不应该拥有读取系统文件、创建用户、执行命令的能力。

3. 关闭危险功能

不同数据库都有需要重点关注的危险配置,例如:

  • SQL Server 的xp_cmdshell;
  • MySQL 的secure_file_priv和文件读写权限;
  • PostgreSQL 的COPY、pg_read_file等函数;
  • Oracle 的 Java 存储过程、外部调用能力。

这些功能在开发调试时可能方便,但生产环境应默认关闭或严格限制。

4. 敏感数据保护

即使发生注入,也不应让攻击者轻易拖走全部数据。可以从数据侧降低损失:

  • 对用户密码使用强哈希算法并加盐;
  • 对身份证号、手机号、银行卡号做脱敏或加密存储;
  • 对敏感字段做访问审计;
  • 对数据库账号做权限分级;
  • 对异常大量查询进行告警。

例如,即便攻击者能查询用户表,如果关键字段已经加密或脱敏,实际可利用价值也会下降。

5. 日志与告警

可以重点监控以下行为:

  • 同一接口出现大量异常参数;
  • 数据库账号访问了平时不访问的表;
  • 出现UNION SELECT、load_file、xp_cmdshell、INTO OUTFILE等高危特征;
  • 数据库进程启动子进程;
  • Web 服务器向数据库发起异常查询;
  • 数据库服务器向外发起大量连接。

这些不一定每一条都是攻击,但集中出现时值得排查。


一段简化的检测思路

下面是一个伪代码示例,用于说明如何从日志中识别可疑 SQL 注入行为:

import re suspicious_patterns = [ r"union\s+select", r"or\s+1\s*=\s*1", r"'\s*or\s*'", r"load_file\s*\(", r"into\s+outfile", r"xp_cmdshell", r"information_schema", r"sleep\s*\(", r"benchmark\s*\(", ] def is_suspicious_sql(value: str) -> bool: lower_value = value.lower() for pattern in suspicious_patterns: if re.search(pattern, lower_value): return True return False user_input = request.args.get("id") if is_suspicious_sql(user_input): log_alert("possible SQL injection attempt", user_input) return "Invalid request", 400

需要注意,这类规则只能作为辅助手段。攻击者可以通过编码、注释、大小写、分块拼接等方式绕过简单匹配,因此不能只依赖关键字拦截,更应从根本上使用参数化查询和最小权限。


学习建议

学习 SQL 注入时,建议不要只停留在“写 payload”的层面,而要从完整链路理解它:

  1. 漏洞成因:为什么用户输入会影响 SQL 语义?
  2. 利用边界:不同数据库能做什么、不能做什么?
  3. 权限影响:数据库账号权限如何决定危害等级?
  4. 数据风险:哪些数据需要加密、脱敏、分级保护?
  5. 检测修复:如何从代码、配置、日志三层同时防御?

可以在授权靶场中练习注入原理和修复方法,但必须遵守法律边界:任何扫描、注入、提权、命令执行等操作都只能在获得明确授权的环境中进行。

SQL 注入看似基础,但它连接着 Web 安全、数据库安全、主机安全和数据安全。把它学透,比单纯记住几个 payload 有价值得多。


如果想继续扩展,可以选一个方向:

  • 代码层:写一份常见语言的参数化查询对照表
  • 靶场层:设计一个从 SQL 注入到读取配置文件的实验场景
  • 防守层:整理一份数据库安全加固检查清单

针对你划选的这段内容,从结构、逻辑和案例补充三个维度给出如下改进建议:

结构优化建议
  • 「学习建议」一节目前以三个并列方向收尾,建议在列表前增加一句过渡,说明这三个方向之间的递进关系,例如「先代码层打基础,再靶场层练实战,最后防守层做沉淀」,让读者明确学习路径。
  • 三个方向建议按「基础 → 进阶 → 综合」排序,当前顺序已基本合理,但可在每个方向后补充一句「预期产出」或「验收标准」,让目标更可衡量。
  • 结尾段「SQL 注入看似基础……有价值得多」作为全文收束很好,建议在其前增加一个「小结」过渡句,例如「回顾整条链路,从注入点到内网横向移动,每一步都环环相扣」,使结尾更自然。
逻辑优化建议
  • 「学习建议」与上文「一段简化的检测思路」之间缺少衔接。建议在「学习建议」开头补充一句「前面讲了攻击链路和防守要点,下面从学习路径角度做一次收束」,让章节过渡更顺滑。
  • 三个扩展方向中,「靶场层」与「防守层」都涉及实操,建议明确二者差异:靶场层侧重「攻击视角」的复现,防守层侧重「加固视角」的落地,避免读者混淆。
  • 结尾段提到「把它学透」,建议补充一个可量化的判断标准,例如「能独立讲清一条从注入到内网的完整链路,并能在授权环境中复现和修复」,让「学透」有具体抓手。
案例补充建议
  • 「代码层」方向可补充一个具体示例:给出 Java、Go、PHP 三种语言的参数化查询写法,与文中已有的 Python 示例形成对照,增强实用性。
  • 「靶场层」方向可补充一个可落地的实验步骤清单,例如「搭建靶机 → 复现注入 → 读取配置 → 记录修复前后对比」,让读者知道从哪开始。
  • 「防守层」方向可补充一个真实加固案例,例如「某团队关闭 xp_cmdshell 后,一次注入攻击的利用链被截断」,用具体场景说明加固的价值。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进