题解一:自定义AES变形加密(AES-ECB分组碰撞爆破)
一、题目简介
本题为中等难度分组密码CTF赛题,基于标准AES算法进行自定义改造,采用极不安全的ECB电子密码本模式加密Flag,同时开放用户自定义明文加密接口。核心考点为AES-ECB模式固有安全缺陷、明文可控下的分组密文碰撞攻击、多分组逐字节爆破逻辑,规避了基础编码解密,聚焦分组密码底层漏洞利用,适合密码学进阶学习者练习。
题目核心特性:固定16字节密钥、自定义PKCS7填充、Flag明文固定格式、用户可控明文加密、无初始向量IV。
二、题目完整源码
python from Crypto.Cipher import AES import os
#固定密钥(题目内置,不可获取) KEY = os.urandom(16) FLAG = "flag{cust0m_aes_ecb_vuln_2026}"
def pad(s): # 自定义pkcs7填充 pad_len = 16 - len(s) % 16 return s + chr(pad_len)*pad_len
def encrypt(plain): aes = AES.new(KEY, AES.MODE_ECB) return aes.encrypt(pad(plain.encode())).hex()
# 交互逻辑 if __name__ == "__main__": print("Welcome to Custom AES Encrypt!") print("1. Encrypt your own text") print("2. Get flag ciphertext") while True: opt = input("Input your option: ") if opt == "1": text = input("Input your text: ") print("Ciphertext:", encrypt(text)) elif opt == "2": print("Flag Ciphertext:", encrypt(FLAG)) else: print("Error option!") |
三、漏洞原理深度分析
1. AES-ECB模式致命缺陷
AES-ECB是最简单、最不安全的分组加密模式,无初始向量IV、无分组混淆逻辑,加密规则为:相同的16字节明文分组,会生成完全一致的密文分组。分组长度固定为16字节,十六进制展示为32位字符,所有分组独立加密、互不干扰。
相较于CBC、GCM等安全模式,ECB无法抵御重放、碰撞、明文爆破攻击,是CTF分组密码高频漏洞考点。
2.题目可利用条件
- 明文可控:用户可任意输入明文并获取对应密文,可自定义分组内容;
- Flag格式固定:以flag{开头、}结尾,字符范围为可打印字符,爆破空间可控;
- 密文可获取:可直接读取完整Flag加密密文,用于分组碰撞匹配;
- Flag跨多分组:区别于入门单分组题目,本题Flag占用多个16字节分组,需要分段精准爆破,难度进阶。
3.核心攻击逻辑
通过占位填充偏移,逐步构造明文分组,让用户自定义分组与Flag未知分组完全重合。利用ECB密文分组唯一性,通过密文碰撞匹配正确字符,逐字节枚举爆破,最终拼接完整Flag。
四、分步解题思路
- 获取目标密文:调用题目接口获取完整Flag加密密文,按32位十六进制(16字节)为单位拆分多个密文分组;
- 构造偏移填充:使用固定字符(A)占位,通过递减填充长度,控制未知Flag字符落在分组末尾;
- 逐字符枚举爆破:基于已知Flag前缀,拼接待枚举可打印字符,加密后匹配对应密文分组;
- 循环拼接终止:匹配成功则记录字符、拓展已知Flag内容,循环迭代直至读取到结尾},终止爆破;
- 输出完整Flag:整合所有爆破字符,得到完整明文Flag。
五、完整EXP脚本
python import string from pwn import *
#连接本地/远程题目服务 io = process("encrypt.py") # io = remote("localhost", 9999)
# 获取flag密文 def get_flag_cipher(): io.sendlineafter(b"option: ", b"2") io.recvuntil(b"Flag Ciphertext: ") return io.recvline().strip().decode()
# 自定义明文加密 def get_user_cipher(text): io.sendlineafter(b"option: ", b"1") io.sendlineafter(b"text: ", text.encode()) io.recvuntil(b"Ciphertext: ") return io.recvline().strip().decode()
def aes_ecb_brute(): flag = "" charset = string.printable.strip() # 所有可打印字符集 flag_cipher = get_flag_cipher() block_size = 32 # 16字节分组 = 32位十六进制字符
# 逐字节爆破,预估flag最大长度60位 for i in range(60): # 计算填充长度,制造分组偏移 pad = "A" * (15 - i % 16) target_block = i // 16
# 枚举所有可打印字符,匹配正确位 for char in charset: test_plain = pad + flag + char test_cipher = get_user_cipher(test_plain) # 分组密文碰撞匹配 if test_cipher[:block_size] == flag_cipher[target_block*block_size:(target_block+1)*block_size]: flag += char print(f"Current flag: {flag}") break # 读取到flag结尾,终止爆破 if "}" in flag: break print(f"\n[+] Final Flag: {flag}")
if __name__ == "__main__": aes_ecb_brute() |
六、运行结果与最终答案
脚本运行后逐位枚举匹配,实时输出爆破进度,最终完整输出:
Final Flag: flag{cust0m_aes_ecb_vuln_2026}
七、题目总结与学习要点
本题是CTF密码学AES考点的中等难度标杆题型,摒弃了简单的密钥泄露、直接解密套路,重点考察对ECB分组加密底层逻辑的理解和手动爆破逻辑的实现。核心学习要点:
- 深刻理解AES-ECB模式的安全缺陷,明确相同明文分组对应相同密文分组的核心特性;
- 掌握分组偏移、占位填充、密文碰撞的攻击思路,适配多分组加密场景;
- 学会针对固定格式明文的逐字节爆破方法,可迁移至所有ECB模式漏洞题目。