CTF BUUOJ Cipher:Playfair 密码解密实战--从“公平的玩吧”到获取 Flag
·
CTF BUUOJ Cipher:Playfair 密码解密实战–从“公平的玩吧”到获取 Flag
一、题目描述
在做 CTF 密码学题目时,遇到了一道非常有意思的题目,题目描述如下:
Cipher
1
还能提示什么呢?公平的玩吧(密钥自己找)Dncnoqqfliqrpgeklwmppu
注意: 得到的 flag 请包上
flag{}提交, flag{小写字母}
二、题目分析
1. 确定加密算法
题目中最关键的提示是中文 “公平的玩吧”。
在密码学中,提到“公平”,最经典的对应算法就是 Playfair Cipher(Playfair 密码)。
Playfair 密码(Playfair cipher)是一种对称式密码,也是第一种双字母替换密码。它的基本原理是使用一个 5x5 的矩阵来对明文进行加密。
2. 确定密钥
题目中提到“密钥自己找”。既然提示已经指向了 Playfair 算法,根据 CTF 出题的常见套路,我们大胆猜测密钥就是算法的名字本身。
因此,猜测密钥为:playfair。
三、手动解密过程
为了深入理解算法,我们先尝试手动解密。
1. 构造密钥矩阵
Playfair 使用一个 5x5 的矩阵。构造规则如下:
- 去除密钥中的重复字母。
- 将剩余字母填入矩阵。
- 剩余位置按字母表顺序填充(通常 I/J 合并,标准 Playfair 中 J 被 I 替代)。
密钥:playfair
去重后得到序列:p,l,a,y,f,i,r
填充剩余字母,得到最终的 5x5 矩阵:
| | 1 | 2 | 3 | 4 | 5 |
|—|—|—|—|—|—|
| 1 | P | L | A | Y | F |
| 2 | I | R | B | C | D |
| 3 | E | G | H | K | M |
| 4 | N | O | Q | S | T |
| 5 | U | V | W | X | Z |
2. 分组解密
密文:Dncnoqqfliqrpgeklwmppu
将密文两两分组:Dn, cn, oq, qf, li, qr, pg, ek, lw, mp, pu
Playfair 解密规则(与加密相反):
- 同行:向左移动一位。
- 同列:向上移动一位。
- 不同行不同列:对角线交换(矩形法则),取同行的另一个角。
具体解密步骤解析:
- Dn: D(2,5), N(4,1) -> 不同行列 -> 取对角 I(2,1), T(4,5) -> IT
- cn: C(2,4), N(4,1) -> 不同行列 -> 取对角 I(2,1), S(4,4) -> IS
- oq: O(4,2), Q(4,3) -> 同行左移 -> N(4,1), O(4,2) -> NO
- qf: Q(4,3), F(1,5) -> 不同行列 -> 取对角 T(4,5), A(1,3) -> TA
- li: L(1,2), I(2,1) -> 不同行列 -> 取对角 P(1,1), R(2,2) -> PR
- qr: Q(4,3), R(2,2) -> 不同行列 -> 取对角 O(4,2), B(2,3) -> OB
- pg: P(1,1), G(3,2) -> 不同行列 -> 取对角 L(1,2), E(3,1) -> LE
- ek: E(3,1), K(3,4) -> 同行左移 -> M(3,5), H(3,3) -> MH
- lw: L(1,2), W(5,3) -> 不同行列 -> 取对角 A(1,3), V(5,2) -> AV
- mp: M(3,5), P(1,1) -> 不同行列 -> 取对角 E(3,1), F(1,5) -> EF
- pu: P(1,1), U(5,1) -> 同列上移 -> U(5,1), N(4,1) -> UN
拼接解密结果:ITISNOTAPROBLEMHAVEFUN
整理明文并去除填充:IT IS NOT A PROBLEM HAVE FUN(明文为:这不是个问题,玩得开心)
四、Python 脚本自动化解密
在实际比赛中,为了节省时间,我们可以编写 Python 脚本来自动处理。以下是完整的解密代码:
def playfair_decrypt(ciphertext, key):
"""
Playfair 密码解密函数
:param ciphertext: 密文
:param key: 密钥
:return: 明文
"""
# 1. 预处理密钥:去重、转大写、J转I
key = key.upper().replace('J', 'I')
seen = set()
processed_key = []
for char in key:
if char not in seen and char.isalpha():
seen.add(char)
processed_key.append(char)
# 2. 生成字母表并构建矩阵
# 标准Playfair字母表不含J
alphabet = "ABCDEFGHIKLMNOPQRSTUVWXYZ"
for char in alphabet:
if char not in seen:
processed_key.append(char)
seen.add(char)
# 构建 5x5 矩阵
matrix = [processed_key[i:i+5] for i in range(0, 25, 5)]
# 辅助函数:查找字符在矩阵中的位置
def find_pos(char):
for r in range(5):
for c in range(5):
if matrix[r][c] == char:
return r, c
return None
# 3. 解密逻辑
ciphertext = ciphertext.upper().replace('J', 'I')
plaintext = ""
# 每两个字符一组进行解密
for i in range(0, len(ciphertext), 2):
char1 = ciphertext[i]
char2 = ciphertext[i+1]
r1, c1 = find_pos(char1)
r2, c2 = find_pos(char2)
# 规则 1: 同一行 -> 左移一位
if r1 == r2:
plaintext += matrix[r1][(c1 - 1) % 5]
plaintext += matrix[r2][(c2 - 1) % 5]
# 规则 2: 同一列 -> 上移一位
elif c1 == c2:
plaintext += matrix[(r1 - 1) % 5][c1]
plaintext += matrix[(r2 - 1) % 5][c2]
# 规则 3: 矩形对角交换
else:
plaintext += matrix[r1][c2]
plaintext += matrix[r2][c1]
return plaintext
if __name__ == "__main__":
# 题目数据
cipher_text = "Dncnoqqfliqrpgeklwmppu"
key = "playfair"
# 执行解密
result = playfair_decrypt(cipher_text, key)
# 格式化输出 Flag
# 题目要求 flag{小写字母}
flag = f"flag{{{result.lower()}}}"
print("-" * 30)
print(f"Key: {key}")
print(f"Plaintext: {result}")
print(f"Flag: {flag}")
print("-" * 30)
运行结果:
------------------------------
Key: playfair
Plaintext: ITISNOTAPROBLEMHAVEFUN
Flag: flag{itisnotaproblemhavefun}
------------------------------
五、总结
这道题的考点主要有两个:
- 脑洞与联想:通过“公平”二字联想到 Playfair 密码。
- 基础密码学知识:掌握 Playfair 密码的矩阵构造和解密规则。
最终的 Flag 为:
flag{itisnotaproblemhavefun}
希望这篇 WriteUp 对你有所帮助!如果有任何疑问,欢迎在评论区留言交流。
觉得有用的话,点个赞再走吧!👍
更多推荐

所有评论(0)