HackThisSite - Steganography Mission 12
Challenge
This is an encoded message, the only tip you get is 'I am the not of a file'.
关卡作者 AzurPunk,页面在
https://www.hackthissite.org/missions/playit/stego/12/,
数据文件是
https://www.hackthissite.org/missions/stego/lvl/12.bmp。
题面只给了一句谜语提示
I am the not of a file,答案大小写敏感。
状态:live 已通关(verified)。提交后 profile 的
Stego: 一行出现 (12), 关卡页回执为
You have already done this mission.
Sample
1 | curl -sL -b "$HTS_COOKIE" -o 12.bmp \ |
1 | 12.bmp: PC bitmap, Windows 3.x format, 123 x 1 x 8, image size 126, |
一张 123×1、只有 1 个像素高的 8 bpp(调色板) BMP,整个文件才 1204 字节。
Solution
先把 BMP 头字段算清楚:
1 | import struct |
1 | bfType b'BM' bfSize 1204 dataOffset 1078 |
几个点先说清楚:dataOffset=1078 正好等于
14 + 40 + 1024, 即文件头 14 字节 + 信息头 40 字节 +
256 项调色板(每项 4 字节 = 1024)。 像素区是
124 × 1 = 124 字节(123 个像素 + 1
个对齐填充),像素之后只剩两个 0000 填充,
没有尾部附加文件。信息只能在像素值里。
确认这个调色板的内容:
1 | import struct |
1 | palette is grayscale identity: True |
调色板是恒等灰度斜坡:第 i 项就是
(i, i, i)。所以像素索引值等于它显示出来的灰度,
每字节一个像素,直接读原始字节即可。
题面 I am the not of a file 里的 not
就是按位取反:255 - x。 把 124 个像素字节逐个取反:
1 | pixel bytes : af b4 fc fb eb ff ff ff ff ff 8b 7f 04 c7 a5 b5 ... |
取反后的头四个字节是
50 4b 03 04(PK\x03\x04):一个
ZIP 归档的本地文件头。
原来这张图片的像素值,本身就是某个文件按位取反后的样子,正如提示所说:
它就是某个文件的 not。
把取反结果当作 ZIP 打开:
1 | import io |
1 | ['pass.txt'] |
压缩包里只有一个 pass.txt,内容是 9
个字符的密码,即本题答案(大小写敏感)。
Key points
- 这道题的隐写只有一步:整个像素区就是另一个文件的按位取反。
关键动作只有一个字节级变换
x -> 255 - x,不需要图像处理库,直接读字节即可。 - 定位思路来自题面谜语:
I am the not of a file里的 not 取双关:既表示否定,也指逻辑/按位取反(NOT)。对像素字节做255 - x后出现PK\x03\x04,方向立刻坐实。 - 本题是 8 bpp
调色板图,
dataOffset = 1078 = 14 + 40 + 256×4; 调色板恰好是恒等灰度(i,i,i),所以像素索引即灰度值,无需查表直接对原始字节取反。 - 排除顺序仍然是先结构后内容:头解析排掉了尾部附加数据/头部尺寸不符, 而 256 项调色板本身也没藏东西(恒等灰度)。排除结构之后,答案就在 124 个像素字节里, 按提示做一次 NOT 再解压即可。
- 提交:
/missions/stego/template.php,字段为formkey(每次加载都变)、lvl、pass, 并且必须带Referer: .../missions/playit/stego/12/,否则后端只回Invalid Referer且不计分。