CTF Misc 入门:文件头、binwalk 与 LSB 隐写

杂项三板斧:file/xxd 识别文件头、binwalk 提取隐藏文件、Python 实现 LSB 隐写的隐藏与提取。
一、现实背景:扩展名不可信
操作系统按扩展名选择打开程序(.png 用图片查看器),但文件的真实类型由文件头(magic bytes)决定。日常中"把 zip 改成 png"会让图片查看器报错;取证中则要反过来:先看文件头,再信扩展名。
必须记住的三个文件头:
| 文件 | 文件头(hex) | 明文 |
|---|---|---|
| PNG | 89 50 4E 47 0D 0A 1A 0A | ‰PNG |
| JPEG | FF D8 FF | — |
| ZIP | 50 4B 03 04 | PK |
二、环境准备
apt install file binutils binwalk foremost exiftool
pip3 install pillow
三、核心技能 1:file 与 xxd
3.1 file:看真实类型
目的:判断题目文件到底是图片、压缩包还是其他东西,避免被扩展名误导。
验证方式:看输出的第一段描述(PNG image data / Zip archive data / PDF document ...)。
file hidden.png
实际输出:
hidden.png: PNG image data, 8 x 8, 8-bit/color RGB, non-interlaced
如果输出是 Zip archive data,说明文件其实是 zip:
mv flag.png flag.zip && unzip flag.zip
3.2 xxd:看文件头
目的:用肉眼确认文件开头的魔数,验证 file 的判断,也为手工分析文件结构做准备。
xxd -l 16 hidden.png
# -l 16:只显示前 16 个字节,看文件头足够。
实际输出:
00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452 .PNG........IHDR
开头的 89 50 4e 47 0d 0a 1a 0a 正是 PNG 魔数,右侧明文是 .PNG。
3.3 strings:搜字符串
目的:快速判断 flag 是不是直接以文本形式藏在文件里。
思路:strings 把可打印字符串抽出来,grep -i flag 过滤含 flag 的行。有输出 = 直接提取;无输出 = 可能是隐写,进入下一步。
strings flag.png | grep -i flag
有输出说明 flag 直接以文本形式藏在文件里;无输出则可能是隐写。
四、核心技能 2:binwalk 提取隐藏文件
文件可以追加在其他文件之后(如图片尾部拼接 zip)。扫描:
binwalk flag.png
预期输出:
DECIMAL HEXADECIMAL DESCRIPTION
0 0x0 PNG image, 500 x 500
12345 0x3039 Zip archive data
提取:
binwalk -e flag.png
会在当前目录生成 _flag.png.extracted 文件夹,里面是提取出的 zip。
备选工具 foremost(按文件头暴力切割):
foremost flag.png
五、核心技能 3:LSB 隐写(完整实现)
5.1 原理
RGB 像素每个通道是一个 0-255 的字节。把每个字节的最低位(LSB)替换成秘密比特,像素值最多变化 1,肉眼不可察觉:
原来: 11001000 10010110 01100100
写入: 11001001 10010110 01100101 ← 最低位依次写入 1,0,1
一张 500×500 的图能藏 500×500×3 比特 ≈ 93 KB。
5.2 隐藏(出题人视角)
目的:理解"把秘密写进像素最低位"的具体实现,便于反向写出提取脚本。
from PIL import Image
# PIL 是 Python 图像处理库,Image 模块负责打开、修改、保存图片。
img = Image.new('RGB', (8, 8), (200, 150, 100))
# Image.new(mode, size, color):新建一张图片。
# mode='RGB' 表示三通道;size=(8,8);color=(200,150,100) 是初始颜色。
message = "flag{lsb_steganography}"
data = message.encode() + b'\x00'
# message.encode():把字符串按 UTF-8 转成字节串。
# 末尾追加 b'\x00'(字节 0)作为结束符,提取时遇到 0 就停止。
bits = ''.join(f'{b:08b}' for b in data)
# 把每个字节转成 8 位二进制字符串并拼接。
# 例:b'f'=0x66=01100110,8 位拼进 bits。
pixels = list(img.getdata())
# img.getdata():返回所有像素的迭代器,转成列表后每项是 (r,g,b) 三元组。
new_pixels = []
i = 0
for r, g, b in pixels:
if i < len(bits):
r = (r & 0xfe) | int(bits[i]); i += 1
# r & 0xfe:把红色通道的最低位清零(0xfe=11111110)。
# | int(bits[i]):把秘密比特写进最低位;i 是当前比特下标。
if i < len(bits):
g = (g & 0xfe) | int(bits[i]); i += 1
# 绿色通道同样处理。
if i < len(bits):
b = (b & 0xfe) | int(bits[i]); i += 1
# 蓝色通道同样处理。三个通道依次消耗秘密比特。
new_pixels.append((r, g, b))
# 修改后的像素放入新列表。
img.putdata(new_pixels)
# putdata():把新像素列表写回图片对象。
img.save('hidden.png')
# save():按扩展名格式保存为 PNG 文件。
print("已生成 hidden.png")
r & 0xfe 把最低位清零(0xfe = 11111110),| bit 写入目标比特。
5.3 提取(解题视角)
目的:把像素最低位收集起来,按 8 位一组还原成字节,得到隐藏消息。
思路:读取所有像素 → 依次取 R、G、B 的最低位 → 每 8 个比特拼成一个字节 → 遇到 0 结束。
from PIL import Image
img = Image.open('hidden.png').convert('RGB')
# Image.open(path):打开图片文件。
# .convert('RGB'):统一转成 RGB 模式,避免调色板/透明度干扰。
bits = []
for r, g, b in img.getdata():
bits += [r & 1, g & 1, b & 1]
# r & 1:取出红色通道最低位(0 或 1),绿、蓝同理。
# 三个最低位依次追加到 bits,顺序与隐藏时一致。
data = bytearray()
for i in range(0, len(bits) - 7, 8):
# range 步长 8:每 8 个比特处理一次,组成 1 字节。
byte = 0
for bit in bits[i:i+8]:
byte = (byte << 1) | bit
# 左移一位再或上新比特,等价于二进制拼接。
# 例:1,0,1 → ((1<<1)|0)=2, (2<<1)|1=5,即二进制 101。
if byte == 0:
# 字节 0 是隐藏时追加的结束符,到此停止。
break
data.append(byte)
print(data.decode())
# decode():字节串按 UTF-8 解码成字符串显示。
实际输出:
flag{lsb_steganography}
5.4 现成工具
gem install zsteg
zsteg -a hidden.png
-a 全自动检测各种 LSB 变体。
六、必学工具
| 工具 | 核心功能 | 命令 |
|---|---|---|
| file | 看类型、看编码、看无后缀文件 | file flag |
| xxd / hexdump | 查看文件头、指定长度 | xxd -l 16 flag.png |
| strings | 提取字符串、指定长度、管道过滤 | strings flag.png、strings -n 3 a、strings a | grep flag |
| binwalk | 扫描、提取 | binwalk flag.png、binwalk -e flag.png |
七、小 CTF 实战:藏了 flag 的 PNG
题目:flag.png。
第一步:
file flag.png
输出:PNG image data。
第二步:
strings flag.png | grep -i flag
无输出。
第三步:
binwalk flag.png
无隐藏文件。
第四步:怀疑 LSB,运行提取脚本,输出:
flag{lsb_steganography}
八、小结
Misc 入门三板斧:file 看类型、strings/binwalk 排除简单情况、LSB 脚本提数据。音频隐写(Audacity 频谱图)、ZIP 伪加密、USB 流量分析在《隐写与取证深入实战》中完整展开。