姚令武 2026-05-17 09:15 采纳率: 98.5%
浏览 0

如何从DDCTF2018流量包中提取隐藏的PNG图片?

常见技术问题:在分析DDCTF2018流量包(如`misc100.pcapng`)时,Wireshark过滤`http || tcp.stream eq 0`可见HTTP响应中夹带异常二进制数据,但直接导出对象(Export Objects → HTTP)无法识别PNG——因图片被分片嵌入多个TCP流、头部被截断或混淆(如PNG魔数`\x89PNG\r\n\x1a\n`被拆分跨包),且部分数据段混杂在非HTTP协议(如DNS TXT记录或ICMP载荷)中。此外,使用`tshark -r misc100.pcapng -T fields -e data.text`提取ASCII十六进制时易丢失原始字节顺序;而盲目拼接TCP流易引入冗余控制字符或错位偏移。关键难点在于:如何准确定位所有含PNG有效载荷的数据包、自动对齐字节边界、修复缺失的文件头/尾,并验证CRC与IHDR结构完整性?这要求结合协议解析、流重组、熵值分析与PNG规范校验,而非依赖单一GUI功能。
  • 写回答

1条回答 默认 最新

  • Airbnb爱彼迎 2026-05-17 09:16
    关注
    ```html

    一、现象层:Wireshark GUI导出失效的典型表现

    • misc100.pcapng 中应用过滤器 http || tcp.stream eq 0,可见 HTTP 响应体含高熵二进制数据(如连续 0x89 0x50 0x4e 0x47 片段但不连续);
    • 右键 → Export Objects → HTTP 导出所有对象,无一被识别为 PNG(file 命令返回 datacannot open);
    • DNS 查询中出现异常长的 TXT 记录(如 Base64 编码的 256 字节字符串),ICMP Echo Reply 载荷含非零填充字节;
    • tshark -r misc100.pcapng -T fields -e data.text 输出大量 DATA TEXT 字段,但因自动 ASCII 解码/截断导致原始字节丢失(如 \x89?)。

    二、协议层:多协议载荷混杂与TCP流边界错位

    DDCTF2018 misc100 的隐写设计刻意规避传统提取路径:

    协议类型载荷特征常见陷阱
    HTTP响应体含 PNG 数据块(IDAT),但首包缺失 IHDR,末包缺失 IENDTCP 重传/乱序导致流重组后头部偏移 +3 字节
    DNS TXTBase64 编码的 PNG 数据段(每条记录 ≤ 255 字节,RFC 1035 限制)Base64 补齐字符 = 被丢弃,解码后长度校验失败
    ICMPICMP Echo Reply 的 Data 字段含 PNG 校验字节(CRC32 of IHDR)Wireshark 默认解析为 ASCII,需手动设置 Raw 视图查看十六进制

    三、分析层:熵值驱动+协议感知的载荷定位策略

    采用多维启发式扫描替代盲目拼接:

    1. 熵值初筛:对每个 TCP 流/UDP 载荷计算 Shannon 熵(阈值 > 7.2 bit/byte),快速排除文本型流量;
    2. 魔数滑动匹配:在原始字节流中以 1-byte 步进搜索 \x89PNG\r\n\x1a\n(8 字节)及 IDAT/IHDR/IEND 四字节签名;
    3. 协议上下文绑定:若某 DNS TXT 记录熵值高且含 Base64 字符集(A-Za-z0-9+/=),则提取并批量 base64 -d 验证输出是否含 PNG 签名;
    4. 流级偏移对齐:使用 tshark -r misc100.pcapng -qz follow,tcp,raw,0 获取未解析原始流,再用 Python scapy 按 IP/TCP 五元组聚合并去重 ACK/PSH 标志干扰。

    四、修复层:PNG 结构完整性自动化重建流程

    graph TD A[原始PCAP] --> B{按协议分流} B --> B1[HTTP 流:提取 raw payload] B --> B2[DNS TXT:Base64 decode] B --> B3[ICMP Data:hexdump -C] B1 --> C[滑动窗口搜索 PNG 签名位置] B2 --> C B3 --> C C --> D[按 PNG chunk 结构对齐:IHDR→IDAT×n→IEND] D --> E[补全缺失头尾:
    • 插入标准 IHDR(宽/高从 IDAT 推断)
    • 计算并追加合法 CRC32] E --> F[验证:pngcheck -v reconstructed.png]

    五、验证层:超越文件头的深度结构校验

    仅恢复魔数不足以保证可渲染性,必须校验:

    • IHDR 完整性:检查宽度/高度是否为非零整数、bit depth ∈ {1,2,4,8,16}、color type ∈ {0,2,3,4,6};
    • IDAT 合法性:ZLIB 头部 0x78 0x9c0x78 0xda 存在,且 zlib.decompress() 不抛异常;
    • CRC32 重算:对每个 chunk type + data 字段执行 zlib.crc32(b'IHDR' + ihdr_data) & 0xffffffff,比对原始 chunk CRC 字段;
    • 像素一致性:用 PIL.Image.open() 加载后检查 img.size 与 IHDR 是否一致,避免“假 PNG”(如伪造头但 IDAT 解压失败)。

    六、工具链:生产级自动化脚本核心逻辑(Python + Scapy)

    from scapy.all import *
    import zlib, base64, binascii
    from PIL import Image
    from io import BytesIO
    
    def extract_png_chunks(pcap_file):
        pkts = rdpcap(pcap_file)
        candidates = []
        
        # Step 1: HTTP payload extraction (raw, no reassembly)
        for pkt in pkts:
            if TCP in pkt and Raw in pkt[TCP]:
                if pkt[TCP].dport == 80 or pkt[TCP].sport == 80:
                    payload = bytes(pkt[TCP][Raw])
                    if b'\x89PNG' in payload[:16]:  # partial header match
                        candidates.append(('http', payload))
        
        # Step 2: DNS TXT decode
        for pkt in pkts:
            if DNS in pkt and pkt[DNS].ancount > 0:
                for rr in pkt[DNS].an:
                    if rr.type == 16 and hasattr(rr, 'rdata') and isinstance(rr.rdata, str):
                        try:
                            decoded = base64.b64decode(rr.rdata.replace(' ', ''))
                            if decoded.startswith(b'\x89PNG') or b'IDAT' in decoded:
                                candidates.append(('dns', decoded))
                        except Exception: pass
        
        # Step 3: Stitch & validate
        full_data = b''.join([cand[1] for cand in candidates])
        # ... then IHDR repair, CRC fix, pngcheck call
        return full_data
    
    ```
    评论

报告相同问题?

问题事件

  • 创建了问题 今天