Author: vilkius
TJCTF 2026 - skeleton
author tmm
Description
I zipped up a picture of the flag, but I forgot the password. Luckily, I saved the zip2john hash. Can you recover the image?
Flag: tjctf{1ts_411_ab0ut_th3_keys}
Writeup
We are given a hash.txt:
flag.zip/flag.png:$pkzip2$1*1*2*0*12c*120*c8a6617a*0*26*0*12c*c8a6*81bd*36bee62e49e2b2c41f6260bdc2e5fdd8cabd38956eb51f1d8a48c8f6228fd7392a8c53f3199068e3017e11c65e32cd55ea33033ab8b2fb52c4f86373098af1732591290e5c99a2a74239243b67108f232def15a73aac1537e75a593abe81fb3a8b0338afeb00835c67f8a31896a5f73facd1f481fd5ebc8882b5b183819f9b71c89506b3ae7d17bc07ab187ece8413a88af072018ccdc8a2db425082cec0715fd5aa3b3c47bb4f5c93b397154eb2212ffd593d0e4e614d83dafba289710be2e538f4610e8cb53c025aa722bfe832ec4d6cbe33350c09b690c92560292893f72c7e9894a50efaaf9635d64c86b053053b861a00e1717d7b2b963782ea4fe407008153d2d0564e2cbe3792eaa0dacd611b9eaf9d3e7d5b54ab63ae9906b62c830ef4b873d954c25c22e8a221c9*$/pkzip2$:flag.png:flag.zip::flag.zip
The structure of the $pkzip2$ hash:
-
The parameters 12c and 120 represent the data lengths.
-
In legacy
ZipCrypto, a 12-byte encryption header is added to the data. 12 + 288 = 300 bytes total. -
Because the size matches perfectly, we can understand that the file (
flag.png) was stored without compression (Deflate was not used). -
Because the file is very small,
zip2johnembedded the entire encrypted payload inside the hash.
Known Plaintext Attack
-
Legacy
ZipCryptoencryption is mathematically broken and vulnerable to aKnown Plaintext Attackif we know at least 12 bytes of the unencrypted file. -
Since the target file is a
.pngand it is uncompressed, we know its 16-byte magic header:
89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52
- By supplying this known plaintext header to
bkcrack, we can get the internal encryption keys without ever needing to brute-force the actual password.
Step 1: Extract the Ciphertext
We extract the hex block from the hash string and convert it into a raw binary file (cipher.bin) using xxd:
echo -n "36bee62e49e2b2c41f6260bdc2e5fdd8cabd38956eb51f1d8a48c8f6228fd7392a8c53f3199068e3017e11c65e32cd55ea33033ab8b2fb52c4f86373098af1732591290e5c99a2a74239243b67108f232def15a73aac1537e75a593abe81fb3a8b0338afeb00835c67f8a31896a5f73facd1f481fd5ebc8882b5b183819f9b71c89506b3ae7d17bc07ab187ece8413a88af072018ccdc8a2db425082cec0715fd5aa3b3c47bb4f5c93b397154eb2212ffd593d0e4e614d83dafba289710be2e538f4610e8cb53c025aa722bfe832ec4d6cbe33350c09b690c92560292893f72c7e9894a50efaaf9635d64c86b053053b861a00e1717d7b2b963782ea4fe407008153d2d0564e2cbe3792eaa0dacd611b9eaf9d3e7d5b54ab63ae9906b62c830ef4b873d954c25c22e8a221c9" | xxd -r -p > cipher.binStep 2: Create the Plaintext Signature File
We write the known 16-byte PNG header into a binary file (plain.bin):
printf "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52" > plain.binStep 3: Crack Internal Keys via bkcrack
We run bkcrack to find the internal keys:
bkcrack -c cipher.bin -p plain.bin
Step 4: Decrypt the Ciphertext
Using the keys, we decrypt cipher.bin into decrypted.bin:
bkcrack -c cipher.bin -k c639d1ca b1fd3d6c 25bb9b08 -d decrypted.bin
Step 5: File Carving
Because bkcrack discovered the plaintext header matching at index 7, the valid PNG data starts exactly 7 bytes into the decrypted file. We strip the padding bytes using dd:
dd if=decrypted.bin of=flag.png bs=1 skip=7Opening flag.png reveals the valid visual text containing the flag.
