news 2026/10/30 4:53:27

CTF学习/crypto/零宽字符与 Unicode 隐写

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CTF学习/crypto/零宽字符与 Unicode 隐写

零宽字符与 Unicode 隐写

考点概念

零宽字符是宽度为零、肉眼不可见的 Unicode 字符

包括零宽空格(U+200B)、零宽非连接符(U+200C)、零宽连接符(U+200D)、零宽不中断空格(U+FEFF)

不占显示宽度,却在字节流中真实存在。可以在"看起来完全正常"的文本中隐藏信息,通常把 3 个字符映射为 0/1/2 的进位制来编码数据。

Unicode 隐写还包括同形异义字(Cyrillic а 与 Latin a 视觉相同但码点不同)与不可见控制符(右至左标记 U+202E 可颠倒显示顺序)等变体,识别难度略高于零宽。

把不可见字符序列还原为比特流再分组

统计可见文本之外的码点,按约定进制重组。工具层面 vim 与 hexdump 均可暴露零宽字符。

四字符二进制映射方案

零宽空格 U+200B 记 00,零宽非连接符 U+200C 记 01,零宽连接符 U+200D 记 10,零宽不中断空格 U+FEFF 记 11。映射表由出题工具自定,解码遇到未知约定时穷举排列

运算规则

编码端先把明文按字符集转成字节序列,每个字节 8 位。四字符方案下把位流按两位为一组查表转成对应零宽字符,两字符方案只保留两种码点逐位记 0 与 1,两者只是分组粒度不同。转换出的零宽字符序列插入任意正常文本,屏幕显示不变。三字符(三进制)方案把每段数据映射到 0/1/2 三种状态,原理一致,仅基数不同。解码端遍历文本只保留零宽码点,逆查映射表还原位流,每 8 位一组转回字节,按原字符集还原明文。

以字母 a 完整演算四字符方案的编码与解码。a 的 ASCII 码点为 97,二进制 01100001。

编码:按两位切分为 01、10、00、01 四段;

查表 01 对应 U+200C 零宽非连接符,10 对应 U+200D 零宽连接符,00 对应 U+200B 零宽空格,01 再次对应 U+200C;把 U+200C、U+200D、U+200B、U+200C 依次插入任意正常句子即完成隐藏,显示与原句完全一致。

解码:扫描载体只收集零宽字符得到同一序列,逆映射得到 01、10、00、01,拼接为 01100001,转十进制 97 查 ASCII 得 a,闭环还原成功。

识别特征

文件特征:文本肉眼正常但文件字节数异常(如 100 字符的 txt 占 2KB)。 编辑器特征:vim 中显示为^@或高亮不可见符;hexdump 可见 E2 80 8B/8C/8D(UTF-8 编码的 200B/200C/200D)。 字符串特征:用 Pythonlen(s)与可见字符数不符,或去空白后re仍多出字符。 提示语:题目描述出现"隐藏、看不见、间谍、句子中藏了东西"等词。

靶场题目

[UTCTF2020]zero(BUUCTF,另见 NSSCTF 收录) zero-width stego(NSSCTF) [安洵杯 2020]BeCare4(NSSCTF)

Writeup

题目:[UTCTF2020]zero(BUUCTF) 识别:文本肉眼正常但字节数与可见字符数严重不符;用 PyCharm 或 vim 打开可见大量 ZWJ(零宽连接符 U+200D)字符夹在字母之间;binwalk 与 foremost 分离不到文件,排除文件级隐藏

解题:把文本提交零宽字符解码工具即可,用在线工具一次读出 flag;脚本做法是遍历文本统计零宽码点出现次序,按常用映射还原位流,每 8 位一组转字节成 ASCII。若工具默认映射与出题映射不符,交换 0/1 或换四字符映射重试,flag{...} 出现即停止。 脚本:

ZWSP = "\u200b" ZWNJ = "\u200c" ​ def extract(s: str) -> bytes: bits = [] for ch in s: if ch == ZWSP: bits.append("0") elif ch == ZWNJ: bits.append("1") out = bytearray() for i in range(0, len(bits) - len(bits) % 8, 8): out.append(int("".join(bits[i:i + 8]), 2)) return bytes(out) ​ carrier = "It looks like a perfectly normal English novel sentence." secret = b"flag{zwsp_demo}" bits = "".join(format(b, "08b") for b in secret) hidden = "".join(ZWNJ if x == "1" else ZWSP for x in bits) text = carrier[:20] + hidden + carrier[20:] print("可见字符 %d 个, len() = %d" % (len(carrier), len(text))) print(extract(text)) assert extract(text) == secret

工具:https://330k.github.io/misc_tools/unicode_steganography.html

题目:[安洵杯 2020]BeCare4(NSSCTF) 识别:txt 打开"空白"但文件字节数不为零,hexdump 可见 E2 80 8B/8C/8D 序列,判定零宽字符携带信息;顺带排除 binwalk 分离。 解题:txt 交零宽解码工具还原,得到压缩包密码 RealV1siBle;解压得到一张 jpg;jpg 用 SilentEye 解密提取出隐藏信息,读出 flag{1nV1sible_flag_Can_You_find?}

脚本:

ZWSP, ZWNJ = "\u200b", "\u200c" password = "RealV1siBle" bits = "".join(format(b, "08b") for b in password.encode()) embedded = "\u8fd9\u884c\u770b\u4e0d\u51fa\u4efb\u4f55\u4e1c\u897f" + \ "".join(ZWNJ if x == "1" else ZWSP for x in bits) + "\u3002" got = "".join("1" if c == ZWNJ else "0" for c in embedded if c in (ZWSP, ZWNJ)) out = bytes(int(got[i:i + 8], 2) for i in range(0, len(got), 8)).decode() print(out) assert out == password

工具:Unicode Steganography with Zero-Width Characters(SilentEye 为本地图形软件)

脚本

ZWSP = "\u200b" ZWNJ = "\u200c" ​ def hide(data: bytes, carrier: str) -> str: bits = [] for byte in data: for i in range(8): bits.append("1" if (byte >> (7 - i)) & 1 else "0") hidden = "".join(ZWNJ if b == "1" else ZWSP for b in bits) return carrier + hidden + "。结尾正常文本" ​ def extract(s: str) -> bytes: bits = [] for ch in s: if ch == ZWSP: bits.append("0") elif ch == ZWNJ: bits.append("1") out = bytearray() for i in range(0, len(bits), 8): out.append(int("".join(bits[i:i + 8]), 2)) return bytes(out) ​ cipher = hide(b"flag{zero_width_ok}", "这是看起来完全正常的句子。") flag = extract(cipher) print("提取结果: %s" % flag.decode()) assert flag == b"flag{zero_width_ok}" print("SUCCESS: flag{zero_width_ok}")
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/30 4:52:38

基于 Traces 数据的 Agent 评测落地方案

基于 Traces 数据的 Agent 评测方案 Trace 数据是 Agent 全执行链路的结构化日志(包含规划、工具调用、记忆、推理、输出的每一步输入输出与状态),基于 Trace 做评测的核心价值是跳出「只看最终答案」的局限,实现过程级精准判分与…

作者头像 李华
网站建设 2026/10/30 4:52:05

自我意象管理

自我意象的四维模型每个自我意象(理想/当下)都从四个维度来描述:维度说明形象穿着、外貌、气质地点在什么环境/城市/空间中行为方式做什么工作、做什么事情绪风格整体情绪状态、心态第一步:写理想自我意象想象十年后最美好的自己&…

作者头像 李华
网站建设 2026/10/30 4:51:27

Agentic World Action Models(AW-0)贡献与不足分析

论文:Agentic World Action Models(UC Berkeley / NVIDIA / UPenn,2026.10)一、论文要解决什么问题论文认为两条路线各有缺口:世界动作模型(WAM),如 DreamZero,能预测未来…

作者头像 李华
网站建设 2026/10/30 4:51:13

逆向某新闻站动态Token:还原RSA非对称加密全过程,搞定接口签名校验

前阵子做公开资讯数据采集项目,碰上个地方新闻门户。列表页能正常访问,可一旦请求正文内容接口,就必须带一个叫x-sign-token的请求头,直接复制浏览器里的token,五六分钟就失效,纯HTTP客户端构造请求直接返回403。 一开始以为就是个简单的时间戳MD5签名,抓了两次包就想自…

作者头像 李华
网站建设 2026/10/30 4:51:12

双目立体视觉入门——(14)OpenCV双目立体视觉完整实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华