1. PNG文件结构与CTF中的常见考点
在CTF比赛中,MISC类题目经常涉及PNG图片分析。要真正掌握这类题目,必须深入理解PNG文件的二进制结构。一个标准的PNG文件由8字节的文件头签名和多个数据块(Chunk)组成,每个数据块都有特定的结构和功能。
1.1 PNG文件头解析
每个PNG文件都以固定的8字节头开始,用十六进制表示为:
89 50 4E 47 0D 0A 1A 0A这8个字节各有用处:
- 首字节
89:用于检测传输系统是否支持8位数据(非ASCII字符) - 接下来的
50 4E 47是ASCII字符"PNG" 0D 0A是DOS风格的换行符1A是DOS下的文件结束符- 最后一个
0A是Unix风格的换行符
在CTF题目中,出题人有时会故意修改这些字节来设置陷阱。我曾遇到过一道题,文件头被改成了89 50 4E 47 0D 0A 1A 00,导致常规图片查看器无法打开,但用hex编辑器修正后就能正常显示。
1.2 关键数据块分析
PNG文件主要由三种关键数据块构成:
IHDR块(图像头部信息):
- 固定长度13字节
- 包含图像宽度(4字节)、高度(4字节)、位深度(1字节)、颜色类型(1字节)、压缩方法(1字节)、滤波方法(1字节)、隔行扫描方法(1字节)
典型CTF考点:
- 修改宽度/高度隐藏部分图像
- 非常规的颜色类型组合
- 故意设置矛盾的参数(如位深度与颜色类型不匹配)
IDAT块(图像数据):
- 存储实际图像数据
- 可能包含多个IDAT块
- 数据采用zlib压缩
常见考点:
- 在IDAT块中隐藏额外数据
- 使用不正确的压缩方式
- 多个IDAT块的异常排列
IEND块(图像结束标志):
- 固定内容
00 00 00 00 49 45 4E 44 AE 42 60 82 - 标记PNG文件结束
出题人可能在IEND后追加隐藏数据,常规图片查看器会忽略这些数据但hex编辑器可以查看。
- 固定内容
2. 十六进制分析实战:从基础到进阶
2.1 基础分析工具链
工欲善其事,必先利其器。以下是PNG分析必备工具:
hex编辑器:
- Windows:HxD、010 Editor
- Linux:xxd、hexdump
- 跨平台:Bless、ImHex
PNG专用工具:
- pngcheck:验证PNG结构完整性
- pngcrush:修复损坏的PNG文件
- zsteg:检测LSB隐写
脚本工具:
- Python的PIL/Pillow库
- binwalk:检测文件中的隐藏数据
2.2 典型CTF题目解析流程
以一道实际CTF题目为例,演示完整的分析过程:
- 初步检查:
$ file challenge.png challenge.png: PNG image data, 800 x 600, 8-bit/color RGB, non-interlaced $ pngcheck -v challenge.png发现pngcheck报告IHDR块CRC校验错误,这是明显的提示。
- 十六进制分析: 用hex编辑器打开,定位到IHDR块:
00000020: 00 00 00 0D 49 48 44 52 00 00 03 20 00 00 02 58 00000030: 08 02 00 00 00 12 34 56 78这里宽度是0x320(800),高度是0x258(600),但CRC校验值明显异常(应为0x12345678)。
- 修复与验证: 计算正确的CRC并修改后,图片显示异常,说明高度可能被修改。尝试调整高度值发现当改为0x158(344)时,图片底部出现flag。
2.3 高级技巧:IDAT块分析
IDAT块存储实际图像数据,是隐写的热门位置。分析步骤:
- 提取所有IDAT块数据:
from PIL import Image import zlib im = Image.open('challenge.png') idat_data = im.info.get('compression', None) # 获取压缩数据 if idat_data: raw_data = zlib.decompress(idat_data) # 分析raw_data...- 检查数据异常:
- 查找非图像数据(如文本、特殊字节序列)
- 检查数据长度是否与图像尺寸匹配
- 尝试不同的解压缩方式
- LSB隐写检测:
$ zsteg -a challenge.png这会检测每个颜色通道的最低有效位是否隐藏信息。
3. PNG隐写术全解析
3.1 常见隐写技术
LSB隐写:
- 修改像素值的最低有效位来隐藏信息
- 对图像视觉效果影响最小
- 检测工具:zsteg、stegsolve
文件尾追加:
- 在IEND块后直接添加额外数据
- 常规图片查看器会忽略这些数据
- 检测方法:比较文件尺寸与IHDR声明尺寸
块注入:
- 插入自定义PNG块(如tEXt、zTXt)
- 可能包含隐藏信息
- 使用pngcheck查看所有块
调色板修改:
- 对于索引颜色图像,修改调色板条目
- 可能导致某些颜色异常
3.2 实际案例分析
一道典型题目分析过程:
- 使用binwalk检测:
$ binwalk challenge.png DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 PNG image, 800 x 600, 8-bit/color RGB, non-interlaced 41 0x29 Zlib compressed data, compressed 123456 0x1E240 Zip archive data, encrypted...发现尾部附加了ZIP文件。
- 提取隐藏数据:
$ dd if=challenge.png bs=1 skip=123456 of=hidden.zip- 破解ZIP密码: 题目提示密码隐藏在图片中,使用stegsolve分析发现LSB平面有二维码,扫描后得到密码。
4. 实战演练:完整解题过程
4.1 题目描述
给定一个PNG文件,要求找出隐藏的flag。文件可以正常打开,但看起来就是一张普通风景图。
4.2 解题步骤
- 基础检查:
$ pngcheck -v challenge.png ... challenge.png additional data after IEND chunk发现IEND后有额外数据。
- 十六进制分析: 在IEND标记(0xAE426082)后找到以下数据:
$ hexdump -C challenge.png | tail -n 10 0001f0a0 00 00 00 00 49 45 4E 44 AE 42 60 82 50 4B 03 04 0001f0b0 14 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 ...明显是ZIP文件头(PK)。
- 提取ZIP文件:
$ dd if=challenge.png bs=1 skip=$((0x1F0A0+12)) of=hidden.zip- 分析ZIP文件:
$ unzip -l hidden.zip Archive: hidden.zip Length Date Time Name --------- ---------- ----- ---- 256 2023-05-01 10:00 secret.txt需要密码才能解压。
寻找密码: 使用stegsolve检查图片,在Red plane 0发现异常图案,调整色阶后看到文字:"password: CTF2023!"
获取flag:
$ unzip -P CTF2023! hidden.zip $ cat secret.txt FLAG{PNG_H3x_4nalys1s_1s_Fun}4.3 经验总结
系统化的分析流程很重要:
- 先常规检查,再深入分析
- 从简单到复杂逐步排查
工具组合使用:
- 不要依赖单一工具
- 命令行工具+图形工具结合
注意细节:
- CRC校验错误往往是提示
- 文件尺寸异常值得关注
- 不寻常的颜色分布可能是线索
保持怀疑:
- 能正常打开的图片不一定"正常"
- 出题人常常在看似正常处设陷
5. 进阶技巧与资源
5.1 不常见PNG块分析
除了标准块,PNG还支持这些可能包含线索的块:
- tEXt/zTXt/iTXt:文本信息
- tIME:最后修改时间
- pHYs:像素物理尺寸
- sRGB:标准RGB色彩空间
- gAMA:图像gamma值
查看所有块:
$ pngcheck -v challenge.png5.2 自动化分析脚本
Python示例脚本,检测异常块:
import struct import zlib import binascii def analyze_png(filename): with open(filename, 'rb') as f: data = f.read() if data[:8] != b'\x89PNG\r\n\x1a\n': print("Not a valid PNG file") return pos = 8 while pos < len(data): chunk_len = struct.unpack('>I', data[pos:pos+4])[0] chunk_type = data[pos+4:pos+8] chunk_data = data[pos+8:pos+8+chunk_len] chunk_crc = data[pos+8+chunk_len:pos+12+chunk_len] print(f"Chunk: {chunk_type.decode()}, Length: {chunk_len}") if chunk_type == b'tEXt': keyword_end = chunk_data.find(b'\x00') keyword = chunk_data[:keyword_end].decode() text = chunk_data[keyword_end+1:].decode() print(f" Text: {keyword} = {text}") pos += 12 + chunk_len analyze_png('challenge.png')5.3 推荐学习资源
官方文档:
- PNG规范:https://www.w3.org/TR/PNG/
- zlib规范:https://www.zlib.net/
CTF相关:
- CTF Wiki:https://ctf-wiki.org/
- Awesome CTF:https://github.com/apsdehal/awesome-ctf
工具集合:
- StegTools:https://github.com/DominicBreuker/stego-toolkit
- CTF工具包:https://github.com/zardus/ctf-tools
在实际CTF比赛中,PNG分析题目往往结合多种技术。建议从基础开始,逐步掌握文件结构、隐写技术和分析工具的使用。记住,耐心和系统化的思维比知道所有技巧更重要。