文章总结: 文档详细讲解了一道名为fanfie的CTF密码学题目解法,核心在于识别Base32编码与仿射密码的组合加密机制。作者利用已知明文攻击推导出仿射密码的参数,构建解密映射表还原Base32字符串,最终解码得到flag。文章结构清晰,数学推导严谨,并提供了完整的Python解题脚本,适合密码学初学者学习已知明文攻击与古典密码分析方法。 综合评分: 90 文章分类: CTF,逆向分析
CTF Crypto 题解:fanfie —— Base32 + 仿射密码的组合加密
原创
破镜安全 破镜安全
破镜安全
2026年3月5日 08:00 四川
CTF Crypto 题解:fanfie —— Base32 + 仿射密码的组合加密
题目信息
- 题目名称:fanfie
- 题目类型:Crypto(密码学)
- 附件:
cd0e90cf13d54d048177008444b29c0b.txt
附件内容只有一行:
MZYVMIWLGBL7CIJOGJQVOA3IN5BLYC3NHI
题目描述告诉我们:flag 被转换为 Base32 编码,然后以某种方式加密。
第一步:理解 Base32 编码
在正式分析密文之前,需要先了解 Base32 是什么。
Base32 是一种将任意二进制数据编码为可打印 ASCII 字符的编码方案。它使用以下 32 个字符作为字母表:
字符: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 2 3 4 5 6 7
数值: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
注意:Base32 的字符集不包含数字 0 和 1(避免与字母 O、I 混淆),以及大写字母 A-Z 加上数字 2-7,共 32 个字符,每个字符代表一个 5 位的二进制值(因为 2^5 = 32)。
Base32 编码时,每 5 个字节(40 位)的原始数据被编码为 8 个 Base32 字符,不足时用 = 号填充。
第二步:确定明文结构,提取已知明文
根据题目描述和 CTF 比赛的惯例,该比赛的 flag 格式为 BITSCTF{...}。
我们观察密文长度:MZYVMIWLGBL7CIJOGJQVOA3IN5BLYC3NHI 共 35 个字符。
已知:
- 原始明文先经过 Base32 编码,再经过某种加密得到密文
- Base32 编码结果只包含
A-Z和2-7这 32 个字符
这意味着密文本身就是一个被加密过的 Base32 字符串。我们可以对密文做字符级别的统计分析。
现在,利用已知的 flag 前缀 BITSCTF{ 来做已知明文攻击。
先对 BITSC(flag 的前 5 个字节)做 Base32 编码:
import base64
base64.b32encode(b"BITSC")
# 输出: b'IJEVIU2D'
所以 flag 明文 BITSCTF{...} 经过 Base32 编码后,开头 8 个字符为 IJEVIU2D。
这 8 个字符对应密文的开头 8 个字符为 MZYVMIWL。
我们得到了 8 组已知的明文-密文字符对:
明文 Base32: I J E V I U 2 D
密文: M Z Y V M I W L
第三步:判断加密方式——单字母替换密码
观察以上对应关系:
- 明文中
I出现了两次(位置 1 和位置 5),密文中两次都对应M - 相同的明文字符始终对应相同的密文字符
- 不同的明文字符对应不同的密文字符(V 对应 V,但其他字符都发生了变化)
这是典型的**单字母替换密码(Monoalphabetic Substitution Cipher)**的特征:明文字母表中每个字符固定映射到密文字母表中的一个字符,且这个映射是一一对应的。
单字母替换密码的常见形式包括:凯撒密码、ROT13、仿射密码等。
第四步:识别仿射密码,推导参数
现在将已知对应关系转换为数值(使用 Base32 字母表中的位置编号):
明文字符 -> 数值 密文字符 -> 数值
I -> 8 M -> 12
J -> 9 Z -> 25
E -> 4 Y -> 24
V -> 21 V -> 21
U -> 20 I -> 8
2 -> 26 W -> 22
D -> 3 L -> 11
仿射密码的加密公式为:
E(x) = (a * x + b) mod m
其中:
x是明文字符的数值m是字母表大小(这里 m = 32)a、b是待确定的参数,且a必须与m互质(即 gcd(a, 32) = 1)
利用任意两对已知明文-密文对建立方程组:
取 I(8) -> M(12) 和 J(9) -> Z(25):
(a * 8 + b) mod 32 = 12 ...(1)
(a * 9 + b) mod 32 = 25 ...(2)
(2) – (1):
a * (9 - 8) mod 32 = (25 - 12) mod 32
a mod 32 = 13
所以 a = 13
将 a = 13 代入方程 (1):
13 * 8 + b ≡ 12 (mod 32)
104 + b ≡ 12 (mod 32)
8 + b ≡ 12 (mod 32) (因为 104 mod 32 = 8)
b = 4
验证 a = 13 与 m = 32 是否互质:
gcd(13, 32) = 1 ✓(13 是质数,且不是 2 的因子)
用全部已知对验证加密公式 E(x) = (13 * x + 4) mod 32:
| 明文字符 | 明文数值 x | 计算值 (13x+4) mod 32 | 密文数值 | 密文字符 | 是否匹配 | | — | — | — | — | — | — | | I | 8 | (104+4) mod 32 = 12 | 12 | M | 匹配 | | J | 9 | (117+4) mod 32 = 25 | 25 | Z | 匹配 | | E | 4 | (52+4) mod 32 = 24 | 24 | Y | 匹配 | | V | 21 | (273+4) mod 32 = 21 | 21 | V | 匹配 | | U | 20 | (260+4) mod 32 = 8 | 8 | I | 匹配 | | 2 | 26 | (338+4) mod 32 = 22 | 22 | W | 匹配 | | D | 3 | (39+4) mod 32 = 11 | 11 | L | 匹配 |
所有对均验证通过,确认加密参数为 a = 13,b = 4,m = 32。
第五步:构建完整的加密字母表(辅助理解)
用加密公式 E(x) = (13x + 4) mod 32 计算 Base32 字母表中每个字符的加密映射:
明文: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 2 3 4 5 6 7
密文: E R 6 L Y F S 7 M Z G T A N 2 H U B O 3 I V C P 4 J W D Q 5 K X
这是一张完整的替换表,明文中任意一个 Base32 字符都可以在这张表中找到对应的密文字符。
第六步:解密——求仿射密码的逆变换
已知加密公式 E(x) = (a * x + b) mod m,解密公式为:
D(y) = a_inv * (y - b) mod m
其中 a_inv 是 a 在模 m 意义下的乘法逆元,满足:
a * a_inv ≡ 1 (mod m)
求 a = 13 在模 32 下的逆元,即找整数 a_inv 使得 13 * a_inv ≡ 1 (mod 32):
13 * 5 = 65 = 2 * 32 + 1
所以 13 * 5 mod 32 = 1
a_inv = 5
解密公式为:
D(y) = 5 * (y - 4) mod 32
第七步:对密文逐字符解密
对密文 MZYVMIWLGBL7CIJOGJQVOA3IN5BLYC3NHI 的每个字符应用解密公式:
base32_alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
a_inv = 5
b = 4
m = 32
ciphertext = "MZYVMIWLGBL7CIJOGJQVOA3IN5BLYC3NHI"
plaintext_b32 = ""
for c in ciphertext:
ci = base32_alpha.index(c) # 找到字符的数值
pi = (a_inv * (ci - b)) % m # 仿射解密
plaintext_b32 += base32_alpha[pi] # 还原字符
解密结果为:
密文: MZYVMIWLGBL7CIJOGJQVOA3IN5BLYC3NHI
仿射解密后: IJEVIU2DKRDHWUZSKZ4VSMTUN5RDEWTNPU
第八步:Base32 解码,还原 flag
仿射解密后得到的字符串 IJEVIU2DKRDHWUZSKZ4VSMTUN5RDEWTNPU 就是原始 flag 经过 Base32 编码后的形式。
注意 Base32 解码时需要考虑填充。Base32 每 8 个字符为一组,不足时用 = 填充。IJEVIU2DKRDHWUZSKZ4VSMTUN5RDEWTNPU 共 35 个字符,35 mod 8 = 3,需要补 6 个 = 号。
(Base32 填充规则:编码长度必须是 8 的倍数,35 个字符的下一个 8 的倍数是 40,需要补 5 个 =。实际上标准补 6 个也被接受,用 Python 的 base64 库验证即可。)
import base64
base64.b32decode('IJEVIU2DKRDHWUZSKZ4VSMTUN5RDEWTNPU======')
# 输出: b'BITSCTF{S2VyY2tob2Zm}'
得到 flag:BITSCTF{S2VyY2tob2Zm}
完整解题脚本
import base64
# Base32 字母表(32个字符,每个字符代表0-31的数值)
base32_alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
m = 32
# 仿射密码参数(通过已知明文攻击推导得出)
a, b = 13, 4
a_inv = 5 # 13 的模 32 乘法逆元:13 * 5 ≡ 1 (mod 32)
# 目标密文
ciphertext = "MZYVMIWLGBL7CIJOGJQVOA3IN5BLYC3NHI"
# 第一步:仿射解密,还原 Base32 编码字符串
plaintext_b32 = ""
for c in ciphertext:
ci = base32_alpha.index(c)
pi = (a_inv * (ci - b)) % m
plaintext_b32 += base32_alpha[pi]
# 第二步:Base32 解码,添加 padding
flag = base64.b32decode(plaintext_b32 + "======").decode()
print(f"密文: {ciphertext}")
print(f"仿射解密后: {plaintext_b32}")
print(f"flag: {flag}")
运行结果:
密文: MZYVMIWLGBL7CIJOGJQVOA3IN5BLYC3NHI
仿射解密后: IJEVIU2DKRDHWUZSKZ4VSMTUN5RDEWTNPU
flag: BITSCTF{S2VyY2tob2Zm}
知识点总结
1. 已知明文攻击(Known-Plaintext Attack)
在 CTF 中,flag 格式通常是已知的(如 BITSCTF{)。利用这部分已知信息,可以直接推导出密钥参数,而不需要穷举所有可能的密钥。这是密码分析中最基础也最重要的技术手段之一。
2. 仿射密码(Affine Cipher)
仿射密码是凯撒密码的推广。凯撒密码只有加法偏移(即 a=1 的特例),而仿射密码同时包含乘法和加法两种变换:
- 加密:E(x) = (a * x + b) mod m
- 解密:D(y) = a_inv * (y – b) mod m
约束条件:a 必须与 m 互质,这是为了保证加密映射是一一对应的(双射),从而可以唯一解密。
3. 组合编码与加密
本题的加密流程是:明文 -> Base32编码 -> 仿射加密 -> 密文。理解这个流程是解题的关键。仿射加密作用在 Base32 字符层面,而非原始字节层面,因此需要用 Base32 字母表(32个字符)作为仿射密码的字母表,模数 m = 32。
4. 题目名称的彩蛋
题目名称 “fanfie” 是 “affine”(仿射)的字母变位(anagram),暗示了加密算法的类型。在 CTF 解题中,题目名称往往包含重要提示。
Flag
BITSCTF{S2VyY2tob2Zm}
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:破镜安全 破镜安全 破镜安全《CTF Crypto 题解:fanfie —— Base32 + 仿射密码的组合加密》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论