uri15012/timing-attack

GitHub: uri15012/timing-attack

一个基于 Python 的远程计时侧信道攻击演示工具,利用响应时间差异逐字符恢复密码。

Stars: 0 | Forks: 0

# 计时侧信道攻击 — 作品集演示 这是一个针对模拟身份验证服务器的**远程计时侧信道攻击**的独立演示项目。所有流量均在 localhost 上运行;此处的内容不适用于真实系统。 ## 什么是计时侧信道攻击? 计时侧信道攻击利用了程序的执行时间可能依赖于秘密数据的*值*(而不仅仅是其长度)这一事实。在一个从左到右逐字符比较并在首次不匹配时立即返回的简单密码校验中,匹配了前 *k* 个字符的候选密码,其被拒绝所需的时间会比在第 0 个字符就不匹配的候选密码更长。攻击者无需窥探服务器内部——网络连接上的时钟泄露了这些信息。 该攻击逐个位置进行。为了恢复第 *i* 个字符,保持前 *i-1* 个字符不变(已恢复),然后在第 *i* 个位置遍历所有 36 个候选字符(a-z, 0-9),将每个候选字符发送多次并记录往返延迟。其**低百分位响应时间最高**的候选字符,比其他所有字符多匹配了一个字符,并在服务器内部多休眠了一个 `CHAR_DELAY` 间隔。锁定该字符,然后移动到第 *i+1* 个位置。 网络抖动是主要的干扰因素。单次探测可能会被操作系统的调度噪声所主导,因此每个候选字符会被探测 *N* 次,并使用**第 10 百分位数**作为代表性延迟。百分位数的选择和校准步骤(如下所述)使得该技术即使在抖动达到几毫秒的情况下依然具有鲁棒性。 ## 架构 ``` attacker.py server.py ─────────── ───────── calibrate() send 30 baseline probes ───────────────► compare_password("xxxxxxxx") measure RTT stdev no match at char 0 → return immediately choose N (5–25 samples) ◄─────────────── FAIL\n (~0.1 ms) attack(): position 0 ThreadPoolExecutor(8) for ch in "abcde…z0…9": probe N times ───────────────► compare_password("fxxxxxxx") 'f'=='f' → sleep 5 ms 'x'!='3' → return ◄─────────────── FAIL\n (~5 ms) ← timing leak probe N times ───────────────► compare_password("axxxxxxx") 'a'!='f' → return immediately ◄─────────────── FAIL\n (~0.1 ms) pick char with max p10 ← 'f' wins by ~5 ms delta lock in 'f', move to pos 1 attack(): position 1 for ch in CHARSET: probe "f" + ch + filler ───────────────► compare_password("f3xxxxxx") 'f'=='f' → sleep 5 ms '3'=='3' → sleep 5 ms 'x'!='a' → return ◄─────────────── FAIL\n (~10 ms) ... after position 7: send "admin:f3a9k2z1" ───────────────► compare_password("f3a9k2z1") all 8 chars match → return True ◄─────────────── OK\n ← confirmed ``` ## 如何运行 **前置条件:** Python 3.10+,`matplotlib`(`pip install matplotlib`) ``` # 终端 1 — 启动 vulnerable server python3 server.py # 终端 2 — 运行 attacker python3 attacker.py # attacker.py 完成后会写入 timing_results.csv。 # 生成 timing chart: python3 visualize.py # → 保存 timing_chart.png ``` **预期的攻击者输出:** ``` Timing side-channel attacker → 127.0.0.1:9999 [*] Calibrating noise floor … Baseline RTT mean=0.09 ms stdev=0.05 ms Chosen N 5 samples per candidate Pos Char p10(ms) Delta(ms) Runner-up Known so far ────────────────────────────────────────────────────────── [+] Position 0 → 'f' p10=6.3 ms delta=6.0 ms runner-up='z' known='f' [+] Position 1 → '3' p10=11.0 ms delta=4.6 ms runner-up='l' known='f3' ... [+] Position 7 → '1' p10=47.4 ms delta=3.2 ms runner-up='i' known='f3a9k2z1' [*] Server confirmed OK — password recovered after 8 chars. [=] Recovered password : 'f3a9k2z1' [=] Total time : 4.8 s ``` ## 复杂度降低 | 方法 | 所需尝试次数 | |---|---| | 暴力破解 | 36⁸ = **2,821,109,907,456** | | 计时攻击 | 36 × 8 = **288** (× N 个样本) | 计时攻击将搜索空间从约 2.8 万亿减少到 288 次候选探测——实现了约 **98 亿倍的缩减**——因为每个恢复的字符都是独立的:一旦第 *i* 个位置被锁定,就不再需要重新访问它。其复杂度为 O(|charset| × |password|),而不是 O(|charset|^|password|)。 ## 为什么使用 p10 而不是平均值或中位数? 负载下的 RTT 分布呈现**右偏态**:下限由服务器的处理时间决定,但偶尔的操作系统调度暂停、TCP 重传或上下文切换会导致一小部分样本的值激增。*平均值*和*中位数*都会吸收这些峰值,并将代表值拉离真实的处理下限。 **第 10 百分位数**从纯净的左侧尾部选择样本——即那些没有触发调度噪声的样本——从而为服务器的实际计算时间提供稳定的估计。这意味着错误的候选字符永远不会因为正确候选字符碰巧获得了“幸运”的样本而“显得更快”,并且即使 N 小到 5,正确的候选字符也能脱颖而出。 在嘈杂的 WAN 环境中,您可能需要将百分位数提高到 p25 或 p50 并增加 N;而在本地套接字上(如本例所示),p10 加上 N=5 就足够了。 ## 文件 | 文件 | 用途 | |---|---| | `server.py` | 故意存在漏洞的模拟身份验证服务器 | | `probe.py` | 阶段 2 健全性检查 — 确认计时信号可被检测到 | | `attacker.py` | 阶段 3 逐字符恢复密码 | | `visualize.py` | 根据 `timing_results.csv` 绘制阶梯状计时图表 | | `timing_results.csv` | 由 `attacker.py` 写入的原始每次探测延迟数据 | | `timing_chart.png` | 由 `visualize.py` 生成的输出图表 |
标签:Python, Red Teaming, RTT, 侧信道攻击, 信息泄露, 反取证, 安全测试, 安全评估, 密码学攻击, 密码还原, 微秒差异, 微秒级响应差异, 恶意样本开发, 攻击性安全, 数据包往返时间, 无后门, 时序攻击, 模拟身份验证, 演示项目, 百分位统计, 网络安全, 网络延迟测量, 网络抖动处理, 自适应统计批处理, 身份验证绕过, 远程时序分析, 隐私保护