Check - Writeup by AI

Check - Writeup by AI

题目信息

项目 内容
题目名称 Check
题目来源 攻防世界
题目类型 Misc / 隐写术
题目文件 check.png (153.2KB)
解题环境 Python 3.x + Pillow + exifread

题目描述

本题提供了一张 PNG 图片,要求分析其中隐藏的信息并提取出 flag。这是一道典型的隐写术题目,考察对 LSB 隐写和 HTML 实体编码的识别与解码能力。

考点分析

核心考点

考点 说明 重要性
PNG 文件结构 了解 PNG 文件的基本结构和块 (chunk) 组织方式 ⭐⭐⭐
LSB 隐写原理 理解最低有效位 (Least Significant Bit) 隐写技术 ⭐⭐⭐⭐⭐
HTML 实体编码 识别和解码 HTML 十六进制实体编码 (&#xHH; 格式) ⭐⭐⭐⭐
Python 图像处理 使用 PIL/Pillow 库处理和分析图像数据 ⭐⭐⭐
多重编码识别 发现 LSB 中隐藏的 HTML 实体编码并逐层解码 ⭐⭐⭐⭐⭐

技术要点

  1. LSB 隐写特点

    • 修改像素颜色值的最后一位二进制数
    • 人眼无法察觉这种微小的颜色变化
    • 每个像素的 RGB 三个通道都可以隐藏 1 bit 数据
    • 对于 510×340 的图片,可隐藏约 520,200 bits 数据
  2. HTML 十六进制实体编码

    • 格式:&#x + 十六进制数字 + ;
    • 例如:f 表示字符 'f' (ASCII 码 0x66)
    • 常用于 Web 开发中转义特殊字符

解题思路

整体流程





获取题目文件
文件基本信息检查
PNG 块结构分析
EXIF 元数据检查
是否发现线索?
LSB 隐写分析
直接提取 flag
发现 HTML 实体编码?
解码 HTML 实体
其他隐写方法
提取 flag

详细分析步骤

步骤 1: 文件基本信息检查
bash 复制代码
文件大小:156925 bytes (153.25 KB)
文件头:89504e470d0a1a0a0000000d49484452
文件类型:PNG 图片 ✓

确认是标准 PNG 文件,文件头签名正确。

步骤 2: PNG 块结构分析

PNG 文件由多个块 (chunk) 组成:

块名 长度 说明
IHDR 13 bytes 图片头信息(宽度、高度、颜色模式等)
IDAT 65536 bytes 压缩的图像数据块(第 1 块)
IDAT 65536 bytes 压缩的图像数据块(第 2 块)
IDAT 25772 bytes 压缩的图像数据块(第 3 块)
IEND 0 bytes 图片结束标记

检查结果:未发现异常的文本块(tEXt、iTXt、zTXt),排除简单的文本隐藏。

步骤 3: EXIF 元数据检查

PNG 文件通常不包含 EXIF 信息,本题中也未发现有用线索。

步骤 4: LSB 隐写分析(关键步骤)

LSB 提取原理

对于每个像素的 RGB 值:

python 复制代码
# 示例:像素 (R=173, G=216, B=199)
R = 173 = 1010110[1]  # LSB = 1
G = 216 = 1101100[0]  # LSB = 0  
B = 199 = 1100011[1]  # LSB = 1
# 提取的比特流:1, 0, 1...

提取过程

  1. 打开图片并转换为 RGB 模式
  2. 读取所有像素数据
  3. 提取每个像素 RGB 通道的最低位
  4. 将比特流每 8 位重组为一个字节
  5. 转换为 ASCII 字符串

关键发现

在 LSB 数据中发现 HTML 十六进制实体编码序列:

复制代码
flag{h0w_4bouT...
步骤 5: HTML 实体解码

识别到编码格式后,进行逐字符解码:

HTML 实体 十六进制 十进制 字符
f 0x66 102 f
l 0x6c 108 l
a 0x61 97 a
g 0x67 103 g
{ 0x7b 123 {
h 0x68 104 h
0 0x30 48 0
w 0x77 119 w
... ... ... ...

详细步骤

环境准备

安装必要的 Python 库:

bash 复制代码
pip install exifread Pillow

完整解题代码

python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
CTF Misc 2-Check Solve Script - 优化版
题目类型:图片隐写/文件分析
来源:攻防世界 Misc 类题目
"""

import os
import struct
from PIL import Image
import exifread
import re

def check_file_info(file_path):
    """检查文件基本信息"""
    print("=" * 60)
    print("【1】文件基本信息")
    print("=" * 60)
    
    file_size = os.path.getsize(file_path)
    print(f"文件大小:{file_size} bytes ({file_size / 1024:.2f} KB)")
    
    with open(file_path, 'rb') as f:
        header = f.read(20)
        print(f"文件头 (16 进制): {header[:16].hex()}")
        
        if header[:8] == b'\x89PNG\r\n\x1a\n':
            print("✓ 文件类型:PNG 图片")
        else:
            print(f"文件类型:未知 (签名:{header[:8]})")

def analyze_png_chunks(file_path):
    """分析 PNG 文件块结构"""
    print("\n" + "=" * 60)
    print("【2】PNG 文件块分析")
    print("=" * 60)
    
    with open(file_path, 'rb') as f:
        f.read(8)  # 跳过 PNG 签名
        
        chunks = []
        while True:
            try:
                length_data = f.read(4)
                if len(length_data) < 4:
                    break
                length = struct.unpack('>I', length_data)[0]
                
                chunk_type = f.read(4)
                if len(chunk_type) < 4:
                    break
                chunk_name = chunk_type.decode('utf-8', errors='ignore')
                
                chunk_data = f.read(length)
                crc_data = f.read(4)
                
                chunks.append({
                    'name': chunk_name,
                    'length': length,
                    'data': chunk_data
                })
                
                print(f"块:{chunk_name:4s}, 长度:{length:6d} bytes")
                
                if chunk_name in ['tEXt', 'iTXt', 'zTXt']:
                    print(f"  └─ 发现文本块!尝试解析...")
                    try:
                        null_pos = chunk_data.find(b'\x00')
                        if null_pos > 0:
                            keyword = chunk_data[:null_pos].decode('latin-1')
                            text = chunk_data[null_pos+1:].decode('latin-1')
                            print(f"     关键字:{keyword}")
                            print(f"     内容:{text}")
                    except Exception as e:
                        print(f"     解析失败:{e}")
                
            except Exception as e:
                print(f"解析错误:{e}")
                break

def check_exif(file_path):
    """检查 EXIF 信息"""
    print("\n" + "=" * 60)
    print("【3】EXIF 元数据分析")
    print("=" * 60)
    
    try:
        with open(file_path, 'rb') as f:
            exif_data = exifread.process_file(f, details=False)
            
            if exif_data:
                print(f"找到 {len(exif_data)} 个 EXIF 字段:")
                for tag in sorted(exif_data.keys()):
                    if tag not in ['JPEGThumbnail', 'TIFFThumbnail']:
                        print(f"  {tag}: {exif_data[tag]}")
            else:
                print("未发现 EXIF 信息")
    except Exception as e:
        print(f"读取 EXIF 失败:{e}")

def extract_lsb_data(file_path):
    """提取 LSB 数据"""
    try:
        img = Image.open(file_path)
        if img.mode != 'RGB' and img.mode != 'RGBA':
            img = img.convert('RGB')
        
        pixels = list(img.getdata())
        
        all_bits = []
        for pixel in pixels:
            r, g, b = pixel[:3]
            all_bits.extend([r & 1, g & 1, b & 1])
        
        data = []
        for i in range(0, min(len(all_bits), 200000) - 7, 8):
            byte = 0
            for j in range(8):
                byte = (byte << 1) | all_bits[i + j]
            data.append(byte)
        
        return bytes(data).decode('latin-1', errors='ignore')
    except Exception as e:
        print(f"LSB 提取失败:{e}")
        return ""

def decode_html_entities(text):
    """解码 HTML 十六进制实体"""
    entities = re.findall(r'&#x([0-9a-fA-F]+);', text)
    if entities:
        decoded = ''.join([chr(int(e, 16)) for e in entities])
        return decoded
    return ""

def lsb_analysis_optimized(file_path):
    """优化的 LSB 分析"""
    print("\n" + "=" * 60)
    print("【4】LSB 隐写分析")
    print("=" * 60)
    
    try:
        img = Image.open(file_path)
        print(f"图片尺寸:{img.size[0]} x {img.size[1]}")
        print(f"图片模式:{img.mode}")
        
        extracted_str = extract_lsb_data(file_path)
        
        if not extracted_str:
            print("未提取到有效数据")
            return None
        
        html_pattern = r'(&#x[0-9a-fA-F]+;)+'
        matches = re.findall(html_pattern, extracted_str)
        
        if matches:
            print(f"\n✓ 发现 HTML 实体编码序列 ({len(matches)} 组)")
            
            longest_match = max(re.finditer(html_pattern, extracted_str), key=lambda m: len(m.group()))
            full_encoded = longest_match.group()
            
            print(f"\n提取的 HTML 实体序列:")
            display_len = min(200, len(full_encoded))
            print(f"{full_encoded[:display_len]}..." if len(full_encoded) > display_len else full_encoded)
            
            decoded = decode_html_entities(full_encoded)
            print(f"\n解码结果:")
            print(decoded)
            
            flag_match = re.search(r'flag\{[^}]+\}', decoded)
            if flag_match:
                print(f"\n🎯 成功提取 FLAG: {flag_match.group()}")
                return flag_match.group()
        else:
            print("\n未发现 HTML 实体编码")
            
            if 'flag' in extracted_str.lower():
                flag_match = re.search(r'flag\{[^}]+\}', extracted_str)
                if flag_match:
                    print(f"\n🎯 直接发现 FLAG: {flag_match.group()}")
                    return flag_match.group()
        
        return None
            
    except Exception as e:
        print(f"LSB 分析失败:{e}")
        return None

def main():
    """主函数"""
    file_path = "check.png"
    
    if not os.path.exists(file_path):
        print(f"错误:文件 {file_path} 不存在")
        return
    
    print("\n")
    print("╔" + "=" * 58 + "╗")
    print("║" + " " * 12 + "CTF Misc 2-Check 解题脚本 (优化版)" + " " * 12 + "║")
    print("╚" + "=" * 58 + "╝")
    
    check_file_info(file_path)
    analyze_png_chunks(file_path)
    check_exif(file_path)
    
    flag = lsb_analysis_optimized(file_path)
    
    print("\n" + "=" * 60)
    if flag:
        print(f"✅ 成功获取 FLAG: {flag}")
    else:
        print("分析完成!请检查以上结果寻找 flag。")
    print("=" * 60 + "\n")

if __name__ == "__main__":
    main()

总结

技术要点回顾

技术点 掌握程度 说明
PNG 文件结构 ✅ 掌握 理解 IHDR、IDAT、IEND 等块的作用
LSB 隐写提取 ✅ 掌握 能从 RGB 通道提取最低有效位
HTML 实体识别 ✅ 掌握 快速识别 &#xHH; 编码格式
正则表达式 ✅ 掌握 使用正则匹配和提取编码序列
Python 图像处理 ✅ 掌握 熟练使用 PIL/Pillow 处理图片

解题关键点

  1. 双重隐藏技术

    • 第一层:LSB 隐写(隐藏在像素最低位)
    • 第二层:HTML 实体编码(隐藏真实内容)
  2. 正确的解码顺序

    复制代码
    原始图片 → LSB 提取 → HTML 实体编码 → 解码 → 明文 flag
  3. 编码识别能力

    • 看到 &#x 开头要立即想到 HTML 实体编码
    • 这是 Web 安全中常见的编码方式
相关推荐
终端鹿2 小时前
Vue3 + TypeScript 大型项目状态管理:Pinia 类型安全最佳实践
安全·ubuntu·typescript
酿情师2 小时前
2026软件系统安全赛初赛MISC--steganography
数据库·安全
小陈工2 小时前
2026年3月25日技术资讯洞察:开源芯片革命、Postgres文件系统与AI Agent安全新范式
开发语言·数据库·人工智能·python·安全·web安全·开源
csdn_aspnet2 小时前
MySQL安全加固十大硬核操作,从账号权限最小化到SSL加密,构建生产环境基础防护层
mysql·安全·ssl·waf
hanniuniu132 小时前
F5发布AI防护全新产品矩阵,定义企业级AI安全新标准
人工智能·安全
wordbaby2 小时前
震惊!Apifox 供应链“投毒”事件深度解析:你的 SSH 密钥安全吗?
安全
不一样的故事1263 小时前
线号管并非必须和端子端面绝对齐平
网络·安全
码农小白AI3 小时前
AI报告文档审核护航飞行安全:IACheck打造航电与飞控检测报告智能审核新利器
人工智能·安全
PcVue China3 小时前
PcVue荣获IEC62443-4-1 国际网络安全认证:深耕中国市场,助力“新质生产力”安全转型
安全·web安全·等保·iec62442--4-1·gb/t 42457-2023·安全开发周期