PNG_challenge
字数 2117 2025-08-30 06:50:35

PNG文件结构与CTF攻防技术详解

一、PNG文件基础结构

1. PNG文件签名

PNG文件以固定的8字节签名开头,用于标识文件类型:

89 50 4E 47 0D 0A 1A 0A

这8个字节是PNG文件的标识符,任何合法的PNG文件都必须以此开头。

2. 数据块(Chunks)结构

PNG文件由多个数据块组成,每个数据块包含以下4个部分:

  1. 长度(Length):4字节,大端序存储,指定数据段的字节数(不包括长度、类型和CRC)
  2. 块类型(Chunk Type):4字节ASCII码,标识数据块的功能(如IHDR、IDAT等)
  3. 数据(Chunk Data):长度指定的数据内容
  4. CRC校验(CRC):4字节,循环冗余校验码,确保数据块完整性

注意:CRC计算范围只包括块类型和数据,不包括长度字段

二、关键数据块详解

1. IHDR块(文件头数据块)

  • 每个PNG图片有且只有一个IHDR块
  • 固定长度:13字节
  • 结构内容:
    • 宽度(4字节,大端序)
    • 高度(4字节,大端序)
    • 位深度(1字节):1、2、4、8、16
    • 颜色类型(1字节):
      • 0=灰度
      • 2=RGB
      • 3=索引颜色
      • 4=灰度+Alpha
      • 6=RGB+Alpha
    • 压缩方法(1字节):通常为0(deflate)
    • 滤波方法(1字节):通常为0
    • 隔行扫描方法(1字节):0=非隔行,1=Adam7隔行

示例分析:

00 00 00 0D 49 48 44 52 00 00 05 DA 00 00 08 8D 08 02 00 00 00 1C 4C 11 E6
  • 长度:00 00 00 0D → 13字节
  • 类型:49 48 44 52 → "IHDR"
  • 数据:
    • 宽度:00 00 05 DA → 1498像素
    • 高度:00 00 08 8D → 2189像素
    • 位深度:08 → 8位
    • 颜色类型:02 → RGB模式
    • 压缩方式:00 → deflate
    • 滤波方式:00
    • 隔行扫描:00
  • CRC:1C 4C 11 E6

2. IDAT块(图像数据块)

  • 存储压缩后的图像像素数据,使用deflate算法
  • 一张PNG可能有多个IDAT块,解码时按顺序拼接
  • 常见IDAT块大小限制为65536字节(64KB),但理论上最大可达2GB
  • DEFLATE算法:
    • LZ77算法:查找和替换重复的数据序列
    • Huffman编码:对符号进行变长编码
  • 预处理过滤器(5种类型):
    • 类型0:无过滤
    • 类型1:Sub(当前值减前一个像素值)
    • 类型2:Up(当前值减上一行对应位置值)
    • 类型3:Average(当前值减前一个和上一行像素的平均值)
    • 类型4:Paeth预测器

3. IEND块(文件尾数据块)

  • 固定结构:
00 00 00 00 49 45 4E 44 AE 42 60 82
  • 数据长度为0,类型为"IEND",CRC固定为AE 42 60 82

三、辅助数据块

1. PLTE块(调色板)

  • 定义调色板(索引颜色模式)的颜色表
  • 每3字节表示一个颜色(RGB),最多256个颜色(768字节)
  • 必须在IDAT块之前出现
  • 仅当颜色类型为3时必须存在

2. 其他辅助块

  • 图像显示:tRNS、bKGD、sRGB、pHYs
  • 颜色管理:cHRM、gAMA、iCCP、sRGB、sBIT
  • 元数据:tEXt、zTXt、iTXt、tIME
  • 调色板优化:hIST、sPLT

四、PNG攻击技术汇总

1. 文件插入字符信息

  • 直接附加:在IEND块后添加任意数据
  • 使用辅助块隐藏信息:
    • tEXt块:存储未压缩文本,格式[关键字]\0[文本内容]
    • zTXt块:存储压缩文本,格式[关键字]\0[压缩标志]\0[压缩数据]
    • iTXt块:支持国际化文本,格式[关键字]\0[压缩标志]\0[压缩方式]\0[语言标签]\0[翻译关键字]\0[文本]
    • 自定义辅助块:块类型为小写字母开头

2. 结构缺失/混乱攻击

  • 多个IHDR块:可能隐藏多个图像
  • 异常数据块顺序:可能导致解析异常
  • 解决方法:使用工具分析并重组合法结构

3. IDAT块隐写

  • 异常IDAT块大小或数量
  • 隐藏额外数据在IDAT块中
  • 工具:TweakPNG分析处理

4. CRC宽高爆破

  • IHDR块宽高被修改但CRC未更新
  • 爆破原理:已知CRC和部分数据,爆破宽高值
  • 脚本示例:
import zlib
import struct

def crc32(data):
    return zlib.crc32(data) & 0xffffffff

def brute_force_crc(png_file):
    with open(png_file, 'rb') as f:
        data = f.read()
    
    # 定位IHDR块
    ihdr_pos = data.find(b'IHDR') - 4
    ihdr_data = data[ihdr_pos:ihdr_pos+29]
    
    original_crc = struct.unpack('>I', ihdr_data[21:25])[0]
    
    # 常见分辨率组合
    common_sizes = [(800, 600), (1024, 768), (1920, 1080), (1280, 720)]
    
    for width, height in common_sizes:
        # 构造IHDR数据
        new_data = ihdr_data[8:12]  # IHDR
        new_data += struct.pack('>I', width)
        new_data += struct.pack('>I', height)
        new_data += ihdr_data[20:21]  # 剩余字段
        
        # 计算CRC
        crc = crc32(new_data)
        if crc == original_crc:
            return width, height
    
    return None

5. 颜色通道隐写

LSB隐写(最低有效位)

  • 修改像素通道的最低位(第0位)
  • 人眼几乎无法察觉变化
  • 工具:stegsolve、zsteg

MSB隐写(最高有效位)

  • 修改像素通道的最高位
  • 变化较明显但仍有隐蔽性

通道组合爆破

  • 当隐写通道未知时,可爆破所有可能的通道组合
  • 示例脚本:
from PIL import Image
import itertools

def extract_from_channels(image_path, dict_words):
    img = Image.open(image_path)
    pixels = img.load()
    width, height = img.size
    
    # 所有可能的通道和位组合
    channels = ['R', 'G', 'B']
    bits = range(8)
    
    for combo in itertools.product(channels, bits):
        channel, bit = combo
        extracted = []
        
        for y in range(height):
            for x in range(width):
                pixel = pixels[x, y]
                if channel == 'R':
                    val = pixel[0]
                elif channel == 'G':
                    val = pixel[1]
                else:
                    val = pixel[2]
                
                extracted.append(str((val >> bit) & 1))
        
        binary = ''.join(extracted)
        # 检查是否包含字典中的关键词
        for word in dict_words:
            if word in binary:
                return combo, binary
    
    return None

五、实用工具推荐

  1. 010 Editor:强大的十六进制编辑器
  2. TweakPNG:分析处理PNG数据块
  3. exiftool:提取PNG元信息
  4. stegsolve:分析颜色通道隐写
  5. zsteg:检测LSB隐写数据

六、开发脚本示例

1. PNG数据块分析脚本

import struct

def analyze_png_chunks(png_file):
    with open(png_file, 'rb') as f:
        data = f.read()
    
    # 检查PNG签名
    if data[:8] != b'\x89PNG\r\n\x1a\n':
        print("Not a valid PNG file")
        return
    
    pos = 8  # 跳过签名
    
    while pos < len(data):
        # 读取块长度
        length = struct.unpack('>I', data[pos:pos+4])[0]
        pos += 4
        
        # 读取块类型
        chunk_type = data[pos:pos+4]
        pos += 4
        
        # 读取块数据
        chunk_data = data[pos:pos+length]
        pos += length
        
        # 读取CRC
        crc = struct.unpack('>I', data[pos:pos+4])[0]
        pos += 4
        
        print(f"Chunk: {chunk_type.decode()}")
        print(f"Length: {length}")
        print(f"CRC: {hex(crc)}")
        
        # 特殊处理IHDR块
        if chunk_type == b'IHDR':
            width, height = struct.unpack('>II', chunk_data[:8])
            print(f"Image size: {width}x{height}")
        
        print("---")

2. 自定义IDAT块生成脚本

import zlib
import struct
from PIL import Image

def create_png_with_custom_idat(output_path, width, height, idat_sizes):
    # 创建空白图像
    img = Image.new('RGB', (width, height), (255, 255, 255))
    
    # 准备IHDR数据
    ihdr_data = struct.pack('>IIBBBBB', width, height, 8, 2, 0, 0, 0)
    ihdr_crc = zlib.crc32(b'IHDR' + ihdr_data) & 0xffffffff
    
    # 准备IDAT数据
    raw_data = img.tobytes()
    compressed = zlib.compress(raw_data)
    
    # 分割IDAT数据
    idat_chunks = []
    pos = 0
    for size in idat_sizes:
        chunk_data = compressed[pos:pos+size]
        pos += size
        if not chunk_data:
            continue
        
        idat_chunks.append({
            'length': len(chunk_data),
            'type': 'IDAT',
            'data': chunk_data,
            'crc': zlib.crc32(b'IDAT' + chunk_data) & 0xffffffff
        })
    
    # 构建PNG文件
    with open(output_path, 'wb') as f:
        # 写入签名
        f.write(b'\x89PNG\r\n\x1a\n')
        
        # 写入IHDR
        f.write(struct.pack('>I', 13))
        f.write(b'IHDR')
        f.write(ihdr_data)
        f.write(struct.pack('>I', ihdr_crc))
        
        # 写入IDAT块
        for chunk in idat_chunks:
            f.write(struct.pack('>I', chunk['length']))
            f.write(chunk['type'].encode())
            f.write(chunk['data'])
            f.write(struct.pack('>I', chunk['crc']))
        
        # 写入IEND
        f.write(struct.pack('>I', 0))
        f.write(b'IEND')
        f.write(struct.pack('>I', 0xAE426082))

七、总结

PNG文件结构复杂但规范,为CTF比赛提供了丰富的出题点。掌握PNG的核心结构和常见攻击技术,能够有效解决大多数PNG相关的CTF题目。关键点包括:

  1. 理解PNG数据块结构和校验机制
  2. 熟悉IHDR、IDAT等关键块的解析方法
  3. 掌握CRC宽高爆破、IDAT隐写等攻击技术
  4. 熟练使用相关分析工具和脚本开发

通过系统学习和实践,可以逐步掌握PNG文件的深入分析和攻击技术。

PNG文件结构与CTF攻防技术详解 一、PNG文件基础结构 1. PNG文件签名 PNG文件以固定的8字节签名开头,用于标识文件类型: 这8个字节是PNG文件的标识符,任何合法的PNG文件都必须以此开头。 2. 数据块(Chunks)结构 PNG文件由多个数据块组成,每个数据块包含以下4个部分: 长度(Length) :4字节,大端序存储,指定数据段的字节数(不包括长度、类型和CRC) 块类型(Chunk Type) :4字节ASCII码,标识数据块的功能(如IHDR、IDAT等) 数据(Chunk Data) :长度指定的数据内容 CRC校验(CRC) :4字节,循环冗余校验码,确保数据块完整性 注意:CRC计算范围只包括块类型和数据,不包括长度字段 二、关键数据块详解 1. IHDR块(文件头数据块) 每个PNG图片有且只有一个IHDR块 固定长度:13字节 结构内容: 宽度(4字节,大端序) 高度(4字节,大端序) 位深度(1字节):1、2、4、8、16 颜色类型(1字节): 0=灰度 2=RGB 3=索引颜色 4=灰度+Alpha 6=RGB+Alpha 压缩方法(1字节):通常为0(deflate) 滤波方法(1字节):通常为0 隔行扫描方法(1字节):0=非隔行,1=Adam7隔行 示例分析: 长度:00 00 00 0D → 13字节 类型:49 48 44 52 → "IHDR" 数据: 宽度:00 00 05 DA → 1498像素 高度:00 00 08 8D → 2189像素 位深度:08 → 8位 颜色类型:02 → RGB模式 压缩方式:00 → deflate 滤波方式:00 隔行扫描:00 CRC:1C 4C 11 E6 2. IDAT块(图像数据块) 存储压缩后的图像像素数据,使用deflate算法 一张PNG可能有多个IDAT块,解码时按顺序拼接 常见IDAT块大小限制为65536字节(64KB),但理论上最大可达2GB DEFLATE算法: LZ77算法:查找和替换重复的数据序列 Huffman编码:对符号进行变长编码 预处理过滤器(5种类型): 类型0:无过滤 类型1:Sub(当前值减前一个像素值) 类型2:Up(当前值减上一行对应位置值) 类型3:Average(当前值减前一个和上一行像素的平均值) 类型4:Paeth预测器 3. IEND块(文件尾数据块) 固定结构: 数据长度为0,类型为"IEND",CRC固定为AE 42 60 82 三、辅助数据块 1. PLTE块(调色板) 定义调色板(索引颜色模式)的颜色表 每3字节表示一个颜色(RGB),最多256个颜色(768字节) 必须在IDAT块之前出现 仅当颜色类型为3时必须存在 2. 其他辅助块 图像显示:tRNS、bKGD、sRGB、pHYs 颜色管理:cHRM、gAMA、iCCP、sRGB、sBIT 元数据:tEXt、zTXt、iTXt、tIME 调色板优化:hIST、sPLT 四、PNG攻击技术汇总 1. 文件插入字符信息 直接附加:在IEND块后添加任意数据 使用辅助块隐藏信息: tEXt块:存储未压缩文本,格式 [关键字]\0[文本内容] zTXt块:存储压缩文本,格式 [关键字]\0[压缩标志]\0[压缩数据] iTXt块:支持国际化文本,格式 [关键字]\0[压缩标志]\0[压缩方式]\0[语言标签]\0[翻译关键字]\0[文本] 自定义辅助块:块类型为小写字母开头 2. 结构缺失/混乱攻击 多个IHDR块:可能隐藏多个图像 异常数据块顺序:可能导致解析异常 解决方法:使用工具分析并重组合法结构 3. IDAT块隐写 异常IDAT块大小或数量 隐藏额外数据在IDAT块中 工具:TweakPNG分析处理 4. CRC宽高爆破 IHDR块宽高被修改但CRC未更新 爆破原理:已知CRC和部分数据,爆破宽高值 脚本示例: 5. 颜色通道隐写 LSB隐写(最低有效位) 修改像素通道的最低位(第0位) 人眼几乎无法察觉变化 工具:stegsolve、zsteg MSB隐写(最高有效位) 修改像素通道的最高位 变化较明显但仍有隐蔽性 通道组合爆破 当隐写通道未知时,可爆破所有可能的通道组合 示例脚本: 五、实用工具推荐 010 Editor :强大的十六进制编辑器 TweakPNG :分析处理PNG数据块 exiftool :提取PNG元信息 stegsolve :分析颜色通道隐写 zsteg :检测LSB隐写数据 六、开发脚本示例 1. PNG数据块分析脚本 2. 自定义IDAT块生成脚本 七、总结 PNG文件结构复杂但规范,为CTF比赛提供了丰富的出题点。掌握PNG的核心结构和常见攻击技术,能够有效解决大多数PNG相关的CTF题目。关键点包括: 理解PNG数据块结构和校验机制 熟悉IHDR、IDAT等关键块的解析方法 掌握CRC宽高爆破、IDAT隐写等攻击技术 熟练使用相关分析工具和脚本开发 通过系统学习和实践,可以逐步掌握PNG文件的深入分析和攻击技术。