Bugku CTF - 隱寫 writeup
題目信息
- 題目類型:Misc / Steganography
- 文件:2.png
- 題目連結: https://ctf.bugku.com/challenges/detail/id/3.html
題目描述
給定一張 PNG 圖片 2.png,需要找出隱藏的 flag。
解題過程
Step 1: 初步分析 - 元數據檢查
首先使用 exiftool 查看圖片的元數據:
$ exiftool 2.png
ExifTool Version Number : 13.25
File Name : 2.png
Directory : .
File Size : 18 kB
File Modification Date/Time : 2017:06:07 22:26:44+08:00
Image Width : 500
Image Height : 420
Bit Depth : 8
Color Type : RGB with Alpha
...
觀察結果:
- 圖片尺寸:500 × 420
- 沒有明顯可疑的元數據
- 需要進一步深入分析
Step 2: PNG 結構檢查 - 發現突破口!
使用 pngcheck 檢查 PNG 文件結構:
$ pngcheck -v 2.png
File: 2.png (17675 bytes)
chunk IHDR at offset 0x0000c, length 13
500 x 420 image, 32-bit RGB+alpha, non-interlaced
CRC error in chunk IHDR (computed c758d77d, expected cbd6df8a)
ERRORS DETECTED in 2.png
關鍵發現:
- ❌ IHDR chunk 的 CRC 校驗失敗
- 計算出的 CRC:
c758d77d - 預期的 CRC:
cbd6df8a - 這說明 IHDR 數據被人為修改過!
Step 3: 十六進制分析
使用 xxd 或 hexdump 查看文件的十六進制內容:
$ xxd 2.png | head -20
00000000 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 |.PNG........IHDR|
00000010 00 00 01 f4 00 00 01 a4 08 06 00 00 00 cb d6 df |................|
00000020 8a 00 00 00 09 70 48 59 73 00 00 12 74 00 00 12 |.....pHYs...t...|
IHDR 結構解析:
偏移 內容 說明
----------------------------------------
0x00 89 50 4e 47 PNG 文件簽名
0d 0a 1a 0a
0x08 00 00 00 0d IHDR chunk 長度 (13 字節)
0x0C 49 48 44 52 "IHDR" 標識
0x10 00 00 01 f4 寬度 = 0x01f4 = 500
0x14 00 00 01 a4 高度 = 0x01a4 = 420 ⚠️
0x18 08 位深度 = 8
0x19 06 顏色類型 = RGBA
0x1A 00 壓縮方法
0x1B 00 過濾方法
0x1C 00 交錯方法
0x1D cb d6 df 8a CRC32 校驗值
分析:
- 當前聲明的尺寸:500 × 420
- CRC 值
cbd6df8a是正確的(文件中存儲的) - 但根據當前 IHDR 數據計算出的 CRC 是
c758d77d - 結論:真實的圖片尺寸不是 500×420!
Step 4: 爆破真實尺寸
既然 CRC 不匹配,說明真實的寬高與聲明的不同。我們需要找出正確的尺寸。
編寫 Python 腳本進行爆破:
#!/usr/bin/env python3
import struct
import zlib
# 目標 CRC(從文件中讀取的)
target_crc = 0xcbd6df8a
print("開始爆破 PNG 尺寸...")
print(f"目標 CRC: {hex(target_crc)}\n")
# 爆破寬度和高度
for width in range(1, 3000):
for height in range(1, 3000):
# 構造 IHDR 數據塊
# 格式: 'IHDR' + 寬(4) + 高(4) + 位深度(1) + 顏色類型(1) + 壓縮(1) + 過濾(1) + 交錯(1)
ihdr_data = b'IHDR' + struct.pack('>II5B', width, height, 8, 6, 0, 0, 0)
# 計算 CRC32
calculated_crc = zlib.crc32(ihdr_data) & 0xffffffff
if calculated_crc == target_crc:
print(f"✅ 找到了!")
print(f"正確的寬度: {width} (0x{width:04x})")
print(f"正確的高度: {height} (0x{height:04x})")
print(f"CRC 校驗: {hex(calculated_crc)}")
exit(0)
if width % 100 == 0:
print(f"正在搜索... 當前寬度: {width}")
print("❌ 在搜索範圍內未找到匹配的尺寸")
運行結果:
$ python3 png_crc_brute.py
開始爆破 PNG 尺寸...
目標 CRC: 0xcbd6df8a
正在搜索... 當前寬度: 100
正在搜索... 當前寬度: 200
正在搜索... 當前寬度: 300
正在搜索... 當前寬度: 400
✅ 找到了!
正確的寬度: 500 (0x01f4)
正確的高度: 500 (0x01f4)
CRC 校驗: 0xcbd6df8a
重大發現:
- 真實尺寸:500 × 500
- 當前尺寸:500 × 420
- 底部 80 像素被隱藏了!
Step 5: 修復圖片
現在我們知道了真實尺寸,需要修復圖片來查看完整內容。
方法 1:使用 Python 腳本
#!/usr/bin/env python3
# 讀取原始文件
with open('2.png', 'rb') as f:
data = bytearray(f.read())
# 修改高度: 位置 0x14-0x17
# 從 0x000001a4 (420) 改成 0x000001f4 (500)
data[0x14] = 0x00
data[0x15] = 0x00
data[0x16] = 0x01
data[0x17] = 0xf4
# 保存修復後的文件
with open('2_fixed.png', 'wb') as f:
f.write(data)
print("✅ 修復完成!文件已保存為 2_fixed.png")
Step 6: 查看完整圖片
打開修復後的 2_fixed.png,可以看到完整的 500×500 圖片。
在底部隱藏的 80 像素區域中,我們找到了 flag!
知識點總結
PNG IHDR CRC 隱寫原理
- IHDR Chunk:
- 包含圖片的基本信息(寬、高、顏色類型等)
- 每個 chunk 都有 CRC32 校驗碼
- CRC 計算範圍:chunk 類型 + chunk 數據
- 隱寫技巧:
- 修改 IHDR 中聲明的寬度或高度
- 圖片查看器只顯示聲明的區域
- 實際圖片數據仍然完整存在
- CRC 校驗值暴露了真實尺寸
PNG 文件結構:
PNG = [簽名] + [IHDR] + [其他 chunks] + [圖片數據] + [IEND]
CRC32 校驗
CRC(Cyclic Redundancy Check)是一種錯誤檢測碼:
- 對數據進行特定算法計算得到校驗值
- 用於檢測數據傳輸或存儲中的錯誤
- 在 PNG 中,每個 chunk 都有獨立的 CRC 校驗
Member discussion