刚接触CTF的时候,很多人一打开Crypto(密码学)方向的题目就是一脸懵:给一串不明觉厉的字符、一份看不懂的脚本、或者一个打不开的加密文件,完全不知道从哪里下手。我自己也是从零基础一路踩坑过来的,所以特别理解这种“看了WP(WriteUp,解题报告)觉得不难,真拿到题还是不会”的状态。这一篇系列开篇文章,我打算先把“CTF里的密码学到底在解什么”这件事彻底讲透,帮零基础的朋友建立起一个清晰的知识框架,再附上真正能落地的入门路线和第一波实操。
1. 先搞清楚:CTF里的Crypto到底在解什么
1.1 一道Crypto题目的真实结构
CTF里的Crypto题目,和教科书上的密码学完全不是一回事。教科书告诉你怎么设计一个安全的加密系统,CTF则反着来:给你一个“被加密/处理过”的结果,让你想办法还原出原始信息,这个原始信息里往往藏着flag(一般是flag{...}这种格式)。
一道典型的入门级Crypto题,通常由三部分构成:
- 一段密文:可能是一串Base64字符、十六进制字符串、一串看起来像乱码的bytes,甚至是一张图片里藏着的字符。
- 一段脚本(可选):出题人用来加密flag的Python代码,很多时候会直接附在题目附件里。
- 一个目标:从密文反推出flag。有的题目密文就是flag直接编码后的结果,有的则要求你在理解了加密流程之后写脚本反推。
举个例子,一道最简单的题可能是这样的:
密文:ZmxhZ3t5MHVfZ290X3RoMVN9如果你能一眼看出这是Base64编码,解码后得到flag{y0u_got_th1S},这道题就做完了。但事实上,真实比赛里的题目远没有这么直白,考点会层层嵌套,这也是为什么我把这个系列命名为“解什么”——解的不是密文本身,而是“出题人到底用了什么手法”这个谜题。
1.2 识别能力永远比计算能力重要
我见过太多新手一上来就死磕数学原理,觉得不懂数论就做不了RSA。这个认知需要纠正:CTF比赛的Crypto,本质上是“模式识别+逆向思维”的游戏,而不是数学考试。
你真正需要的核心能力是:看到一段密文,能快速判断出它可能属于哪一类加密方式,然后调用对应的工具或脚本去处理。换句话说,你需要“认得出”Base64的特征(大小写字母+数字++/结尾的=)、认得出凯撒密码的特征(字母错位但保留字母表)、认得出RSA的特征(给了n、e、c三个大整数)。
这种识别能力怎么练?没有捷径,就是多看多解。做题做多了,你看到一串字符,大脑会自动弹出“这像Base64”“这像是十六进制”“这像是栅栏密码”的直觉。我自己的经验是:入门阶段先别追求自己推导加密算法,而是先建立“识别→解码→验证”的肌肉记忆,等接触得多了再回头补数学原理,效率会高很多。
1.3 主流的题目分类速览
在动手之前,先给零基础的朋友一张“地图”,知道Crypto板块大致有哪些山头的题:
| 分类 | 典型考点 | 难度 | 解题侧重点 |
|---|---|---|---|
| 编码类 | Base64、Hex、URL编码、ASCII、ROT13 | 入门 | 识别+工具调用 |
| 古典密码 | 凯撒、维吉尼亚、仿射、栅栏、培根 | 入门-简单 | 字母频率分析+爆破 |
| 现代密码 | 哈希(MD5/SHA)、AES/DES、分组模式 | 中等 | 理解模式+脚本编写 |
| 公钥密码 | RSA、ECC、ElGamal | 中等-困难 | 数学原理+攻击脚本 |
| 特殊类型 | 随机数预测、侧信道、隐藏信息 | 困难 | 按场景定制 |
新人入门的路径非常清晰:先掌握编码类和古典密码(这是最容易拿分的),再过渡到哈希和对称加密,最后才啃RSA这块硬骨头。千万别一上来就挑战高难度RSA题,那只会打击信心。
2. 入坑第一周:工具、环境与一条通用解题思路
2.1 工具准备:三个够用就行的武器
工欲善其事,必先利其器。零基础入门Crypto,不需要装一堆花里胡哨的软件,下面这三个工具就足够你撑过前两个月。
第一个是CyberChef。这是一个网页版的数据处理工具,被大家叫作“网络瑞士军刀”。它最大的优点是把几十种常见的编码、加密、哈希操作做成了拖拽式模块,你只需要把密文拖进输入框,然后拖一个“From Base64”模块过去,结果就出来了。不需要安装、不需要配置,打开浏览器就能用,对新手极其友好。
第二个是Python环境。如果说CyberChef是快手菜,那Python就是正式的厨具。很多题目的加密脚本是Python写的,你需要读懂它,并且写脚本反推。我建议用Anaconda或者直接装官方Python 3,再配合一个IDE(比如VS Code或PyCharm社区版),就足够了。基础的pycryptodome库(提供AES、DES等加密算法的Python实现)需要提前装好,命令一行搞定:
pip install pycryptodome第三个是现成的解密网站。dCode、cmd5这类网站虽然看起来不太起眼,但关键时刻真的能救急。尤其是dCode,提供了大量古典密码的在线破解工具,支持频率分析、自动爆破。我遇到陌生加密方式时,第一反应常常就是去dCode碰碰运气,很多时候真的能直接解出来。
2.2 一条通用的五步解题流程
拿到一道Crypto题之后,不要慌,按下面这条流程走,大部分情况下能理出头绪。
- 读题:仔细看题目描述和附件文件名,出题人经常会把提示藏在描述里,比如“Caesar was here”“public key”这种单词,直接指向考点。
- 认格式:观察密文的字符组成。如果只有大小写字母和数字,可能是Base64或Hex;如果只是字母且保持了大小写,可能是凯撒或维吉尼亚;如果是大整数,多半是RSA。
- 试编码:先用CyberChef批量试一遍常见的编码解码(Base64、Hex、URL、Unicode),很多题到这一步就结束了。
- 看脚本:有Python附件就打开读,理清加密流程。重点关注密钥怎么来的、填充方式是什么、有没有明显的“漏洞”(比如随机数固定、公钥指数太小)。
- 搜索+写脚本:识别出加密算法后,如果不会解,先搜有没有现成脚本或在线工具,搜不到再自己写。写不出来就检索这类算法的常见攻击方式(比如RSA的小公钥指数攻击、共模攻击),找到对应的Python脚本模板改一改。
这套流程听起来简单,但真到了比赛里,能救很多人。我最开始做题的时候,经常在第五步卡住,后来发现其实是第三步没做彻底——有些题是Base64编码套了一层十六进制再套一层URL编码,多嵌套几次就认不出来了。
2.3 Python脚本需要掌握到什么程度
零基础的朋友可能担心:我不会写Python怎么办?我的建议是:不需要系统学完整个Python语言,掌握下面这些就足够入门Crypto了。
- 字符串和字节串的基本操作(
encode()、decode()、bytes.fromhex()) - 循环和条件语句
for循环里的爆破(for key in range(26)这类)- 调用第三方库(
from Crypto.Cipher import AES这类)
这些语法练个两三天就能上手。Crypto题目的常规解题脚本,说白了就是把加密过程逆向写一遍,或者暴力枚举密钥空间。不需要你写出多么优雅的代码,能跑出flag就是胜利。
再分享一个实战中很有用的调试技巧:写解密脚本时,先打印中间步骤的输出,确认每一步的结果是否符合预期。比如解密AES之前,先确认密钥和初始向量的类型是不是字节串,这个细节我踩过无数次坑。
3. 从零手解三道典型入门题:编码、古典密码、RSA初体验
3.1 第一题:双层编码的识别与拆解
先看一道我在入门阶段遇到过的经典题,密文是:
NjM3YzYzNjM2YzYzNjM2YzYzNjM2YzYzNjM2YzYzNjM2YzYzDQo=新手拿到这串东西,第一眼会觉得像Base64(以=结尾,字符由大小写字母和数字组成)。如果你直接丢到CyberChef里,用From Base64解一次,得到的结果是:
637c63636c63636c63636c63636c63636c63636c63636c这一串全是0-9和a-f的字符,标准的十六进制格式。继续用From Hex解一次,得到:
clclclclclclclclclclclcl再仔细一看,cl重复出现,其实这是flag被某种方式替换后的结果。把cl看成一组,对照flag四个字母的位置,你会发现f->c、l->l、a->c……实际上这是一个简单的ROT(旋转)替换,偏移量固定。这个例子想说明一个关键点:Crypto题很少有单层加密,多数是多层嵌套。每一层解出来的结果都可能变成下一层的输入,所以你必须学会“剥洋葱”。
我把这个思路整理成一条很实用的经验:只要拿到密文,先把URL解码、Base64解码、Hex解码、ROT13这些最基础的“净化操作”全部过一遍。如果解出来还是可读格式,就继续解,直到结果变成一串意义不明但格式完整的文本或出现flag字样为止。
3.2 第二题:凯撒密码的频率分析爆破
古典密码里,最有代表性的就是凯撒密码(Caesar Cipher)。它的加密原理很简单:把每个字母按照字母表顺序往后移动固定的位数。比如偏移3时,a变成d,b变成e,以此类推。
破解凯撒密码的核心思路是枚举密钥。因为字母表只有26个字母,偏移量也就只有26种可能,直接暴力试一遍就行。这里我给出一个非常简短的Python脚本,枚举所有偏移并输出可读文本:
def caesar_brute_force(ciphertext): for shift in range(26): plain = "" for ch in ciphertext: if ch.isalpha(): base = ord('A') if ch.isupper() else ord('a') plain += chr((ord(ch) - base + shift) % 26 + base) else: plain += ch print(f"shift {shift:2d}: {plain}") caesar_brute_force("Dwwdfn dw gdzq dw qrq0")如果密文足够长,打印出的26条结果里,总有一条读起来通顺,那条对应的偏移量就是真正的密钥。这个方法一旦熟练,处理凯撒题就是半分钟内的事。
稍微进阶一点的维吉尼亚密码(Vigenère Cipher)也没那么可怕,它相当于用一组循环的凯撒偏移量来加密。破解时先通过计算重复子串的距离推断密钥长度,再对每一列分别做频率分析。这个过程手算太麻烦,直接用dCode这类在线工具,粘贴密文进去基本上几秒钟就能出结果。我的建议是:古典密码这类题,优先靠工具,把时间省下来去研究更需要动脑的题目。
3.3 第三题:RSA的第一个攻击点——小公钥指数
RSA是Crypto板块的重头戏,也是很多新手觉得劝退的地方。但入门RSA其实没有想象中那么难,先从一个最经典也最简单的攻击场景说起:小公钥指数攻击。
RSA加密中,c = m^e mod n,其中e是公钥指数。很多出题人不小心把e设成3(本来是为了加密速度更快),如果m^3 < n,那么加密结果c其实就是m^3的完整值,根本没有经过取模。这种情况下,直接对c开三次方根就能还原m。
题目一般会给你三个值n、e、c,解题脚本长这样:
from Crypto.Util.number import long_to_bytes from sympy import integer_nthroot n = 0x... e = 3 c = 0x... m, exact = integer_nthroot(c, e) if exact: print(long_to_bytes(m))这里用到了sympy库的integer_nthroot函数,它能对大整数进行精确的整数开方运算。exact变量用来判断开方结果是否是整数,如果是整数,说明m^3确实没有超过n,也就成功解出了明文。
我当初第一次跑通这个脚本的时候,真的有一种“原来数学还能这么玩”的感觉。这也是Crypto的迷人之处:它考的不是死记硬背,而是你能不能发现算法在特定参数下的“破绽”。随着你后面学到更多攻击方法(共模攻击、低解密指数攻击、维纳攻击等),你会逐渐意识到,RSA题目本质上是“出题人故意留破绽,你来当侦探”的游戏。
3.4 实战:手把手解一道多层嵌套的入门综合题
把前面这些知识点串起来,咱们来解一道我临时设计的综合性入门题。题目附件是一个脚本:
from Crypto.Util.number import bytes_to_long, getPrime import base64 flag = "flag{example_flag}" p = getPrime(512) q = getPrime(512) n = p * q e = 3 m = bytes_to_long(flag.encode()) c = pow(m, e, n) data = base64.b64encode(str(c).encode()).decode() with open("cipher.txt", "w") as f: f.write(data)而在cipher.txt里,你看到的是一串Base64:
MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OQ==现在按照五步法来:
第一步,读题。脚本里e=3,这就是最大的提示,基本上可以锁定小公钥指数攻击。
第二步,认格式。cipher.txt里明显是Base64,先解码:
import base64 c = int(base64.b64decode(open("cipher.txt", "rb").read()).decode()) print(c)第三步,直接用小公钥指数的思路,对c开三次方根:
from sympy import integer_nthroot from Crypto.Util.number import long_to_bytes m, exact = integer_nthroot(c, 3) print(exact) if exact: print(long_to_bytes(m).decode())第四步,如果exact是True,直接输出明文,flag到手。如果exact是False,说明密文取过模,需要用更高级的攻击手段,不过这道题不会那么复杂。
这道题虽然是我自己拼接的,但它的解题思路和真实比赛里的入门题几乎一模一样:识别脚本漏洞(小e)、处理编码(Base64)、编写逆向脚本。希望大家通过这道题,能把前两节的内容真正串联起来。
4. 现代密码学考点速览:哈希、对称加密与RSA扩展
4.1 哈希题:识别与“破解”真相
哈希(Hash)在CTF题目里也经常出现,但很多人对它的理解有偏差:哈希不是加密,它是一种不可逆的摘要算法(MD5、SHA-1、SHA-256等)。你没法把e99a18c428cb38d5f260853678922e03直接“解密”成abc123,因为哈希函数本身就是单向的。
那CTF里怎么考哈希?入门阶段最常见的是两种:
- 哈希查找:给一个哈希值,让你在彩虹表或在线库里查到原始字符串。比方说管理员密码的MD5值泄露了,你去cmd5这类网站搜一下,往往能直接搜出明文。
- 哈希长度扩展攻击:这个稍后进阶一点会遇到,它利用了MD5/SHA-1这类Merkle-Damgard结构哈希的弱点,可以在不知道原始消息的情况下,把新内容附加到消息后面并计算出合法的新哈希值。
入门阶段,你只需要掌握第一种就够应付大部分简单题了。说实话,哈希题是最没技术含量但也最容易拿分的一类,看到明显是32位或40位的十六进制字符串,先丢到在线哈希查询网站去搜一圈,十有八九能搜出来。
4.2 对称加密:AES题目的三个核心参数
AES这类对称加密在CTF里属于中等难度,出题人通常会配合各种“不安全的模式”来考你。拿到一道AES题,你需要从脚本或描述里提取三个关键参数:
- 密钥(key):最简单的题目可能直接硬编码在脚本里,那你就能直接解密;复杂的题目则可能要求你通过其他手段(比如已知明文攻击)推断出密钥。
- 初始向量(IV):CBC等模式下需要IV才能正确解密,如果出题人把IV固定为全零或者写在题目附件里,这就是一个可以利用的点。
- 分组模式和填充方式:ECB模式存在一个著名的特点——相同的明文块产生相同的密文块,这可以用来做字节级的模式分析。CBC模式如果IV固定或可控,则可能引发字节翻转攻击。
我建议入门阶段先自己写一遍AES的加密解密流程,把pycryptodome库的基本用法混个脸熟,后面再慢慢接触各种攻击手法。下面是一段标准CBC模式的解密模板,可以直接保存备用:
from Crypto.Cipher import AES from Crypto.Util.Padding import unpad key = bytes.fromhex("...") iv = bytes.fromhex("...") ciphertext = bytes.fromhex("...") cipher = AES.new(key, AES.MODE_CBC, iv) plaintext = unpad(cipher.decrypt(ciphertext), AES.block_size) print(plaintext.decode())4.3 RSA的其他入门攻击面
RSA的考点简直是Crypto界的半壁江山,这里再补充两个入门后不久就会遇到的攻击面。
第一个是共模攻击。如果出题人用同一个n,但两个不同的指数e1、e2加密了同一条明文,你就获得了两个密文c1、c2。数学上可以证明,只要gcd(e1, e2) = 1,就一定存在整数a、b使得a*e1 + b*e2 = 1,于是可以用扩展欧几里得算法求出m = c1^a * c2^b mod n。这个攻击经常出现在两个人共享同一个模数、但各自使用不同公钥指数的场景里。
第二个是低解密指数攻击(维纳攻击)。当私钥指数d比较小(通常小于n^(1/4))时,可以通过连分数逼近的方法还原出d。这种攻击有现成的脚本,遇到d取值小的情况直接套模板,不过入门阶段先了解即可,等RSA基础扎实了再去深入实现。
我想强调的是:学习RSA攻击方法,最好的方式不是背公式,而是把每种攻击对应到一个“出题场景”里。比如看到e=3,脑子里立刻浮现“小公钥指数攻击”和“开三次方根”的画面;看到同一个n出现两对e和c,立刻想到“共模攻击”。这种“场景-手法”的对应关系,就是Crypto解题能力提升的核心。
5. 零基础高频翻车现场与避坑记录
5.1 常见问题速查表
以下是我带过不少新人、自己也踩过无数坑之后,整理出来的高频问题速查表。建议直接截图收藏。
| 问题现象 | 根本原因 | 解决办法 |
|---|---|---|
| Base64解出来是乱码 | 可能不是纯Base64,而是Base64后再Hex/压缩 | 继续对结果尝试各种解码 |
Python解密时报错bytes-like object is required | 密钥或密文是字符串,不是字节串 | 调用.encode()或bytes.fromhex()转换类型 |
解出来的字符串没有flag{}格式 | 可能结果还需要经过一层ROT或字符替换 | 对所有可能偏移量做循环尝试 |
| RSA脚本跑得很慢 | 可能用了效率低的算法或数值过大 | 优先用pow内置函数,避免自写大数运算 |
| 在线解密网站解不出来 | 密文可能被自定义字符集编码过 | 观察字符集是否包含+、/以外字符,考虑自定义Base |
| 明文有可读单词但整体不通顺 | 可能是维吉尼亚或替换密码,不止一层凯撒 | 用dCode的自动检测功能多跑几个算法 |
5.2 新人最常犯的四个思维误区
误区一:一定要把整个加密算法完全理解才动手。恰恰相反,先动手解,解完再回头看原理,效率更高。很多新人卡在第一步就是觉得“我还不懂RSA数学原理”,于是一个星期过去了还在看数论,一道题都没做。我的建议是:直接拿题目练手,不会什么就查什么,以题带学比系统看书快得多。
误区二:只解自己擅长的题型。有的人编码题解得好,就一直做编码题,碰到古典密码就跳过。这样练习的覆盖面太窄,比赛时遇到其他题型就只能干瞪眼。建议大家按照题型的难度梯度,各类型都刷上二三十道,建立全面的识别能力。
误区三:忽视题目描述和文件名。Crypto的出题人其实很“善良”,经常在描述或文件命名里给提示。比如caesar.txt明确告诉你这是凯撒,rsa_public_key.pem告诉你这是RSA。只看密文不看题面,等于把自己眼睛蒙上一半做题。
误区四:不习惯看别人的WriteUp。有的朋友觉得看WP等于作弊,其实完全不是这样。Crypto题目的解题思路常常是一层窗户纸,你不去捅破就想破头也想不出来。正确做法是先自己努力一阵子,如果实在没有头绪,就去看WP,看懂后一定要亲自把题目重做一遍,把思路内化成自己的,这样才真正有收获。
5.3 入门期的训练节奏建议
最后给零基础的朋友一个可执行的训练计划。第一个月,不要贪多,按以下节奏走:
- 第一周:掌握CyberChef的常用模块,刷30道左右的编码类题目(各个CTF平台的入门题都行),把Base64、Hex、URL、ROT13这些解码做到闭着眼睛都能操作。
- 第二周:学习古典密码的识别和破解,重点是凯撒、维吉尼亚、仿射。建议自己在本地跑一遍dCode的解密流程,理解频率分析的原理,顺便写一个凯撒爆破脚本练手。
- 第三周:过一遍Python操作
pycryptodome库的基本接口,写AES-CBC的加密和解密各一遍,再尝试解两三道对称加密入门题。 - 第四周:正式进入RSA,先掌握
n、e、c三件套的理解、小公钥指数攻击和共模攻击,把脚本跑通,每道题都尝试不看WP独立复现。
按照这个节奏,一个月后你已经具备独立解决简单Crypto题的能力了。
说到训练平台,有一个很重要的小建议:刚开始别贪图高难度比赛,先在本地搭建一个训练环境,或者使用面向新手的练习平台,把上面的分类题库刷一遍。很多公开的入门题库都自带WriteUp,做完一道看一道,学到的经验会非常扎实。
6. 一个小技巧:善用搜索引擎和“复现消耗”法
6.1 遇到不会的题,怎么搜最有效
这里额外分享一个我后来才琢磨明白的技巧:遇到不熟悉的概念或报错信息,把完整的关键词直接丢进搜索引擎/社区,而不是问“这道题怎么做”。
比如你遇到error when starting dev server: typeerror: crypto$2.getrandomvalues is not a这种报错,这是JavaScript环境里crypto对象的问题,不是CTF考点,搜的时候直接搜报错原文比搜“Crypto题目”有用得多。同样的,Python里遇到AttributeError: module 'Crypto' has no attribute 'Cipher',多半是包名冲突或库没装对,搜报错原文马上能找到答案。
搜索本身也是一项CTF技能。会用正确的关键词,能让你在解题卡住时节省大量时间。我的经验是:搜索时优先用英文关键词,因为CTF的国际社区资料远比中文社区丰富,RSA common modulus attack搜出来的内容质量通常比“RSA共模攻击”更高。
6.2 “复现消耗”学习法
最后一个压箱底的学习方法,我叫它“复现消耗法”。具体操作:每看完一道题的WriteUp,关掉网页,凭记忆自己独立把解题脚本重写一遍,直到能跑出正确的flag。
不要小看这个动作。很多新手看WP的时候觉得“哦原来这么简单”,关上网页之后三分钟就忘了。而当你亲手把每一步代码敲出来、把每个参数打印出来检查,你才是真正“消耗”掉了这道题。Crypto的解题能力,就是在这样一个又一个“消耗”中积累起来的。
我到现在依然保持这个习惯:即使在比赛里做出来的题,赛后也会重新看一遍最佳WriteUp,找出自己思路里绕远路的部分,然后思考下次遇到同类题能不能做得更快。这种持续的自我迭代,才是Crypto水平稳步提升的关键。