打CTF的时候,最烦的不是难题,而是“看着简单却解不出”的题。Crypto赛道里有一类签到题特别典型:题目给你一串字符,看起来像Base64,老老实实解码,出来的却是一堆毫无逻辑的字母乱码。这时候十有八九是撞上了“Base64 + ROT13 + Atbash”的三件套套娃。Base64负责把二进制变成可见字符,ROT13和Atbash则是两种古老的字母替换,它们单独拆开都不值一提,合在一起却能精准卡住一批人。这篇文章从原理讲到实战,把三者的特征、辨识方法、拆解流程和常见翻车点全部过一遍,适合刚入门CTF的新人,也适合想在密码学杂项上建立系统套路的选手。
1. 三种基础“武器”的原理与特征
1.1 Base64:不是加密,是“换姿势表达数据”
很多人一看到Base64就以为是什么高深加密,其实它在计算机世界里就是一层“表示层”。它的核心操作是把任意字节流按每3个字节一组,拆成4个6bit片段,再把每个6bit片段映射到一张64字符的索引表:A-Z、a-z、0-9、+、/。因为6bit能表示0到63,64个字符正好一一对应。举个例子,字符串“flag{base64}”,长度12字节,编码后变成“ZmxhZ3tiYXNlNjR9”,长度16字符,没有出现“=”号,因为12刚好能被3整除。
“=”号是另一个重要特征:当原始数据的长度不是3的倍数时,Base64会在末尾补一个或两个“=”。所以看到字符串以“=”结尾、并且字符集落在上面那64个字符里,基本可以认定这层是Base64。它和真正的加密有本质区别:没有密钥,不做混淆,任何知道规则的人都能立刻还原。CTF里Base64最常见的用途就是“外壳”,把二进制、图片、序列化数据变成一串可传输的文本,然后再在外面套各种花活。
1.2 ROT13:自带“撤销键”的字母轮盘
ROT13理解起来特别简单:把26个英文字母往前或往后移13位,a变成n,n变成a,b变成o,o变成b,以此类推。因为26的一半就是13,所以同样的操作执行两次,刚好回到原文,加密函数和解密函数是同一个。这个自逆特性非常方便,早期论坛经常用它隐藏剧透内容,现在CTF出题人也爱拿它做“字母乱码层”。
ROT13只作用于字母,数字和标点符号一概不动。所以如果一串密文里字母看起来完全没意义,但数字和“{}”“_”这些符号都老老实实待着,那大概率遇到了ROT13或者其他字母移位。它还有几个变体:ROT5负责转数字,ROT18同时转字母和数字,ROT47则把可见ASCII字符都轮转一遍。比赛里如果看到一串含有大量数字和符号的密文,可以直接试试ROT47,很多时候比ROT13更对症。
1.3 Atbash:把字母表从后往前倒一遍
Atbash比ROT13更古老,思想也极端:把字母表首尾对折,a变z,b变y,c变x,一直到m变n。它同样只处理字母,同样自逆,跑两遍就还原。名字本身来自希伯来文的字母顺序,属于最古老的替换密码之一——在古典密码教科书里它常和凯撒密码并列出现。
Atbash和ROT13最迷惑人的地方在于,它们作用在密文上,肉眼看起来效果非常像:都是“一堆字母乱码”。但两者用的是完全不同的映射。想快速区分,取一个字母就够了。比如密文里有个z:如果是ROT13,z会变成m;如果是Atbash,z会变成a。同理,a在ROT13下变n,在Atbash下变z。记住这两个对照,现场几秒钟就能判断出该往哪个方向试。
2. 组合套娃的识别套路
2.1 出题人为什么总爱“套娃”
从出题角度讲,Base64、ROT13、Atbash单独出成题,三秒就被秒了,没区分度。把它们叠在一起,表面看是“知识点合集”,实际考察的是两件事:第一,你知不知道这三个东西的特征;第二,你有没有“逐层剥离”的思路。很多时候选手卡住,不是不会Base64,而是解了一层发现不是flag,就以为自己做错了,不敢继续往下处理。这套题的难点从来不是算法本身,而是“如何判断下一步做什么”。
2.2 拿到密文先看“指纹”
破套娃题不需要一上来就暴力穷举,先做一次特征扫描:
- 看字符集:字符串里是否同时有大写字母、小写字母、数字,是否含+和/,是否以=结尾。满足这些,优先考虑Base64。
- 看字母形态:如果字符串几乎全是英文字母,数字和符号占比极低,优先考虑ROT13或Atbash。
- 看特殊符号:ROT13和Atbash都不动{}、_、=这类符号。如果这些符号“原样留在原地”,反而说明它们不是最外层,而是内部数据的一部分。
- 看长度:Base64编码后的长度一般是4的倍数,并且和原始数据长度有固定换算关系。没有=不代表不是Base64,可能是长度恰好整除。
把这些特征组合起来,基本能在第一眼锁定两个候选方向:Base64方向,或者替换密码方向。注意,这两个方向很多时候是先后关系,不是并列关系。
2.3 从外到内逐层剥,每步重新观察
不管题目怎么套,解码顺序永远是“从外向内”。最外层的操作是最晚被施加的,所以要最先解开。比如加密流程是flag先做Base64,再ROT13,最后Atbash,那密文的最外层就是Atbash,解题第一步就要先做Atbash。做完一层之后,不要急着欢呼,把输出重新贴到文本里观察一遍:它现在像什么?是像Base64,还是像另一层替换?这个“观察—尝试—再观察”的循环,就是整套套娃题的心法。
还有个冷知识:ROT13和Atbash都是仿射密码的一种,两者叠加在一起时顺序其实可以互换。Atbash(ROT13(x))和ROT13(Atbash(x))的结果完全一样。所以如果你判断出某层是“ROT13和Atbash组合”,就不用纠结谁先谁后,一股脑都做完,再统一做Base64解码就行。当然,前提是没有其他干扰层。
3. 工具实操:从CyberChef到本地Python
3.1 CyberChef:密码学瑞士军刀
我最常用的工具是CyberChef,它对这类套娃题几乎是降维打击。把密文粘到输入框,它会实时显示处理结果。新手上手可以直接点左侧的Magic,这个功能会尝试自动侦测编码/密码链,很多时候能一步到位给出答案。如果Magic没识别出来,就手动拖算子搭Recipe:
- 先拖入From Base64,看输出。
- 输出是字母乱码?再拖入ROT13或者Atbash,继续看。
- 直到输出变成可读的flag。
操作上有个细节:CyberChef默认按UTF-8处理文本,如果Base64解码出来有非打印字符,界面会显示成乱码块。这时候别慌,把输出继续按“下一个候选操作”处理。还要注意输入框里不要带多余空格和换行,很多Base64解码错误都是这些看不见的字符引起的。本地版还有个好处是可以在断网环境跑,比赛现场网络不稳定时,本地版救过我好几次。
3.2 Python脚本:三件套一把梭
工具虽好,但有些骚操作还是得靠脚本。下面这个三件套函数,比赛时复制粘贴改个字符串就能用:
import base64 import codecs def atbash(s: str) -> str: table = str.maketrans( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", "zyxwvutsrqponmlkjihgfedcbaZYXWVUTSRQPONMLKJIHGFEDCBA" ) return s.translate(table) def rot13(s: str) -> str: return codecs.encode(s, "rot13") cipher = "NapfN3teOPZbZdV9" # 依次尝试 Atbash -> ROT13 -> Base64 step1 = atbash(cipher) step2 = rot13(step1) plain = base64.b64decode(step2).decode("utf-8") print(step1) print(step2) print(plain)这里用到了三个技巧:atbash函数用maketrans一次性建立映射,ROT13直接用codecs模块内置能力,Base64解码用标准库b64decode。如果你想批量试不同顺序,也可以把操作函数放进列表,用itertools.permutations生成所有组合,逐个跑一遍,再把输出里匹配flag正则的文本挑出来。思路很简单:把“人能判断的事”变成“程序穷举的事”。
4. 完整案例复盘:从“天书”到flag
4.1 题目给的“天书”
假设赛题给出了这样一串字符串:NapfN3teOPZbZdV9,只告诉你这是Crypto签到题,目标是提交flag。第一眼观察:长度16,正好是4的倍数;字符集包含大写、小写、数字;没有=,没有{},没有下划线。如果硬按Base64去解,确实能解出东西,但解出来的是类似“39.3x/,�2”这种半可读半乱码的内容,显然不是flag。
这个“半可读”的表象非常有迷惑性。很多人解到这一步就以为题目有问题,或者认为自己是解码方式错了。其实这正是套娃题的陷阱:Base64不是一个无效层,但它的输出碰巧也有可打印字符,给人“方向对了”的错觉。这时候要做的不是怀疑Base64,而是继续往内层挖掘。
4.2 逐层解密全过程
第一次尝试:对原串执行Atbash。NapfN3teOPZbZdV9变成MzkuM3gvLKAyAwE9。观察这个结果:依然全是Base64字符集内的字符,长度还是16,依然是4的倍数。如果此时直接Base64解码,依然得到一段半可读半乱码的东西。这说明还没到最内层,继续剥。
第二次尝试:对Atbash的结果执行ROT13。MzkuM3gvLKAyAwE9变成ZmxhZ3tiYXNlNjR9。这个字符串一出来,心里基本就有底了:它是最典型的Base64形态,而且解码后的内容肉眼可读。执行Base64解码,得到flag{base64},题目完成。
整个过程只有三步,但这三步只要走错一步,或者中途放弃,就拿不到flag。回过头看,正确的解密链是Atbash → ROT13 → Base64。如果你一开始先对原串执行ROT13,得到AncsA3grBCMoMqI9,再执行Atbash,同样会回到ZmxhZ3tiYXNlNjR9。因为这两个替换层可以互换顺序,所以先做哪个都不影响最终结果。
4.3 从案例里提炼四个经验
这个案例看着简单,但信息量不小。第一,不要因为第一层Base64解码出乱码就否定Base64这个方向,乱码可能是下一层的“正常现象”。第二,每一层操作之后都要重新观察特征,判断继续走Base64还是替换方向。第三,验证一个中间结果是否靠谱,最简单的标准是“整段是否可读、是否包含flag特征”。第四,像Atbash和ROT13这种组合,不要纠结顺序,先把所有可能的替换做一遍,再看哪一步输出最像Base64。
如果题目给的字符串以=结尾,那Base64会更好识别,解题速度也会快很多。反之,没有=的Base64反而容易让人误判,这也是出题人提高区分度的手段之一。
5. 避坑手册:那些容易翻车的细节
5.1 高频翻车点速查
| 现象 | 可能原因 | 处理方法 |
|---|---|---|
| Base64解码后是字母乱码 | 内层还有ROT13/Atbash | 对解码结果继续做替换 |
| Base64解码后半可读半乱码 | 替换层没解干净 | 继续剥,警惕部分可读的假象 |
| Base64解码报非法字符 | 混入空格、换行、URL编码 | 先清理空白,再做URL解码 |
| ROT13转完还是读不懂 | 可能是Atbash或ROT5/18/47 | 切换其他变体逐个尝试 |
| 字符串全是字母且没有= | 可能不是Base64,而是替换层 | 先Atbash/ROT13再做Base64 |
| 解出flag{xxx}但内容乱码 | 内部还有一层Hex/Base32等 | 对花括号内容继续特征分析 |
这张表是我踩坑踩出来的经验。里面尤其要强调“半可读半乱码”这个状态,它是套娃题最阴险的地方。你以为自己快解完了,其实只剥开了一层,里面还有一层等着你。
5.2 一套通用排错流程
如果你面对一段密文毫无头绪,我建议按这个流程走:
- 先把字符串复制到文本编辑器,去掉所有不可见字符、空格和换行。
- 观察字符集,列出候选操作:Base64、ROT13、Atbash、ROT5、ROT18、ROT47、URL解码、Hex解码。
- 按“从外向内”的原则选一个操作做下去,每做一步都把中间结果保存下来。
- 每得到一个新结果,重新跑一遍第二步的特征分析。
- 如果某层Base64输出是二进制乱码,可以尝试把乱码以Hex形式显示,看看里面有没有可读片段。
- 连续做三四步后如果完全卡住,大概率是混入了非标准手法,比如自定义Base64索引表,这时候再回到题目上下文找线索。
这套流程不仅能处理三件套,碰到更复杂的多层套娃也适用。方法其实就一句话:眼观六路,手要勤快,别在一层输出上死磕。
我个人在实际操作中还有一个习惯:把CyberChef的Recipe保存成预设,Base64→ROT13→Atbash、Base64→Atbash→ROT13、URL→Base64→ROT47这些组合各存一份。遇到新题直接套预设跑一遍,几分钟没结果再手动调整。这个习惯救了我很多次,比赛时间紧的时候,能省下大量试错成本。如果你也经常碰Crypto题,强烈建议提前建好这套“武器库”。