1. 古典密码:从brainfuck到栅栏密码
刚接触CTF密码学的新手,最容易遇到的就是各种古典密码题目。这类题目通常不需要复杂的数学知识,主要考察对编码规则的理解和工具使用能力。以NewStarCTF 2025第一周的题目为例,我们先来看三种典型的古典密码。
1.1 brainfuck:符号背后的秘密
第一道题目给出了一段由+><[]-.组成的奇怪代码:
++++++++[>>++>++++>++++++>++++++++>++++++++++>++++++++++++>++++++++++++++>++++++++++++++++>++++++++++++++++++>++++++++++++++++++++>++++++++++++++++++++++>++++++++++++++++++++++++>++++++++++++++++++++++++++>++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++<<<<<<<<<<<<<<<<-]>>>>>>>++++++.>----.<-----.>-----.>-----.<<<<-.>>++..<.>.++++++.....------.<.>.<<<<<<<+++.>>>>+.<<<<+++++++.>>>>+.<<<<-------.>>>>-.<<<<+.+++++++.--..>>>>---.-.<<<<<-.+++.>>>>.<<<<<-------.+.>>>>>>++.这其实是brainfuck语言编写的程序。这种语言只有8种符号,但能实现图灵完备的计算。实战中不需要手动解析,使用在线解释器(如https://www.splitbrain.org/services/ook)直接运行就能得到flag:
flag{Oiiaioooooiai#b7c0b1866fe58e12}避坑指南:遇到特殊符号时,先考虑是否是已知的编码或编程语言。CTF中常见的还有Ook!、Malbolge等小众语言。
1.2 凯撒密码:字母的位移游戏
第二题给出密文:
kqfl{hf3x4w'x_h1umjw_n5_a4wd_3fed}观察花括号结构,这显然是flag的变形。凯撒密码的特点是字母按固定位移替换,使用CyberChef的ROT13工具尝试所有位移(ROT3-ROT25),发现ROT2时得到可读文本:
flag{ca3s4rs_c1pher_i5_v4ry_3azy}进阶技巧:当密文包含数字和符号时,可以编写Python脚本处理:
def caesar_decrypt(ciphertext, shift): result = "" for char in ciphertext: if char.isalpha(): base = ord('a') if char.islower() else ord('A') result += chr((ord(char) - base - shift) % 26 + base) else: result += char return result1.3 栅栏密码:文字的重新排列
第三题密文:
fa{ereigtepanet6680}lgrodrn_h_litx#8fc3栅栏密码的特点是将明文按"之"字形排列后逐行读取。使用在线工具(如https://www.boxentriq.com/code-breaking/rail-fence-cipher)尝试不同栏数,发现栏数为2时解密成功:
flag{reordering_the_plaintext#686f8c03}实战要点:
- 栅栏数通常为2-10
- 密文长度是栅栏数的整数倍
- 可以观察是否有规律间隔出现的字符(如原题中
f和l的间距)
2. 混合编码:Base家族与UUencode
现代CTF题目常将多种编码方式组合使用,考察选手的编码识别能力。来看NewStarCTF的典型题目:
2.1 Base64/Base32混合编码
题目给出三部分flag:
part 1: ZmxhZ3tkYXp6bGluZ19lbmNvZGluZyM0ZTBhZDQ= part 2: MYYGGYJQHBSDCZJRMQYGMMJQMMYGGN3BMZSTIMRSMZSWCNY= part 3: =8S4U,3DR8SDY,C`S-F5F-C(S,S<R-C`Q9F8S87T`解题步骤:
第一部分明显是Base64(以
=结尾的特征),解码得到:import base64 print(base64.b64decode("ZmxhZ3tkYXp6bGluZ19lbmNvZGluZyM0ZTBhZDQ=")) # b'flag{dazzling_encoding#4e0ad4'第二部分长度是56字符,符合Base32特征(5的倍数):
print(base64.b32decode("MYYGGYJQHBSDCZJRMQYGMMJQMMYGGN3BMZSTIMRSMZSWCNY=")) # b'f0ca08d1e1d0f10c0c7afe422fea7'第三部分包含
,和`等符号,是UUencode特征:from codecs import decode print(decode("=8S4U,3DR8SDY,C`S-F5F-C(S,S<R-C`Q9F8S87T`", 'uu')) # b'c55192c992036ef623372601ff3a'
最终组合得到完整flag:
flag{dazzling_encoding#4e0ad4f0ca08d1e1d0f10c0c7afe422fea7c55192c992036ef623372601ff3a}2.2 编码识别技巧
| 编码类型 | 特征 | 常见结尾符 |
|---|---|---|
| Base64 | A-Za-z0-9+/ | =或== |
| Base32 | A-Z2-7 | =序列 |
| UUencode | 首字符为M,包含很多标点符号 | ` |
实用命令:
# Linux系统自带解码工具 echo "Zm..." | base64 -d echo "MY..." | base32 -d echo "=8..." | uudecode3. 现代密码学:RSA实战突破
RSA是CTF中最常见的非对称加密算法,NewStarCTF 2025的babyrsa题目展示了典型场景:
3.1 基础RSA解密
题目给出:
n = 17290066070594979571009663381214201320459569851358502368651245514213538229969915658064992558167323586895088933922835353804055772638980251328261 e = 65537 c = 14322038433761655404678393568158537849783589481463521075694802654611048898878605144663750410655734675423328256213114422929994037240752995363595解题步骤:
使用factordb.com分解n:
p = 132086377753166933499766611941800337310934338951519 q = 130972866061794536676655924442991410247854192126419计算私钥d:
from Crypto.Util.number import * phi = (p-1)*(q-1) d = inverse(e, phi)解密得到flag:
m = pow(c,d,n) print(long_to_bytes(m)) # b'flag{us4_s1ge_t0_cal_phI}'
3.2 Wiener攻击破解小d
当私钥d很小时(如题目中d是32位素数),可以使用Wiener攻击:
def wiener_attack(e, n): # 连分数展开 def continued_frac(x, y): cf = [] while y: cf.append(x // y) x, y = y, x % y return cf # 渐进分数计算 def gradual_frac(cf): gf = [] for i in range(1, len(cf)+1): numerator = 0 denominator = 1 for x in cf[:i][::-1]: numerator, denominator = denominator, x*denominator + numerator gf.append((numerator, denominator)) return gf cf = continued_frac(e, n) gf = gradual_frac(cf) for d, k in gf: if d.bit_length() == 32: # 已知d是32位 return d攻击原理:当d < (1/3)n^(1/4)时,通过e/n的连分数展开可以快速恢复d。
4. 对称密码实战:从XOR到AES
4.1 XOR加密破解
题目给出密文:
e9e3eee8f4f7bffdd0bebad0fcf6e2e2bcfbfdf6d0eee1ebd0eabbf5f6aeaeaeaeaeaef2XOR加密的关键在于找到密钥,这里采用已知明文攻击:
c = bytes.fromhex('e9e3...') key = c[0] ^ ord('f') # 假设flag以f开头 flag = bytes([x ^ key for x in c]) # flag{x0r_15_symm3try_and_e4zy!!!!!!}4.2 AES-CBC模式攻击
题目给出:
a = 3657491768215750635844958060963805125333761387746954618540958489914964573229 c = b'>]\xc1\xe5\x82/\x02\x7ft\xf1B\x8d\n\xc1\x95i'通过分析源码可知:
- key是a的前16字节重复两次
- iv = key[:16] ^ key[16:] ^ 1
解密脚本:
key = long_to_bytes(a)[:16] iv = bytes_to_long(key) ^ bytes_to_long(long_to_bytes(a)[16:]) ^ 1 cipher = AES.new(key*2, AES.MODE_CBC, long_to_bytes(iv)) print(cipher.decrypt(c)) # b'flag{firsT_cry_Aes}\x00\x00\x00'CBC模式要点:
- 需要正确的IV值
- 填充错误可能导致解密失败
- 可以通过修改IV实现位翻转攻击
5. 仿射密码与维吉尼亚密码
5.1 仿射密码解密
题目密文:
dd4388ee428bdddd5865cc66aa5887ffcca966109c66edcca920667a88312064仿射加密公式:c = (a*p + b) mod 256。通过已知明文建立方程:
# 已知flag头是'f','l','a','g' s = Solver() a, b = Int('a'), Int('b') s.add(a * ord('f') + b == cipher[0]) s.add(a * ord('l') + b == cipher[1]) if s.check() == sat: model = s.model() a_val = model[a].as_long() b_val = model[b].as_long()解密得到:
flag{4ff1ne_c1pher_i5_very_3azy}5.2 维吉尼亚密码破解
题目密文:
pqcq{qc_m1kt4_njn_5slp0b_lkyacx_gcdy1ud4_g3nv5x0}使用工具https://www.guballa.de/vigenere-solver自动分析,发现密钥是"kfc":
flag{la_c1fr4_del_5ign0r_giovan_batt1st4_b3ll5s0}破解技巧:
- 先确定密钥长度(Kasiski测试)
- 对每组字母进行频率分析
- 使用重合指数验证
6. 实战工具链与学习路径
6.1 必备工具推荐
| 工具名称 | 用途 | 链接 |
|---|---|---|
| CyberChef | 各种编码/加密在线解密 | https://gchq.github.io/CyberChef |
| RsaCtfTool | RSA自动攻击工具 | https://github.com/Ganapati/RsaCtfTool |
| XORTool | XOR加密分析 | https://github.com/hellman/xortool |
| SageMath | 高级数学运算 | https://www.sagemath.org |
6.2 系统学习建议
古典密码:
- 熟悉凯撒、栅栏、摩斯等传统密码
- 掌握频率分析等基本技巧
现代密码:
- 理解RSA、AES等算法数学原理
- 学习常见攻击方式(如选择密文攻击)
实战训练:
- 从CTFtime找适合新手的比赛
- 定期刷Cryptohack.org的题目
我在实际解题中发现,密码学题目往往需要多次尝试。比如有一次遇到看似是Base64的字符串,实际需要先进行字母替换才能正常解码。这种经验只能通过大量练习积累。