从零破解CTF图像隐写题:Python脚本实战ZIP爆破与DWT+Arnold变换

1. 前言:当CTF新手遇到"缺失的数据"

第一次看到这道题时,我正对着一个加密的ZIP压缩包和一张看似普通的PNG图片发呆。题目描述只有简单的一句:"关键数据藏在图片里,但需要先找到打开它的钥匙"。作为刚接触CTF安全竞赛的新手,这种涉及多重加密的题目确实让人望而生畏。但经过系统拆解后,我发现这道题完美融合了 ZIP密码爆破 图像隐写分析 两大基础技能点,是入门数字取证的绝佳案例。

本文将带你用Python还原完整解题流程,重点解决两个核心问题:

  • 如何在没有密码的情况下暴力破解ZIP文件
  • 如何从图片中提取经过DWT小波变换和Arnold置乱加密的隐藏信息

2. 工具准备与环境搭建

2.1 必备工具清单

在开始前,请确保准备好以下工具和环境:

# Python库安装
pip install pywavelets opencv-python numpy
工具名称 用途说明 获取方式
ARCHPR ZIP密码字典攻击工具 官网下载绿色版
Python 3.8+ 运行隐写分析脚本 官方安装包
7-Zip 压缩包处理 开源免费工具
文本编辑器 查看/修改脚本 VS Code/PyCharm等

2.2 创建解题工作目录

建议按以下结构组织文件:

/CTF-Challenge
│── /original_files
│   ├── secret.zip  # 待破解的加密压缩包
│   └── hint.txt    # 题目提供的线索
│── /dictionaries   # 密码字典存放处
│── /output         # 解压和脚本输出目录
│── extract_flag.py # 隐写分析主脚本

3. 第一阶段:破解ZIP密码

3.1 分析压缩包特征

首先用7-Zip检查压缩包信息:

7z l -slt secret.zip

关键信息提取:

  • 加密算法:ZIP传统加密(非AES)
  • 包含文件:a.png
  • 注释字段:无特殊提示

这类传统加密的ZIP对字典攻击非常敏感,我们需要准备合适的密码字典。

3.2 制作针对性字典

根据CTF出题规律,优先尝试以下密码类型:

# 常见弱密码生成示例
weak_passwords = [
    "password", "123456", "qwerty",
    "admin", "ctf", "flag", 
    "secret", "pavilion", "2023",
    "password123", "welcome1"
]

专业技巧 :将题目描述中的关键词(如"缺失的数据")进行以下变形后加入字典:

  • 拼音全拼:queshideshuju
  • 首字母缩写:qsdsj
  • 常见英文翻译:missingdata

3.3 使用ARCHPR进行爆破

ARCHPR(Advanced Archive Password Recovery)操作步骤:

  1. 选择ZIP文件路径
  2. 攻击类型选择"字典攻击"
  3. 加载准备好的字典文件
  4. 开始攻击并监控进度

注意:如果字典攻击未成功,可尝试切换为"掩码攻击"或"暴力破解",但耗时将显著增加

成功破解后,我们得到密码: pavilion ,解压获得关键文件 a.png

4. 第二阶段:图像隐写分析

4.1 初识DWT+Arnold隐写技术

题目使用的隐写方案结合了两种核心技术:

  1. DWT(离散小波变换)

    • 将图像分解为不同频率子带
    • 在HL/LH/HH等高频区域嵌入信息
    • 对视觉影响小,鲁棒性强
  2. Arnold变换

    • 经典的图像置乱算法
    • 通过迭代使像素位置随机化
    • 需要知道变换次数才能复原

4.2 逆向分析Python脚本

从题目给出的脚本中,我们提取出关键参数:

class WaterMarkDWT:
    def __init__(self, origin: str, watermark: str, key: int, weight: list):
        self.key = key      # Arnold变换次数=20
        self.coef = weight  # 小波系数=[0.2, 0.2, 0.5, 0.4]

逆向思路:

  1. 对a.png执行Arnold正变换20次
  2. 进行三级'db2'小波分解
  3. 按给定系数提取各子带隐藏信息

4.3 完整逆向脚本实现

import cv2
import numpy as np
import pywt

def recover_hidden_data(encrypted_img_path):
    # 初始化参数(与加密时一致)
    arnold_iterations = 20
    wavelet_type = 'db2'
    decomposition_level = 3
    coefficients = [0.2, 0.2, 0.5, 0.4]
    
    # 读取加密图像
    img = cv2.imread(encrypted_img_path, cv2.IMREAD_GRAYSCALE)
    
    # Arnold正变换(置乱)
    def arnold_transform(image, iterations):
        h, w = image.shape
        for _ in range(iterations):
            temp = np.zeros_like(image)
            for i in range(h):
                for j in range(w):
                    x = (i + j) % h
                    y = (i + 2*j) % w
                    temp[x, y] = image[i, j]
            image = temp
        return image
    
    # 执行置乱还原
    arnold_img = arnold_transform(img, arnold_iterations)
    
    # 小波分解
    coeffs = pywt.wavedec2(arnold_img, wavelet_type, level=decomposition_level)
    
    # 提取隐藏信息
    ca1 = coeffs[0] / coefficients[0]
    ch1 = coeffs[1][0] / coefficients[1]
    cv1 = coeffs[1][1] / coefficients[2]
    cd1 = coeffs[1][2] / coefficients[3]
    
    # 小波重构
    recovered_watermark = pywt.waverec2([ca1, (ch1, cv1, cd1)], wavelet_type)
    
    # 后处理
    recovered_watermark = np.uint8(np.clip(recovered_watermark, 0, 255))
    cv2.imwrite('recovered_flag.png', recovered_watermark)
    return recovered_watermark

# 执行恢复
recover_hidden_data('a.png')

4.4 结果验证与优化

首次运行可能遇到以下问题及解决方案:

  1. 图像全黑/全白

    • 检查Arnold变换次数是否正确
    • 尝试调整小波系数±10%
  2. 部分信息模糊

    # 添加形态学处理增强对比度
    kernel = np.ones((3,3), np.uint8)
    enhanced = cv2.morphologyEx(result, cv2.MORPH_CLOSE, kernel)
    
  3. 文字扭曲严重

    • 在Arnold变换后添加高斯滤波
    blurred = cv2.GaussianBlur(arnold_img, (5,5), 0)
    

最终输出的 recovered_flag.png 中应清晰显示flag内容。

5. 技术深度解析

5.1 DWT隐写原理详解

三级小波分解后的子带分布:

子带 频率特征 常用嵌入强度
LL3 最低频近似成分 不嵌入
HL3 水平方向高频 0.1-0.3
LH3 垂直方向高频 0.1-0.3
HH3 对角线方向高频 0.3-0.5

嵌入公式: $$ W' = W + \alpha \cdot S $$ 其中$W$为原始小波系数,$S$为秘密信息,$\alpha$为强度系数。

5.2 Arnold变换数学原理

Arnold变换的矩阵表示为: $$ \begin{pmatrix} x' \ y' \end{pmatrix} = \begin{pmatrix} 1 & 1 \ 1 & 2 \end{pmatrix} \begin{pmatrix} x \ y \end{pmatrix} \mod N $$

其周期性取决于图像尺寸$N$,对于$N \times N$图像,变换周期$T$满足: $$ T \approx \frac{\pi N}{2\sqrt{3}} $$

6. 防御与检测方案

6.1 如何增强隐写安全性

如果作为出题方,可以采取以下措施增加破解难度:

  1. 复合加密

    # 先Arnold再DWT最后LSB
    img = arnold(img, 20)
    img = dwt_embed(img, secret)
    img = lsb_replace(img, key)
    
  2. 动态参数

    • 将迭代次数作为密码函数
    iterations = hash(password) % 50 + 10
    
  3. 噪声干扰

    # 添加高斯噪声
    noise = np.random.normal(0, 5, img.shape)
    img = np.clip(img + noise, 0, 255)
    

6.2 隐写检测技术

使用Python检测可疑图像:

def detect_stegano(image_path):
    img = cv2.imread(image_path, 0)
    # 计算残差图像
    blur = cv2.GaussianBlur(img, (5,5), 0)
    residual = cv2.absdiff(img, blur)
    
    # 分析统计特征
    mean = np.mean(residual)
    std = np.std(residual)
    
    # 经验阈值
    if std > 15 and mean > 5:
        print("可能包含隐写内容!")
        return True
    return False

7. 扩展训练建议

想要真正掌握这类题型,建议按以下路径练习:

  1. 基础训练

    • 单独练习ZIP密码爆破(尝试不同字典)
    • 用Python实现Arnold变换可视化
  2. 中级挑战

    • 修改脚本支持不同小波基(haar/sym等)
    • 尝试彩色图像的隐写分析
  3. 高级应用

    # 实时隐写检测工具
    import matplotlib.pyplot as plt
    
    def analyze_frequency(image):
        coeffs = pywt.wavedec2(image, 'db2', level=3)
        plt.figure(figsize=(12,8))
        for i, (c, (h,v,d)) in enumerate(zip(coeffs[:-1], coeffs[1:])):
            plt.subplot(3,3,i+1)
            plt.imshow(h, cmap='gray')
            plt.title(f'HL{i+1}')
        plt.show()
    

在实际CTF比赛中,这类题目通常会与其他技术(如流量分析、逆向工程)结合出现。建议从Github上的CTF题库中寻找类似题目进行针对性训练,逐步建立完整的取证分析思维体系。

更多推荐