Steganography Writeup


题目描述

给出一个名为 steganography.png 的图片文件。图片无法正常打开,也无法拖入 Stegsolve 等工具中进行分析。需要从中提取隐藏的 flag。

解题思路与步骤

1. PNG 文件结构修复 (IDAT 块异常)

首先,尝试将图片拖入 Stegsolve 时会报错。使用十六进制编辑器 (如 010 Editor) 或 Python 脚本检查图片的二进制结构,发现 PNG 的区块 (Chunks) 链被破坏。

具体来说,在正常的 PNG 文件中,一个数据块的结尾 (包含 4 字节的 CRC 校验码) 应该紧接着下一个数据块的开头 (4 字节的长度字段)。但在这个文件中,多个 IDAT 数据块之间被恶意插入了额外的字节,导致图片解析器因为遇到未知的长度和类型而无法继续解析。

通过脚本提取出这些被插入的额外字节,并在原文件中将它们删除。同时,为确保修复后的文件严格符合 PNG 标准,可以顺便重新计算并修复所有块的 CRC 验证值。
修复后得到一个正常的 PNG 文件 fixed2.png,现在它可以被 Stegsolve 正常打开了。

顺便一提:修复过程中提取出的多余字节为:
a61ade 6428b2 1091025592d7 6c7f2c bcd754 a82fd7
组合起来:a61ade6428b21091025592d76c7f2cbcd754a82fd7
(这在后续步骤中没有用到,但属于常见的隐写思路。)

2. LSB 隐写提取 ZIP

图片修复后使用 Stegsolve 打开,利用 LSB (最低有效位) 隐写分析功能,在勾选了Red、Green和Blue的0通道后发现有PK(50 4b 03 04)和flag.zip的16进制数据,使用 Save Bin 提取 0 通道的数据,提取出了一个 ZIP 压缩包 flag.zip

注意:使用工具提取出的文件通常包含了原文件末尾的大量垃圾数据。因为真正的 ZIP 文件结尾以 50 4b 05 06 (EOCD 标志) 结束,可以使用脚本将其后面的冗余数据截断,进而得到一个结构完全正常的 flag_fixed.zip

3. ZIP 伪层叠与 CRC32 爆破

解压修复后的 flag_fixed.zip,发现里面包含了 7 个同样需要密码的压缩包:

  • flag.zip
  • pass1.zip ~ pass6.zip

查看 pass1pass6 这 6 个压缩包的属性,发现里面的明文文件(data1.txt ~ data6.txt)的 大小全部仅有 4 个字节
对于这种加密压缩包已知明文极短的情况,可以直接利用明文的 CRC32 校验值进行爆破,无需密码即可知道明文内容。

通过 Python 脚本组合所有可打印的 ASCII 字符,对这 6 个 4字节长度文件的 CRC32 进行碰撞运算,结果如下:

  • pass1.zip (CRC: 0xce70d424) -> 爆破结果: pass
  • pass2.zip (CRC: 0xf90c8a70) -> 爆破结果: is
  • pass3.zip (CRC: 0xff3fe4bb) -> 爆破结果: c1!x
  • pass4.zip (CRC: 0x242a5387) -> 爆破结果: xtLf
  • pass5.zip (CRC: 0x9a27098e) -> 爆破结果: %fXY
  • pass6.zip (CRC: 0xd3f6df9f) -> 爆破结果: PkaA

拼接后得到重要提示(即最终密码):
pass is c1!xxtLf%fXYPkaA

4. 零宽字符隐写解密

利用获取到的密码 c1!xxtLf%fXYPkaA 解压加密的 flag.zip,里面包含了一个 flag.txt 文件。

用文本编辑器打开 flag.txt,发现里面只有似乎是常规的一段话 flag is here
但实际上该文本内隐藏了不可见的“零宽字符”。利用脚本对文本进行读取遍历,分析发现含有大量的:

  • 零宽空格 (\u200b)
  • 零宽不连字 (\u200c)

这是一种典型的零宽隐写 (Zero-Width Steganography)。由于只存在两种不可见字符,将其理解为二进制串。
\u200b 替换为 0,将 \u200c 替换为 1,把整段编码转换为二进制字符串后,按每 8 位转换为对应的 ASCII 字符,成功还原出暗藏的 flag。

最终 Flag

dart{bf4100d9-cc8d-48f6-a095-54cbfad189e1}


文章作者: 持之以 恒
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 持之以 恒 !
  目录