别再只用AES-ECB了!手把手教你用Python复现CTF经典攻击,从密文块反推HTTP请求

张开发
2026/7/2 0:42:10 15 分钟阅读
别再只用AES-ECB了!手把手教你用Python复现CTF经典攻击,从密文块反推HTTP请求
从密文块到HTTP请求Python实战AES-ECB攻击全解析当你面对一段看似随机的密文时是否想过它可能隐藏着完整的HTTP请求这正是AES-ECB模式最危险的特性之一。作为CTF竞赛中最常见的加密漏洞ECB模式的攻击不仅考验着选手的密码学知识更是对实际编程能力的挑战。本文将带你用Python完整复现这一经典攻击场景从零开始构建攻击脚本最终实现从密文反推原始请求的完整过程。1. 为什么AES-ECB如此危险AES算法本身是安全的问题出在ECBElectronic Codebook模式的使用方式上。想象一下如果我们将一本字典中的每个单词都替换成固定的代码那么无论这本字典多么复杂相同的单词永远对应相同的代码。这就是ECB模式的核心缺陷——确定性加密。在HTTP请求这种高度结构化的数据中这种特性尤为致命。典型的请求包含大量重复的模式GET /api/user/1234 HTTP/1.1 Host: example.com Cookie: sessionabcd当这样的请求被ECB模式加密时每个16字节的块都会独立处理。结果是所有GET /api/user/开头的请求其第一个密文块完全相同相同的用户ID会产生相同的密文块协议版本HTTP/1.1也会生成固定模式的密文实战观察在CTF中你可以通过以下特征识别ECB模式密文长度是16的整数倍长文本加密后会出现重复的密文块修改明文中的单个字符只有对应的密文块发生变化2. 构建攻击环境我们需要准备以下工具来复现攻击from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad import base64 import os2.1 模拟加密服务首先创建一个模拟的加密服务它使用ECB模式加密任意HTTP请求class ECBCipher: def __init__(self, keyNone): self.key key if key else os.urandom(16) self.cipher AES.new(self.key, AES.MODE_ECB) def encrypt(self, plaintext): return base64.b64encode(self.cipher.encrypt(pad(plaintext.encode(), 16))) def decrypt(self, ciphertext): return unpad(self.cipher.decrypt(base64.b64decode(ciphertext)), 16).decode()2.2 生成测试用例让我们创建一个典型的API请求并加密它cipher ECBCipher() request GET /api/user/1678 HTTP/1.1\r\nHost: ctfdemo.com\r\n encrypted cipher.encrypt(request) print(f加密结果: {encrypted.decode()})示例输出加密结果: U2FsdGVkX1L5aW9jR2V0L2FwaS91c2VyLzE2Nzg3. 密文块分析实战3.1 解码和分块首先将Base64密文解码并分割成16字节的块def analyze_ciphertext(ciphertext): raw base64.b64decode(ciphertext) blocks [raw[i:i16] for i in range(0, len(raw), 16)] print(f总长度: {len(raw)}字节, 块数: {len(blocks)}) for i, block in enumerate(blocks): print(f块{i1}: {block.hex()})运行结果可能显示总长度: 48字节, 块数: 3 块1: 552f736467566b58312b4c356157396a 块2: 523256304c3246776157396a52325630 块3: 4c3246776157396a523256304c3246773.2 识别重复模式观察块2和块3的相似性我们可以推测重复的密文块对应着重复的明文结构HTTP请求中的路径分隔符(/)和协议(HTTP)可能产生了这种模式用户ID部分可能占据了独立的块4. 构造已知明文攻击4.1 建立字典通过猜测常见HTTP请求结构我们可以构建一个明文-密文映射表def build_dictionary(cipher, common_prefixes): dictionary {} for prefix in common_prefixes: # 填充至完整块 padded prefix.ljust(16, \x00) encrypted cipher.encrypt(padded)[:16] # 取第一个块 dictionary[encrypted] prefix return dictionary常见前缀示例common_http [ GET /api/user/, POST /api/login, HTTP/1.1\r\nHos, Cookie: session ]4.2 自动化匹配编写匹配函数将密文块与字典比对def decrypt_ecb(ciphertext, dictionary): raw base64.b64decode(ciphertext) blocks [raw[i:i16] for i in range(0, len(raw), 16)] result for block in blocks: if block in dictionary: result dictionary[block] else: result f[未知块: {block.hex()}] return result5. 完整攻击脚本将以上步骤整合成一个完整的攻击工具class ECBAttacker: def __init__(self): self.known_blocks {} def add_known_pair(self, plaintext, ciphertext): block base64.b64decode(ciphertext)[:16] self.known_blocks[block] plaintext[:16] def analyze(self, target): raw base64.b64decode(target) blocks [raw[i:i16] for i in range(0, len(raw), 16)] result [] for block in blocks: if block in self.known_blocks: result.append(self.known_blocks[block]) else: # 尝试基于位置的智能猜测 guess self._guess_block(block, len(result)) result.append(guess) return .join(result) def _guess_block(self, block, position): # 根据位置做出智能猜测 if position 0: return [疑似请求行] elif position 1: return [疑似请求头] return f[未知块{position}: {block.hex()}]使用示例attacker ECBAttacker() attacker.add_known_pair(GET /api/user/, U2FsdGVkX1L5aW9jR2V0L2FwaS91c2VyLzE2Nzg) target U2FsdGVkX1L5aW9jR2V0L2FwaS91c2VyLzE2Nzg print(attacker.analyze(target))6. 应对CTF中的变种挑战在实际CTF比赛中出题者会设置各种障碍增加难度。以下是几种常见变种及应对策略6.1 自定义填充方案有些题目会使用非标准的填充方式可以通过以下方法检测def check_padding(ciphertext): raw base64.b64decode(ciphertext) last_block raw[-16:] padding_byte last_block[-1] # 检查PKCS#7填充 if all(b padding_byte for b in last_block[-padding_byte:]): return PKCS#7 # 其他填充检测逻辑...6.2 混合编码攻击当密文经过多重编码时需要逆向处理def decode_layered(ciphertext): # 尝试Base64解码 try: decoded base64.b64decode(ciphertext) # 如果看起来像十六进制 if len(decoded) % 2 0: hex_str decoded.hex() if all(c in 0123456789abcdef for c in hex_str): return bytes.fromhex(hex_str) return decoded except: return ciphertext7. 防御ECB攻击的最佳实践虽然我们讨论了如何利用ECB的弱点但在实际开发中更应该关注如何避免这些漏洞永远不要在生产环境使用ECB模式优先选择经过验证的模式组合AES-GCM认证加密AES-CBC HMAC先加密后认证对静态数据使用随机IV实施完整的密钥管理方案# 安全加密示例 from Crypto.Cipher import AES from Crypto.Random import get_random_bytes def safe_encrypt(plaintext): key get_random_bytes(32) # AES-256 nonce get_random_bytes(16) cipher AES.new(key, AES.MODE_GCM, noncenonce) ciphertext, tag cipher.encrypt_and_digest(plaintext) return nonce ciphertext tag在CTF竞赛中遇到加密题目时记住这个检查清单确认加密模式和填充方案分析密文的长度和结构特征寻找可能的已知明文注入点尝试构建字典攻击考虑编码转换和复合攻击的可能性

更多文章