应对WAF(网页防火墙)
过滤空格
使用下面的URL编码尝试替代空格:
%20 标准空格
%09 水平tab键
%0a 新建一行
%0c 新建一页
%0d 回车键
%0b 垂直tab键
%a0 也是空格键
特殊的,对于两个不同语法单元之间的空格也可以用运算符来代替:
||:逻辑或运算符(注意:MySQL 中默认 || 是逻辑或,和OR等价,部分环境需配置)
&:位与运算符
^:位异或运算符
+:算术加法运算符(前提是左右两边可转换为数字)
例如:
|
|
传入上面的参数后,因为select查询和extractvalue是两个无关的语法单元,所以可以用|连接。
过滤注释符
过滤注释符后我们的思路一般就是两种:
- 将注释符换为截止符
%00 - 使得查询语句中
$id前后的符号都能闭合
这里针对第二种进行讲解(以Less-23为例):
将前后两个单引号闭合再用or连接,即:
|
|
这样在整个查询语句中就是:
|
|
过滤and、or
替换URL编码:
or = || = %7c%7c(url编码)
and = && = %26%26(url编码)
双写绕过:
or—>oorr
and–>anandd
过滤-``/``\等重要符号
使用URL编码绕过
过滤union、select等重要单词
- 随机其中几个字母大小写
- 双写绕过
过滤数据库名
可对数据库名进行HEX编码,顺便也可绕过对引号的屏蔽,例如:
|
|
补充
- 大小写绕过
unION SelECT
- 双写绕过
uniunionon
- 编码绕过
hex和urlencode
- 注释符
uni/**/on
- 反引号
列名和表名可以使用``包裹
|
|
- 内联注释
and /*!select*/ 1,2,3
- <>绕过
uni<>on sel<>ect
- 屏蔽逗号
select substr("haha",1,3)
select substr("haha" from 1 to 3)
union select 1,2,3=union select * from (select 1)a join (select 2)b join (select 3)c join;
limit 0,1=limit 0 offset 1
- sleep屏蔽
and sleep(1)=and benchmark(100000000000,1)
- group_concat屏蔽
select group_concat("haha","nihao")=select concat_ws("haha","nihao")
- 屏蔽等号
like rlike regexp <>
1' or '1'='1
1' or '1'<>'1
- POST情况屏蔽#
-- a
- ip地址屏蔽
X-Forwarded-For: 1.1.1.1
X-Remote-IP
X-Originating-IP
X-Real-Ip
- 修改静态资源
www.x.com/sql.php?id=1
www.x.com/sql.php/1.js?id=1
- url白名单
防火墙很多时候不会去管管理员后台做了什么,利用waf自带的白名单admin login manager system sys
www.x.com/sql.php/admin.php?id=1
www.x.com/sql.php?xxxx=/manager/&id=1
www.x.com/../../../../../manager.php/../../../sql.php?id=1
- 爬虫白名单
伪造自己成为搜索引擎的爬虫(ua的伪造)
- 增加干扰
union all %a0 select
利用insert、update和delete进行注入 [1]
思路简要说明
insert、update、delete注入的本质是参数未经过滤,攻击者可在语句的关键位置插入恶意 SQL 片段,利用数据库执行特性获取敏感数据,核心常用方法为报错注入,具体应用场景如下:
- insert 注入:在
insert into 表名(字段1,字段2) values(值1,值2)中,针对 “值 1”“值 2” 等参数构造恶意内容(如插入错误的函数调用、语法错误片段),触发数据库报错,通过错误提示回显数据库名、表名、字段名等信息。 - update 注入:在
update 表名 set 字段=值 where 条件中,针对 “值” 或 “条件” 部分注入恶意 SQL,例如构造含报错函数的语句,使数据库执行时抛出包含敏感数据的错误(如用户密码、权限信息等)。 - delete 注入:在
delete from 表名 where 条件中,针对 “条件” 参数注入恶意片段,通过触发报错或利用条件判断逻辑,间接获取数据或破坏数据完整性(注入核心以 “获取数据” 为目标时,优先通过报错回显实现)。
本地测试
执行下面的的命令创建测试用数据
|
|
接下来我们通过单双引号包含的形式来人为构造语法错误实现报错。
|
|
然后就大同小异了,插入我们的payload(以查看版本号为例)。
|
|
其他报错注入语句请见 1.22 SQL注入系列-2
Insert:
|
|
Update:
|
|
Delete:
|
|
此外,除了or (payload) or闭合格式,还有下面的其他闭合变种:
|
|
宽字节注入
在一些情况下,目标会使用诸如
addslashes()、mysql_real_escape_string()、mysql_escape_string()、Magic_quotes_gpc()等函数对我们传入的内容进行转义,特别是引号之类的内容,这就会导致我们无法通过直接在参数里填入'来达成闭合,而宽字节注入就是为了解决这个问题。
宽字节概念
相对于单字节,我们引入一个字符数大小为两个字节的为宽字节,比如GBK编码,我们汉字通常使用的就是GBK编码,也就是说一次性会读取两个字节。
注入原理
以addslashes()函数为例,它的目的是根据预定义的字符(由服务端根据需要配置,例如单双引号)在你传的参数中检查是否存在这些预定义字符,如果有就在前面加上一个\起到转义的作用。这样当你试图传入?id=1'时实际的参数为?id=1\'后面的引号失去了闭合作用。
但假如我们像下面这样传参:?id=%df'🤔
你先别急%df是什么意思,但是你肯定知道\的URL编码是%5C,这样一来参数在程序内部就变成了%df%5C',而%df%5C实际上是[運]这个汉字的GBK编码,而MySQL会优先识别汉字的GBK编码**(前提是数据库本身就是GBK编码)**而忽略了转义符的存在,导致后面跟着的单引号仍然具有闭合作用。
具有相似性质的汉字还有这些:

小测验(Less-32)
使用刚才学习的宽字节注入轻松完成闭合并获取到了报错位:
|
|
接下来的操作就和第一关一样了。
最终payload:
|
|
使用sqlmap进行宽字节注入
新增参数:--tamper unmagicquotes指加载unmagicquotes这个宽字节注入专用模型,其他没什么区别。
爆库名:
|
|
爆表名:
|
|
爆字段名:
|
|
爆字段数据:
|
|
参考文献
[1] babers. 利用 insert、update 和 delete 注入获得数据 [EB/OL]. 2017-07-28. https://www.cnblogs.com/babers/articles/7252401.html. [2026-01-23]