1. 从零理解AES加密机制
在CTF比赛中,AES加密题型通常以三种形式出现:ECB模式下的明文预测、CBC模式的IV攻击以及密钥爆破。AES作为目前最广泛使用的对称加密算法,采用分组加密方式处理数据块。其核心在于多轮的字节代换(SubBytes)、行移位(ShiftRows)、列混淆(MixColumns)和轮密钥加(AddRoundKey)操作。
以2023年HackTheBox赛事中的一道真题为例,题目给出了加密脚本和部分密文:
python复制from Crypto.Cipher import AES
import os
key = os.urandom(16)
iv = os.urandom(16)
cipher = AES.new(key, AES.MODE_CBC, iv)
with open('flag.txt', 'rb') as f:
flag = f.read()
padded = flag + b'\x00' * (16 - len(flag) % 16)
ciphertext = cipher.encrypt(padded)
print(iv.hex() + ciphertext.hex())
1.1 AES工作模式特征分析
CBC模式特有的初始化向量(IV)使用方式往往成为突破口。当观察到密文开头16字节被单独作为IV使用时,典型的攻击模式是:
- 提取前16字节作为IV
- 剩余部分作为实际密文
- 通过修改IV值影响首个明文块的解密结果
在ECB模式下,相同的明文块必然生成相同的密文块。这个特性使得我们可以通过观察密文的重复模式来推断明文结构。例如当发现密文中每16字节出现重复段时,很可能对应着标准化格式的头部或填充字段。
2. CBC字节翻转攻击实战
让我们通过一个完整案例演示如何利用CBC模式的特性进行攻击。假设获得如下加密数据:
code复制iv = '5477656e747974776f20466f72204f6e65'
ciphertext = '9c7dd04f9858d8f24823a3d5a84b5e3d'
2.1 攻击原理剖析
CBC模式解密时,前一个密文块会与当前块的解密结果进行异或操作。这意味着如果我们修改IV中的特定字节,将直接影响第一个明文块的对应位置:
code复制修改后的IV[0] = 原始IV[0] XOR 期望明文[0] XOR 当前明文[0]
使用Python实现攻击:
python复制from Crypto.Util.strxor import strxor
def cbc_flip(original_iv, ciphertext, pos, original_byte, desired_byte):
iv_bytes = bytes.fromhex(original_iv)
modified_iv = list(iv_bytes)
modified_iv[pos] ^= original_byte ^ desired_byte
return bytes(modified_iv).hex()
# 假设我们知道第一个明文字符是'T',想改为'F'
new_iv = cbc_flip(iv, ciphertext, 0, ord('T'), ord('F'))
2.2 实际CTF案例解析
在2022年西湖论剑CTF中,一道题给出了加密后的管理员令牌:
code复制IV: a8d2e8d2e8d2e8d2e8d2e8d2e8d2e8d2
Token: 1a4f3c...(后续省略)
通过分析发现令牌结构为:
code复制user=admin&role=user
使用字节翻转将role=user改为role=admin的关键步骤:
- 确定目标修改位置在第二个块(第16字节开始)
- 计算需要翻转的IV对应字节:
python复制# 原始明文第16字节是'r',需要改为'a' new_iv = cbc_flip(iv, ciphertext, 0, ord('r'), ord('a')) - 用修改后的IV提交令牌获得管理员权限
3. ECB模式下的字典攻击
当遇到ECB模式加密且密钥固定的情况时,可以采用选择明文攻击。以下是典型攻击流程:
3.1 构建已知前缀字典
- 准备包含所有可能字符的测试集
- 使用固定密钥加密"已知前缀+猜测字符"
- 记录每个字符对应的密文块
python复制from Crypto.Cipher import AES
def build_dict(known_prefix):
dictionary = {}
for c in range(256):
test = known_prefix + bytes([c])
cipher = AES.new(key, AES.MODE_ECB)
encrypted = cipher.encrypt(test.ljust(16, b'\x00'))
dictionary[encrypted[:16]] = c
return dictionary
3.2 逐字节破解密文
通过比较密文块与字典中的模式,可以逐个恢复明文:
python复制def ecb_decrypt(ciphertext, dictionary):
result = b''
for i in range(0, len(ciphertext), 16):
block = ciphertext[i:i+16]
if block in dictionary:
result += bytes([dictionary[block]])
return result
重要提示:实际CTF中常会限制加密次数,此时需要优化字典构建策略,采用分阶段逐步缩小范围的方法。
4. 密钥爆破与侧信道攻击
当其他方法失效时,针对弱密钥的暴力破解可能成为最后手段。以下是优化后的爆破策略:
4.1 基于已知明文的密钥校验
假设我们知道密文对应的部分明文(如flag格式为"flag{"开头),可以编写校验脚本:
python复制def brute_force(ciphertext, known_plaintext):
for i in range(0x1000000): # 限制爆破范围
key = (i).to_bytes(16, 'big')
cipher = AES.new(key, AES.MODE_ECB)
decrypted = cipher.decrypt(ciphertext)
if known_plaintext in decrypted:
return key
return None
4.2 时间侧信道攻击实战
某些实现会因密钥错误提前返回,通过精确计时可以发现密钥校验的差异:
python复制import time
def timing_attack(encrypted_data):
base_time = 0.1
candidates = []
for byte_pos in range(16):
times = []
for guess in range(256):
key = bytes([guess if i == byte_pos else 0 for i in range(16)])
start = time.time()
decrypt_with_key(encrypted_data, key) # 假设的解密函数
elapsed = time.time() - start
times.append((elapsed, guess))
candidates.append(max(times)[1])
return bytes(candidates)
5. 防御措施与出题思路
作为CTF选手,了解防御措施能更好逆向思考攻击路径:
5.1 安全的AES实现要点
- 始终使用随机且不可预测的IV
- 优先选择GCM等认证加密模式
- 对密钥进行适当的派生处理(如PBKDF2)
- 实施完整的HMAC校验
5.2 CTF出题常见陷阱
- 在CBC模式中故意重用IV
- ECB模式下设置可预测的明文结构
- 在密钥生成中使用弱随机源(如时间种子)
- 泄露错误信息(如Padding Oracle)
在一次真实渗透测试中,我们曾遇到系统使用固定IV加密会话令牌的情况。通过收集多个用户的令牌,可以构建出:
code复制用户A令牌: IV + Enc(用户A数据)
用户B令牌: IV + Enc(用户B数据)
由于IV相同,对两个密文进行异或操作实际上等同于对两个明文的异或,这为已知明文攻击创造了条件。
