コース目次 / 第1章
ファイルの終わりの、その先 — carving
PNGファイルの末尾にzipを追記して隠します。fileコマンドは気づかないこと、binwalkが埋め込まれたzip署名を検出できることを確認し、バイト列を切り出して復元します。
隠しファイルを、作る
make_carving.py として保存します。PNG画像を1つ手作りして(Pillow等の画像ライブラリは使わず、PNG形式の仕様どおりにチャンクを組み立てます)、その末尾にzipアーカイブを追記します。
import struct, zlib, subprocess
def chunk(tag, data):
return struct.pack('>I', len(data)) + tag + data + struct.pack('>I', zlib.crc32(tag + data))
width, height = 8, 8
sig = b'\x89PNG\r\n\x1a\n'
ihdr = chunk(b'IHDR', struct.pack('>IIBBBBB', width, height, 8, 2, 0, 0, 0))
row = b'\x00' + bytes([120, 180, 220] * width) # filter byte + RGB pixels
idat = chunk(b'IDAT', zlib.compress(row * height))
iend = chunk(b'IEND', b'')
png = sig + ihdr + idat + iend
with open('photo.png', 'wb') as f:
f.write(png)
with open('flag.txt', 'w') as f:
f.write('flag{carved_from_the_png_tail}\n')
subprocess.run(['zip', 'hidden.zip', 'flag.txt'], check=True)
hidden_zip = open('hidden.zip', 'rb').read()
with open('combo.png', 'wb') as f:
f.write(png + hidden_zip)
print('combo.png size:', len(png) + len(hidden_zip))
python3 make_carving.py
combo.png が、今回のチャレンジファイルです。これ以降、あなたは中身(flagの値)を知らないふりをして進めてください。
fileコマンドで、見てみる
file combo.png
combo.png: PNG image data, 8 x 8, 8-bit/color RGB, non-interlaced
fileは「先頭のマジックバイトからPNGだと判定した」だけで、ファイル全体を見ているわけではありません。PNGはIENDチャンクで論理的に終わりますが、OS上のファイルとしては、その後ろにいくらでもバイト列を続けられます。画像ビューアはIENDより後を無視するので、見た目には何も分かりません。
binwalkで、中を掘る
binwalk combo.png
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 PNG image, 8 x 8, 8-bit/color RGB, non-interlaced
41 0x29 Zlib compressed data, default compression
74 0x4A Zip archive data, at least v1.0 to extract, compressed size: 31, uncompressed size: 31, name: flag.txt
249 0xF9 End of Zip archive, footer length: 22
binwalkは、ファイル全体をスキャンして、既知のファイル形式の「署名(マジックバイト)」を探します。オフセット74に、zipアーカイブの署名(PK\x03\x04)が見つかりました——これがPNGのIENDより後ろに追記した部分です。
切り出して、復元する
binwalkが報告したオフセットを使って、Pythonでzip部分を切り出します。
data = open('combo.png', 'rb').read()
idx = data.find(b'PK\x03\x04')
print('zip署名の位置:', idx)
with open('carved.zip', 'wb') as f:
f.write(data[idx:])
python3 carve.py
unzip -p carved.zip flag.txt
zip署名の位置: 74
flag{carved_from_the_png_tail}
復元できました。 binwalkで「何が・どこに」埋め込まれているかを見つけ、Pythonでその位置からのバイト列を切り出す——これがcarvingの基本の型です。
こうなっていればOK
卒業まであと3章です。
この章はまだ完了していません。
保存できませんでした(プライベートブラウズ中かもしれません)。この端末に進捗は残りませんが、先へは進めます。