📌 题目信息
| 项目 | 内容 |
|---|---|
| 题目名称 | 消失的Yui |
| 题目来源 | 青少年CTF S1·2026 公益赛(投稿题) |
| 题目类型 | Misc |
| 子分类 | 零宽字符 / Base100 / RC4 / 地理坐标 |
| 考点 | 零宽字符隐写、emoji(Base100)编码、压缩包、二维码、RC4解密、坐标定位 |
| 难度 | ⭐⭐⭐⭐ |
| 最终 Flag | flag{hefei} |

题面:你是A市的警官,负责找到离奇失踪的作家Yui,你找到了一些线索,觉得事情大有蹊跷……
🛠️ 使用工具
- 零宽字符解码:提取文本中隐藏信息(格式提示)
- Base100 解码(emoji → 字符串):得压缩包密码
- zbarimg / 扫码工具:读取二维码
- Python3:RC4 解密、(可选)零宽字符解析
📝 解题思路
一道剧情化套娃题,线索一层套一层:
最后的作品.txt
├─ 零宽字符 → 格式提示 flag{当前所在城市英文名(小写)}
└─ emoji串(Base100) → 压缩包密码 TrY_to_F1Nd_m3
└─ 解压 yui的房间.zip
├─ under_the_sea.jpg(二维码) → RC4密文
└─ 时间的谜语.txt → 拼出RC4密钥 1145141919810
└─ RC4解密 → 经纬度 → 定位城市(合肥) → flag
第 1 步:从初始文本提取线索
什么是零宽字符:
零宽字符(Zero-width characters)是一类"看不见、但真实存在"的 Unicode 字符——它们占位置,但显示时宽度为 0,所以肉眼看不到痕迹,但程序能读出来。
常用几种:
| 名称 | Unicode | Python 转义 | 作用 |
|---|---|---|---|
| ZWSP(零宽空格) | U+200B | \u200b | 常见隐写载体 |
| ZWNJ(零宽不连字) | U+200C | \u200c | 常见隐写载体 |
| ZWJ(零宽连字) | U+200D | \u200d | 常见隐写载体(还影响 emoji 组合) |
| BOM(字节序标记) | U+FEFF | \ufeff | 有时也被利用 |
| ZWJ-no-break | U+2060 | \u2060 | 少见 |
为什么能隐写:把一段"隐藏信息"用 2~3 种零宽字符编码成二进制(比如 \u200c=0、\u200d=1),穿插在正常文字中间。人眼看不出来,但解码程序能提取出来。这就是零宽字符隐写(Zero-width steganography)。
⚠️ 举例:
你好vs你\u200c\u200d\u200c你好显示完全一样,但后者藏了 3 位二进制。
最后的作品.txt 是一大段小说文本,但里面藏着:
- 零宽字符(如
\u200cZWNJ、\u200dZWJ、\ufeff),肉眼看不见(vscode可以看见)。
- 解码后给出格式提示:
#这是关于flag的提示,后面要用 flag{当前所在城市英文名(小写)} - 文本中还有很多 emoji,用 Python 提取 emoji:
import re s = open('最后的作品.txt', encoding='utf-8').read() emojis = re.findall(r'[\U0001F300-\U0001FAFF]', s) print(''.join(emojis)) # 👫👟👜👧...
第 2 步:Base100 解码 emoji → 得到压缩包密码
emoji 串是 Base100 编码(用 emoji 表示信息)。用 Base100 工具/库解码:
👫👟👜👧👘👪👪📦💳💑👋👩👐👖👫👦👖🐽🐨👅👛👖👤🐪
↓ Base100 解码
TrY_to_F1Nd_m3
得到 yui的房间.zip 的解压密码。
解压yui的房间.zip,得到如下内容:
第 3 步:二维码 → Base64编码文本
under_the_sea.jpg里是二维码,扫码得到 Base64 格式的文本:
z2qbTYV1U4vpBNoL7jEGcAFyrLpTnlNQdi4OuFxsFl8=
Base64 解码 → 得到一堆二进制乱码(不是可读文本)
看到乱码 → 说明它不是"明文文本的 Base64",而是加密/压缩后数据的 Base64"
最后结果多次尝试,得到本题出题时的加密过程:
明文
│ RC4加密
▼
二进制密文(乱码)
│ Base64编码
▼
Base64密文: z2qbTYV1U4vpBNoL7jEGcAFyrLpTnlNQdi4OuFxsFl8=
│ 存进二维码
▼
扫码得到的那串 Base64
为什么这道题优先锁定 RC4(核心判断依据):
- 线索 1:只给了一串密钥,没有 IV ,绝大多数工作模式都必须搭配 IV(初始向量)才能正常解密;而 RC4 是经典的流加密算法,只需要一个密钥即可完成加解密,完全匹配题目给出的条件。
- 线索 2: AES 等分组加密会有固定块大小的填充(比如 16 字节对齐),密文长度一定是块大小的整数倍,和本题特征不符。
- 线索 3:CTF 入门赛的高频考点。青少年 CTF、入门级赛事的加密考点里,RC4 是流加密的头号考点,出题频率远高于 Salsa20、ChaCha20 等其他流加密算法。
第 4 步:从「时间的谜语」拼出 RC4 密钥
时间的谜语.txt 分四段,每段藏一个数字线索:
| 段落 | 线索 | 数字 |
|---|---|---|
| 一 | 钟楼指针凝固在十一时四十五分 | 1145 |
| 二 | 档案柜编号14与19 | 1419 |
| 三 | 1981年学术期刊 | 1981 |
| 四 | 仪器刻度盘零点熵增阈值 | 0 |
按顺序拼接密钥:
1145 + 1419 + 1981 + 0 = 1145141919810
⚠️ 易错点:注意"十一时四十五分"要写成 1145(不是 11:45 或带冒号);"仪器零点"是数字
0,要拼在末尾作为第 4 段。四段顺序必须一致,少一段/顺序错都会导致 RC4 解不出来。
第 5 步:RC4 解密 → 坐标 → 定位城市
用密钥 1145141919810 对密文做 RC4 解密:
import base64
def rc4(key, data):
S = list(range(256)); j = 0
for i in range(256):
j = (j + S[i] + key[i % len(key)]) % 256
S[i], S[j] = S[j], S[i]
i = j = 0; out = bytearray()
for b in data:
i = (i + 1) % 256; j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
out.append(b ^ S[(S[i] + S[j]) % 256])
return bytes(out)
key = b"1145141919810"
cipher = base64.b64decode("z2qbTYV1U4vpBNoL7jEGcAFyrLpTnlNQdi4OuFxsFl8=")
print(rc4(key, cipher).decode())
输出:
31°49′14″N 117°13′38″E
这个坐标(约 31.82°N, 117.23°E)指向安徽省合肥市。
⚠️ 易错点:RC4 密钥是字节串(
b"..."),Base64 密文要先b64decode成二进制再喂给 RC4。坐标用度分秒(DMS)表示,31°49′14″N= 31+49/60+14/3600 度;换算后对应合肥。
第 6 步:按格式提示组装 flag
第 1 步的零宽字符提取已提示flag格式:flag{当前所在城市英文名(小写)}
坐标位于合肥,英文名小写为 hefei
✅ 最终 Flag
flag{hefei}
【Misc】消失的Yui&spm=1001.2101.3001.5002&articleId=163948993&d=1&t=3&u=1acadc0d58fe4a838b9f63a4028542c7)
4181

被折叠的 条评论
为什么被折叠?



