第七章哈希函数与攻击7.1哈希函数基础哈希函数是将任意长度的输入映射为固定长度输出称为哈希值、摘要或指纹的函数。安全哈希函数应满足的性质确定性相同输入总是产生相同输出抗原像性Preimage Resistance给定哈希值h难以找到m使得H(m) h抗第二原像性Second Preimage Resistance给定m1难以找到m2≠m1使得H(m1) H(m2)抗碰撞性Collision Resistance难以找到任意两个不同的m1、m2使得H(m1) H(m2)雪崩效应输入的微小变化导致输出的巨大变化算法输出长度安全性备注MD5128位已不安全已发现碰撞不应用于安全场景SHA-1160位已不安全2017年Google实现了碰撞SHAtteredSHA-256256位安全SHA-2家族目前广泛使用SHA-3可变安全基于Keccak海绵结构非Merkle-Damgård7.2哈希碰撞攻击生日攻击基于生日悖论对于n位哈希值期望在约2^(n/2)次尝试后找到碰撞。例如MD5128位理论上需要约2^64次运算。MD5碰撞使用fastcoll工具可以在几秒内生成两个具有相同MD5值的不同文件。CTF中常见考法提交两个不同的文件使其MD5相同。SHA-1碰撞SHAtteredGoogle团队在2017年展示了首个实际SHA-1碰撞构造了两个不同但SHA-1相同的PDF文件。7.3哈希长度扩展攻击Length Extension Attack适用算法MD5、SHA-1、SHA-256等基于Merkle-Damgård结构的哈希函数。不适用于SHA-3海绵结构和HMAC。攻击原理Merkle-Damgård结构的哈希函数按块迭代处理输入。H(m)的结果就是处理完所有块后的内部状态。如果知道H(secret || message)和message的长度攻击者可以将H(secret || message)作为新的内部状态从这个状态继续追加数据在不知道secret的情况下计算H(secret || message || padding || extension)攻击条件已知H(secret || message)的值已知message的内容已知或可猜测secret的长度使用基于Merkle-Damgård结构的哈希函数攻击工具HashPump, hash_extenderimport hashpumpy import hashlib from urllib.parse import quote class HashLengthExtender: CTF 专用哈希长度扩展攻击工具类 支持MD5, SHA1, SHA256, SHA512 def __init__(self, algorithmmd5): self.algorithm algorithm # 映射算法名称到 hashpumpy 支持的格式 self.algo_map { md5: md5, sha1: sha1, sha256: sha256, sha512: sha512 } def attack(self, original_hash_hex, original_data_bytes, append_data_bytes, secret_length): 执行长度扩展攻击 参数: original_hash_hex: 原始哈希值 (Hex字符串) original_data_bytes: 原始数据 (bytes) append_data_bytes: 要追加的数据 (bytes) secret_length: 密钥长度 (int) 返回: (new_hash_hex, new_data_bytes): 新哈希值和新数据 algo self.algo_map.get(self.algorithm.lower()) if not algo: raise ValueError(f不支持的算法: {self.algorithm}) # 调用 hashpumpy # 注意hashpumpy.hashpump 返回的是 (new_hash_hex, new_data_bytes) new_hash, new_data hashpumpy.hashpump( original_hash_hex, original_data_bytes, append_data_bytes, secret_length, algo ) return new_hash, new_data def url_encode_payload(self, data_bytes): 将 payload 转换为 URL 编码格式 (Web 题常用) # safe 表示所有非字母数字字符都编码 return quote(data_bytes, safe) # # 使用示例 # if __name__ __main__: # --- 场景配置 --- # 假设服务端逻辑md5(SECRET userguest) SECRET bsupersecretkey16 # 实际攻击中不知道这个只知道长度 ORIGINAL_DATA buserguest APPEND_DATA broleadmin # 我们想追加的内容 SECRET_LENGTH 16 # 1. 模拟获取原始哈希 (攻击者已知) original_hash hashlib.md5(SECRET ORIGINAL_DATA).hexdigest() print(f--- 场景: MD5 长度扩展攻击 ---) print(f原始数据: {ORIGINAL_DATA}) print(f原始哈希: {original_hash}) print() # --- 执行攻击 --- extender HashLengthExtender(algorithmmd5) try: new_hash, new_data extender.attack( original_hash, ORIGINAL_DATA, APPEND_DATA, SECRET_LENGTH ) print(f--- 攻击结果 ---) print(f新哈希 (伪造签名): {new_hash}) print(f新数据 (原始字节): {new_data}) # Web 题中通常需要 URL 编码发送 url_payload extender.url_encode_payload(new_data) print(f新数据 (URL编码): {url_payload}) print() # --- 本地验证 --- # 模拟服务端接收数据并验证 # 服务端逻辑md5(SECRET received_data) verify_hash hashlib.md5(SECRET new_data).hexdigest() print(f--- 验证 ---) print(f服务端计算哈希: {verify_hash}) if verify_hash new_hash: print(✅ 攻击成功哈希值匹配。) print(f 构造的最终 URL: ?data{url_payload}sign{new_hash}) else: print(❌ 攻击失败哈希值不匹配。) except Exception as e: print(f发生错误: {e})7.4彩虹表与字典攻击原理预计算大量常见密码的哈希值建立密码→哈希的查找表。遇到目标哈希时直接查表。在线查询服务cmd5.com中文最大的哈希查询网站crackstation.net支持多种哈希算法的在线查询hashcat基本用法#!/bin/bash # # Hashcat 实战命令库 # 版本: 2026.04 # 描述: 整理常用的 Hashcat 攻击模式、掩码定义及性能参数 # # --- 基础变量定义 --- HASH_FILEhash.txt # 包含哈希值的文件 WORDLISTrockyou.txt # 字典文件 OUTPUT_FILEfound.txt # 破解结果输出文件 RULE_FILEbest64.rule # 规则文件 (位于 hashcat/rules/ 目录下) # # 1. 基础信息与环境检查 # # 查看版本信息 hashcat --version # 查看支持的哈希类型 (查找特定算法如 MD5) hashcat --help | grep MD5 # 基准测试 (测试当前硬件的破解速度非常重要) hashcat -b # 查看设备信息 (OpenCL/CUDA 设备) hashcat -I # # 2. 常见哈希类型 (-m 参数速查) # # 0 MD5 # 100 SHA1 # 1400 SHA256 # 1000 NTLM (Windows 密码) # 3200 Bcrypt # 11600 7-Zip (压缩包) # 17220 ZIP (压缩包) # 22000 WPA/WPA2 (WiFi) # # 3. 攻击模式详解 (-a 参数) # # --- 模式 0: 字典攻击 (直攻) --- # 最基础的模式逐行尝试字典中的密码 echo 模式 0: 字典攻击 # hashcat -m 0 -a 0 $HASH_FILE $WORDLIST -o $OUTPUT_FILE # --- 模式 1: 组合攻击 --- # 将两个字典进行笛卡尔积组合 (例如: dict1 中的 admin dict2 中的 123 admin123) echo 模式 1: 组合攻击 # hashcat -m 0 -a 1 $HASH_FILE dict1.txt dict2.txt -o $OUTPUT_FILE # --- 模式 3: 掩码暴力攻击 --- # 适用于知道密码格式的情况 (如: 8位数字, 6位小写字母等) echo 模式 3: 掩码攻击 # 示例 A: 6位纯数字 (如验证码, 手机锁屏) # hashcat -m 0 -a 3 $HASH_FILE ?d?d?d?d?d?d # 示例 B: 8位字母数字混合 (不区分大小写) # hashcat -m 0 -a 3 $HASH_FILE ?h?h?h?h?h?h?h?h # 示例 C: 8位任意字符 (包含特殊符号速度慢) # hashcat -m 0 -a 3 $HASH_FILE ?a?a?a?a?a?a?a?a # 示例 D: 自定义字符集 (例如只包含 a,b,c,1,2,3) # hashcat -m 0 -a 3 $HASH_FILE -1 abc123 ?1?1?1?1?1 # --- 模式 6: 混合字典掩码 (字典在左) --- # 在字典单词后追加字符 (例如: password 2位数字) echo 模式 6: 混合攻击 (字典掩码) # hashcat -m 0 -a 6 $HASH_FILE $WORDLIST ?d?d # --- 模式 7: 混合掩码字典 (掩码在左) --- # 在字典单词前添加字符 (例如: 2位数字 password) echo 模式 7: 混合攻击 (掩码字典) # hashcat -m 0 -a 7 $HASH_FILE ?d?d $WORDLIST # --- 模式 0 规则攻击 --- # 使用规则引擎变换字典单词 (如: 首字母大写, 末尾加数字) echo 模式 0 规则: 智能变换 # hashcat -m 0 -a 0 $HASH_FILE $WORDLIST -r $RULE_FILE # # 4. 常用性能与优化参数 # # -O : 开启优化内核 (推荐能显著提升速度但限制密码最大长度) # -w 3 : 设置工作负载 (1-4, 3为高性能4为疯狂模式注意散热) # --force : 强制运行 (忽略警告如没有GPU或权限问题时) # --show : 显示已破解的密码 # --remove : 破解成功后从哈希文件中删除该行 (节省时间) # -i : 开启增量模式 (配合掩码攻击自动从短到长尝试) # 示例: 优化的掩码攻击命令 # hashcat -m 0 -a 3加盐Salt防御在密码哈希前加入随机字符串salt使相同密码产生不同的哈希值使彩虹表攻击失效。7.5实战案例哈希长度扩展攻击题目场景Web服务使用md5(SECRET query_string)作为请求签名。你是普通用户已知一个有效请求query_string actionviewuserguest对应签名为sig a8f5f167f44f4964e6c998dee827110c。SECRET长度为12字节。你需要构造一个包含actionadmin的合法请求。分析攻击条件已知哈希值sig✓已知原始数据query_string✓已知secret长度12✓使用MD5Merkle-Damgård结构✓import hashpumpy from urllib.parse import quote import hashlib class LengthExtensionAttack: def __init__(self, algorithmmd5): self.algorithm algorithm def generate_payload(self, original_hash, original_data, append_data, secret_length): 生成攻击 Payload # 执行长度扩展攻击 # 注意hashpumpy 默认处理 MD5其他算法需指定 new_hash, new_data hashpumpy.hashpump( original_hash, original_data, append_data, secret_length, self.algorithm ) return new_hash, new_data def url_encode(self, data): URL 编码处理 safe 表示保留 和 符号不编码方便阅读也可以去掉 safe 参数全部编码 return quote(data, safe) # # 实战场景配置 # if __name__ __main__: # 1. 题目已知信息 original_hash a8f5f167f44f4964e6c998dee827110c original_data bactionviewuserguest append_data bactionadmin secret_length 12 # 如果不知道通常需要从 1 到 20 爆破 print(f--- 开始攻击 ---) print(f原始数据: {original_data}) print(f原始签名: {original_hash}) print(f目标追加: {append_data}) print(- * 30) # 2. 执行攻击 attacker LengthExtensionAttack(algorithmmd5) new_hash, new_data attacker.generate_payload( original_hash, original_data, append_data, secret_length ) # 3. 结果处理 url_encoded attacker.url_encode(new_data) print(f新签名: {new_hash}) print(f新数据 (Hex): {new_data.hex()}) print(f新数据 (URL编码): {url_encoded}) print(- * 30) # 4. 构造最终请求 final_request f?query{url_encoded}sig{new_hash} print(f最终请求: {final_request}) # # 本地验证 (模拟服务端逻辑) # # 假设我们猜对了 secret 长度并且知道 secret 内容用于验证实际攻击中不知道 # 这里仅用于演示验证逻辑是否正确 print(\n--- 本地验证 (模拟服务端) ---) # 注意因为不知道真实 secret这里无法真实验证 md5(secret data) # 但如果题目给了验证接口你可以用上面的 final_request 去请求 # 仅演示如果我们知道 secret 是 testsecret12 (长度12) mock_secret btestsecret12 # 服务端接收到的数据其实是original_data padding append_data # 也就是 new_data server_calc_hash hashlib.md5(mock_secret new_data).hexdigest() # 如果攻击成功new_hash 应该等于 server_calc_hash # (这里因为 mock_secret 是我瞎编的所以大概率不相等除非原题 secret 也是这个) # 但在 CTF 中只要服务端不报错且返回 admin 页面即视为成功 print(f如果服务端 Secret 长度为 12且算法为 MD5) print(f攻击 Payload 已生成请尝试提交。)7.6 哈希算法的“万能胶”——Hash Injection (Flask/Jinja2)背景在Web开发中Python的Flask框架及其模板引擎Jinja2非常流行。在2026年的今天虽然Flask已迭代多代但老版本遗留的“哈希比较漏洞”依然是CTF中Web题的经典考点。原理当Web应用使用或比较哈希值且后端语言如Python、PHP将某些特定字符串如0e\d解析为科学计数法0的任意次方等于0时就会产生逻辑漏洞。CTF场景题目通常是一个登录系统要求输入的密码哈希值与数据库中的哈希值“相等”。攻击者不需要知道原密码只需要找一个哈希值以0e开头的字符串。代码示例# 模拟存在漏洞的验证逻辑 def vulnerable_check(input_pass, stored_hash): # 用户控制的 input_pass 经过哈希后与 stored_hash 比较 user_hash md5(input_pass.encode()).hexdigest() # 危险的比较方式如果解释器将字符串转为数字0e123 0e456 会成立 return user_hash stored_hash # 攻击载荷寻找 Hash 以 0e 开头的字符串 # 例如md5(240610708) 0e462097431906509019562988736854 # 如果 stored_hash 是另一个 0e 开头的哈希验证就会通过7.7 中文环境下的特殊攻击面 (cmd5.com 特色)背景结合你提到的cmd5.com和当前地点中国浙江CTF题目中经常出现针对中文用户习惯的哈希。内容补充中文弱口令中国用户的常用密码往往是拼音数字如zhangsan123或键盘序列如1qaz2wsx。在字典攻击中需要专门针对中文环境的字典如darkc0de的中文变种。编码差异GBK vs UTF-8在某些老旧的Web系统尤其是国内老系统中输入可能使用 GBK 编码。攻击点同一个汉字在 GBK 和 UTF-8 下的字节流不同导致哈希值完全不同。如果题目背景是“破解一个老式OA系统的密码”可能需要考虑编码转换。实战技巧# 例子同一个汉字“密”在不同编码下的哈希 import hashlib text 密 md5_utf8 hashlib.md5(text.encode(utf-8)).hexdigest() md5_gbk hashlib.md5(text.encode(gbk)).hexdigest() print(fUTF-8 MD5: {md5_utf8}) # 不同的值 print(fGBK MD5: {md5_gbk}) # 不同的值 # 在CTF中如果已知目标系统是老式Windows简体中文版可能需要尝试GBK编码。7.8 哈希猫池与API自动化 (2026年现状)背景你提到的cmd5.com和crackstation.net都提供了API。在2026年的CTF自动化脚本编写中选手通常不会手动查表而是编写“哈希中控台”。内容补充多线程哈希破解脚本介绍如何编写一个脚本同时调用多个在线查询接口cmd5, hashkiller, myhashfw等并处理API的并发请求和验证码CAPTCHA绕过。代码示例伪代码/框架import requests import threading class HashCracker: def __init__(self): self.api_keys { cmd5: YOUR_KEY, hashkiller: ANOTHER_KEY } def query_cmd5(self, hash_value): # cmd5.com 的 API 接口调用 url https://cmd5.com/api.aspx data {hash: hash_value, key: self.api_keys[cmd5]} response requests.post(url, datadata) return response.json() def query_hashkiller(self, hash_value): # hashkiller 的接口逻辑 pass def crack(self, hash_list): results {} threads [] # 创建多线程并发查询 for h in hash_list: t threading.Thread(targetself.query_all_apis, args(h,)) threads.append(t) t.start() for t in threads: t.join() return results # 这种自动化工具在处理大量哈希如日志分析、批量解密时非常有用。7.9 针对慢哈希的绕过 (Bcrypt/Argon2)背景现代Web应用如Django, Flask-Security不再使用MD5/SHA而是使用Bcrypt或Argon2。虽然它们很难被彩虹表攻击但在CTF中常出现配置错误。内容补充配置错误导致的弱安全性轮数过低Bcrypt 的安全性依赖于rounds/cost参数。如果题目中设置的 cost4正常是 12那么就可以使用暴力破解。长度截断某些旧版 Bcrypt 实现只取密码的前 72 个字节。如果题目是“找回一个超长密码”可能只需要爆破前 72 字节。工具推荐hashcat模式 3200 (Bcrypt) 在 2026 年已经可以通过 FPGA 或云 GPU 集群进行高速破解如果 cost factor 较低的话。7.10 总结与防御 (Defense in Depth)内容补充在章节结尾总结如何在 2026 年正确使用哈希永远不要使用 MD5/SHA1即使是加盐也因为算力提升变得不安全。使用专用算法对于密码存储使用Argon2id(winner of Password Hashing Competition) 或scrypt。HMAC 替代拼接为了防止长度扩展攻击不要使用Hash(secret message)而应该使用标准的HMAC-SHA256算法。HMAC 代码示例防御 Length Extensionimport hmac import hashlib # 正确的做法 secret bsuper_secret_key message buserguest # 使用 HMAC内部结构是 hash((key ⊕ opad) || hash((key ⊕ ipad) || message)) # 这种结构天然免疫长度扩展攻击 digest hmac.new(secret, message, hashlib.sha256).hexdigest() print(digest)