Base64Encryption(换表 Base64 + 二维码藏密码 + AES-ZIP)
[info] 题目信息
- 考点:换表 Base64、二维码方向判定、WinZip AES 加密
- 来源:SHCTF Misc
- 最终 flag:
SHCTF{fbf655a2-0661-4665-ac56-2331ca65e887}
一、题目给了什么
四个文件,其中三个是换表 Base64 密文,一个是已知明文:
| 文件 | 内容 | 作用 |
|---|---|---|
Readme.txt |
明文 + 一串乱序表 b4CYzZ3RWg7p... |
已知明文 |
Readme.txt.enc |
Readme 换表后的密文 | 用来验证恢复出的表是否正确 |
flag.zip.enc |
一个 ZIP 的换表密文 | 最终目标 |
png.png.enc |
一个 PNG 的换表密文 | 中间跳板(藏密码的二维码) |
[warning] 关键提示
Readme 里那句「只要别人解不开,那就是加密?」是出题人的暗示:这不是加密,是换表 Base64。而且 Readme 里给的那张表b4CYzZ3...是烟雾弹,真正能解的表要靠自己反推。
二、核心概念:什么是「换表 Base64」
标准 Base64:把 3 字节(24 bit)拆成 4 个 6-bit 索引,用索引去一张固定字母表里查字符:
1 | 索引 0~63 → 标准表 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" |
「换表」就是把这 64 个字符重新排列,用新表来编码。它不是加密,只是混淆:
| 换表 Base64 | 真正的加密 | |
|---|---|---|
| 安全性来源 | 表本身(藏不住) | 密钥 key |
| 是否可逆 | 确定、无密钥 | 需要密钥 |
| 能否被已知明文攻破 | 一次就破 | 不能 |
[abstract] 柯克霍夫原则
密码系统的安全性只能依赖密钥的保密,不能依赖算法(或表)的保密。换表 Base64 恰好违反这条原则,所以「打乱表 ≠ 加密」。
三、解题流程总览
1 | flowchart TD |
四、Step 1:反推换表
4.1 文件头是「天然明文」
很多文件格式的固定字节是公开规定的,等于免费送你已知明文:
| 来源 | 明文(已知) | 密文(从 .enc 抠出) |
|---|---|---|
| ZIP 文件头 | 50 4B 03(即 PK) |
7RA8 |
| PNG 文件头 | 89 50 4E 47 0D 0A 1A 0A |
cZydiQ1tkpgKKKKNf7SR |
| PNG 尾 IEND | 00 00 00 00 49 45 4E 44 ... |
KKKKKR0IWOfDYhrr |
| Readme 全文 | 明文已知 | HHnaHgci... |
[tip] 记住这些魔数
- ZIP →
PK(50 4B)- PNG →
89 50 4E 47 0D 0A 1A 0A- PNG 尾 IEND →
49 45 4E 44+AE 42 60 82- JPEG →
FF D8 FF、PDF →GIF89a
4.2 recover 原理
用「已知明文 → 手动算 6-bit 索引 → 去密文里对齐取字符 → 填表」:
1 | def recover(pts, enc='utf-8'): |
- 明文每 3 字节 → 算出 4 个 6-bit 索引
- 密文去掉
=后逐字符和索引对齐 tbl[索引] = 密文字符,多组样本反复覆盖填充
结果恢复出 62/64,缺位置 31 和 59。
4.3 补全缺失字符
缺的位置 = 「所有已知明文里从没出现过的索引」,对应标准表里没用到的 X 和 z。
因为标准表 64 字符的集合是固定的,已确定 62 个,剩下 X、z 只能落在空缺的 31、59。只有 2! = 2 种排列,逐一试解码即可。
正确换表:
1 | Kyr8RIkJwCtaTNdBYifW7Zo6UbmqPG9zpSc/O0hxg52jALDlQenV1+EM4HvX3Fsu |
五、Step 2:解码三个文件
1 | STD = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" |
| 文件 | 解码结果 |
|---|---|
Readme.txt.enc |
还原出 Readme 明文 ✓(验证表正确) |
flag.zip.enc |
flag.zip(PK 开头,AES 加密,内含 flag.txt.enc) |
png.png.enc |
png.png(二维码 PNG) |
六、Step 3:二维码拿密码(有烟雾弹)
png.png 是二维码,直接扫得 There is no password here. —— 假的。
[warning] 烟雾弹
出题人故意把二维码转了角度。正常扫描器会误判或识别出错误内容。正确做法是旋转 90°/180°/270° + 镜像穷举扫。
换角度后扫出真正的 ZIP 密码:
1 | base64_15_n0t_3ncrypt10n |
(读出来就是「base64 is not encryption」,再次点题。)
七、Step 4:AES-ZIP 解压(又一个坑)
flag.zip 的压缩方法是 99(WinZip AES),Python 标准库 zipfile 解不了,要用 pyzipper:
1 | import pyzipper |
flag.txt.enc 还是换表密文,再 dec() 一次:
1 | SHCTF{fbf655a2-0661-4665-ac56-2331ca65e887} |
八、完整脚本
1 | import base64 |
九、关键知识点总结
- 换表恢复 = 已知明文逐位对齐反推,
recover里 zip 对齐一旦冲突,说明某对明文猜错了。 - 文件头尾是天然明文:
PK、PNG magic、IEND 都是格式规定的固定字节。 - 缺字符补全:空位数量 = 标准表里「从未出现的字符」数,排列数 =
n!,通常很小可爆破。 - 二维码方向判定:扫出奇怪内容先别信,试试旋转 + 镜像穷举。
- AES-ZIP(compress_type 99):标准
zipfile解不了,用pyzipper。 - 烟雾弹:题面给的「表」、二维码直接扫出的「密码」,都可能不是真的,别轻信。