Challenge
题目提供
stego14.tar.gz,提示Get the image here。需要先看清 tar 内部结构,再从图片里挖出隐藏的密钥与密文,最后用古典密码还原口令。 A tar.gz is provided; the answer is recovered by unpacking it, carving a RAR archive out of the JPG, and decrypting a classical cipher with a key that is itself hidden inside the archive.
Solution
$ tar tzf stego14.tar.gz6578747261637400.jpg$ tar xzf stego14.tar.gz成员名 6578747261637400 是一串 ASCII 十六进制,解码后是 extract\0。这个名字在提示真正的载荷不在图片像素里,而在图片之后附加的数据里。
图片本体是一张 150x20 的基线 JPEG:
$ file 6578747261637400.jpgJPEG image data, JFIF standard 1.02, baseline, precision 8, 150x20, components 3JPEG 以 EOI(FF D9)结束,但文件真实长度超过了 EOI 的位置,后面还跟了一段数据。扫描标记来定位:
data = open("6578747261637400.jpg", "rb").read()eoi = data.rfind(b"\xff\xd9")print("file len :", len(data))print("EOI at :", eoi)print("trailing :", len(data) - eoi - 2, "bytes")print("signature:", data[eoi + 2:eoi + 9])输出显示 EOI 之后还有 5617 字节,且以 Rar! 开头,是一段 RAR v4 归档。把它切出来解开:
$ python3 -c "d=open('6578747261637400.jpg','rb').read(); i=d.rfind(b'\xff\xd9'); open('appended.rar','wb').write(d[i+2:])"$ file appended.rarappended.rar: RAR archive data, v4, os: Win32$ 7z l appended.rar Date Time Attr Size Compressed Name------------------- ----- ------------ ------------ ------------------------2009-01-23 01:02:14 ....A 12758 5546 key.jpg$ 7z x appended.rar归档里只有一个文件 key.jpg(48x13)。
key.jpg 是纯黑白位图,像素值只有黑(0)与白(255)两色,放大后是一行小字。直接按 ASCII 打印像素即可读:
from PIL import Image
im = Image.open("key.jpg").convert("L")w, h = im.sizefor y in range(h): print("".join("#" if im.getpixel((x, y)) < 128 else "." for x in range(w)))打印出来的是 *5+10。这正是仿射密码(Affine cipher)的密钥,含义为 ,也就是 。
6578747261637400.jpg 的像素对比度极低,灰度只落在 197–255 之间,肉眼看上去几乎全白。把灰度拉伸到完整范围后,里面藏着一行大写密文 PGNNZCFYXD:
from PIL import Imageimport numpy as np
a = np.asarray(Image.open("6578747261637400.jpg").convert("L")).astype(int)b = np.clip((255 - a) * 255.0 / (a.max() - a.min()), 0, 255).astype("uint8")Image.fromarray(b).resize((a.shape[1] * 8, a.shape[0] * 8)).save("cipher_zoom.png") 与 26 互质,模逆元为 21()。解密公式是 :
def affine_decrypt(cipher, a=5, b=10, alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZ"): a_inv = pow(a, -1, len(alphabet)) out = [] for ch in cipher: if ch.upper() in alphabet: x = alphabet.index(ch.upper()) out.append(alphabet[(a_inv * (x - b)) % len(alphabet)]) else: out.append(ch) return "".join(out)
def affine_encrypt(plain, a=5, b=10, alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZ"): out = [] for ch in plain: if ch.upper() in alphabet: x = alphabet.index(ch.upper()) out.append(alphabet[(a * x + b) % len(alphabet)]) else: out.append(ch) return "".join(out)
ct = "PGNNZCFYXD"pt = affine_decrypt(ct)print(pt)print(affine_encrypt(pt) == ct) # True:往返一致bulldozinj