SQL 注入实战:时间盲注原理与 Python 脚本详解

前言

前两篇文章介绍了布尔盲注------它依赖页面在"正确"和"错误"时呈现不同内容。但如果目标页面对任何输入都返回完全相同的响应,布尔盲注就彻底失效了。

这时候就轮到时间盲注(Time-based Blind SQLi) 登场:我们不再观察页面内容的变化,而是让数据库"睡一觉",通过测量响应时间来判断条件真假。只要服务器响应慢了,我们就知道答对了。

文章目录

靶场题目:Less-9 / Less-10

Less-9 和 Less-10 是时间盲注的标准练习题。特征如下:

  • 无论输入什么,页面响应始终一致
  • 没有报错,没有数据回显,没有任何状态区分
  • 唯一可利用的信息通道:HTTP 响应时间

Less-9 使用单引号闭合,Less-10 使用双引号闭合,脚本中通过切换注释对应 payload 即可适配两种情况。


核心原理:用时间说话

MySQL 提供了两个关键武器:

sql 复制代码
-- 让数据库暂停 5 秒
SELECT SLEEP(5);

-- 条件判断:库名是否为 security?是则睡 5 秒,否则什么都不做
SELECT IF((SELECT DATABASE())='security', SLEEP(5), NULL);

IF(condition, true_result, false_result) 是时间盲注的核心结构。把"睡眠"嵌入条件分支,就把"对与错"变成了"慢与快"。

手工验证注入点

sql 复制代码
-- 第一步:确认 sleep 能否生效
?id=2' and sleep(5) --+

-- 第二步:尝试条件判断
?id=2' and if((select database())='security', sleep(5), null) --+

如果第一条让页面延迟 5 秒,说明注入点存在且时间盲注可行。


手工注入:完整流程

爆破数据库名长度

sql 复制代码
?id=1' and if(length(database()) > 7, sleep(5), 0) --+

从 1 开始逐步增大比较值,当页面不再延迟时,上一个值就是库名长度。

逐字符爆破库名

sql 复制代码
-- 判断库名第一个字符是否为 's'(ASCII 115)
?id=1' and if(ascii(substr(database(),1,1))=115, sleep(5), 0) --+

爆破表名

sql 复制代码
-- 取第一张表的第一个字符,判断是否为 'e'(ASCII 101)
?id=1' and if(ascii(substr(
    (select table_name from information_schema.tables 
     where table_schema=database() limit 0,1)
,1,1))=115, sleep(5), 0) --+

limit 0,1 取第一张表,limit 1,1 取第二张,依此枚举。

实战技巧 :数据库名、表名等字符串在 payload 中可以用其 HEX 值 代替,避免引号冲突,同时也能绕过部分过滤。例如 'security' 等价于 0x73656375726974...


Python 脚本:timeblind.py 解析

脚本将完整攻击链拆分为五个独立函数,结构清晰,每一步都可以单独调用:

整体架构

复制代码
main()
├── database_len()      → 爆破库名长度
├── database_name(len)  → 爆破库名
├── table_name()        → 枚举所有表名
├── colum_name(table)   → 枚举指定表的字段名
└── data(column, table) → 提取字段数据

时间测量的核心写法

所有函数都遵循同一个计时模式:

python 复制代码
import datetime

time1 = datetime.datetime.now()
r = requests.get(url + payload)
time2 = datetime.datetime.now()

sec = (time2 - time1).seconds
if sec >= 2:
    # 条件成立,记录当前字符

datetime 精确记录请求前后的时间戳,差值超过阈值(2~3 秒)即视为"条件成立"。这比直接用 time.time() 更语义化,也更便于阅读。

库名爆破的两段式设计

database_len() 先确定长度,database_name(len) 再按长度逐字符枚举。这样避免了盲目爆破造成的请求浪费------一旦知道库名只有 8 位,后续循环就不会多跑一次。

database_name 的枚举字符集直接使用字符串遍历,而非 ASCII 码范围循环:

python 复制代码
for j in '0123456789abcdefghijklmnopqrstuvwxyz':
    # 直接比较字符,payload 构造更直观

对于库名、表名这类通常只含小写字母和数字的目标,缩小字符集能显著减少请求次数。

表名与字段名的三层嵌套

爆破表名和字段名需要三层 for 循环:

复制代码
外层 k:第 k 张表(或第 k 个字段)
中层 i:字符串的第 i 个字符
内层 j:ASCII 码范围 65~122(A-z)

这个范围覆盖了大小写字母,足以应对常见的表名和字段名。如需支持数字或下划线,扩展 range 的起始值即可。


时间盲注的局限与注意事项

网络抖动是最大的干扰源。 时间盲注依赖响应时间判断,而网络延迟是不稳定的。实践中有几种应对策略:

  • 将 sleep 时间设置得足够大(≥3 秒),远超正常响应时间
  • 对同一个 payload 发送多次,取平均值或多数结果
  • 在本地靶场练习时效果最稳定,生产环境慢网络下误判率会上升

线性枚举效率问题。 timeblind.py 的内层循环使用线性枚举,每个字符最多需要发送数十次请求,每次还要等待 sleep 超时。爆破一个完整的数据集可能需要数分钟甚至更长时间。结合二分查找优化是进阶方向。


三种盲注方式横向对比

注入类型 判断依据 适用场景 效率
布尔盲注(线性) 页面内容差异 有明显回显状态区分 较低
布尔盲注(二分) 页面内容差异 同上,追求效率 较高
时间盲注 HTTP 响应时间 页面完全无差异 最低

时间盲注是三者中适用范围最广 的,但也是效率最低、稳定性最差的。真实渗透测试中,时间盲注通常是在其他方式都失效后的最后手段。


完整代码与靶场资源

timeblind.py 脚本已开源至 GitHub,与 bool.pybool1.py 收录在同一仓库中。

脚本链接: github.com/aqirompt/sqli-labs-bool

靶场搭建请参考:

至此,布尔盲注与时间盲注的完整系列告一段落,届见。

免责声明:本文所有内容仅供授权环境下的安全学习与研究,切勿将相关技术用于任何未经授权的系统。

相关推荐
黑战士安全1 天前
基于Ollama的自动化渗透测试框架:设计方案
web安全·网络安全·渗透测试
PyHaVolask2 天前
SQL 注入实战:布尔盲注完整流程与 Python 脚本详解
web安全·渗透测试·sql注入·python脚本·布尔盲注
交个_朋友3 天前
HTB TartarSauce writeup(信息收集不遗漏,脚本漏洞能利用)
渗透测试·oscp·htb
PyHaVolask3 天前
SQL 注入实战:布尔盲注原理与自动化脚本解析
sql注入·二分查找算法·自动化脚本·布尔盲注·sqli-labs靶场
ShoreKiten6 天前
DC-3靶机渗透--CTFer从0到1的进阶之路
安全·网络安全·渗透测试
xcLeigh7 天前
SQL 注入防不住?金仓内核级防火墙,白名单防护零误报
数据库·数据安全·sql注入·kingbasees·金仓数据库·数据补丁
Sombra_Olivia8 天前
Vulhub 中的 adminer CVE-2021-21311
安全·web安全·网络安全·渗透测试·vulhub
vortex59 天前
文件上传漏洞绕过技术总结(含实操指南与防御方案)
linux·服务器·网络安全·渗透测试
Atomic121389 天前
隧道搭建之端口复用
web安全·网络安全·渗透测试