This is my write-up of a Cryptography challenge My Magic Numbers on the CTF site 247CTF.com.
Can you recover the secret XOR key we used to encrypt the flag?
You are provided with an encrypted file, which the challenge states has been XOR’ed. The file has the extension of .jpg.enc, which implies that the unencrypted version is a JPG image. Since JPG images have known file headers (magic bytes), you can perform a known plaintext attack against the encrypted file to recover the key. Once you have recovered the key, you can unencrypt the file using the properties of XOR to gain access to the flag.
Magic numbers or magic bytes are usually used to tell the operating system what kind of a file it is dealing with.
A JPG file typically has magic number:
FF D8 DD E0 FF D8 FF DB FF D8 FF E1 FF D8 FF E0 00 10 4A 46 49 46 00 01 FF D8 FF E0
A XOR B = C C XOR B = A A XOR C = B
So file XOR jpg magic bytes = key
FF D8 FF E0 00 10 4A 46 49 46 00 01 | jpg magic bytes | b9 14 06 45 71 e0 b5 f7 37 07 cb 85 | encoded file | XOR 46 cc f9 a5 71 f0 ff b1 7e 41 cb 84 | key | =
Dump the file to hex string file with
xxd (xxd is a part of vim text editor package):
$ xxd -p my_magic_bytes.jpg.enc > hex
xxd has 16 octet limit per line by default in the output, so newlines will be present. There is -c parameter to control the number of octets per line, but max value is 256, so this would not help in our case.
So to remove the newlines:
$ tr -d '\n' < hex > hexoneline
Use the script to xor the hex with key (I have used it to guess the keys also, the commented out message var):
from itertools import cycle def do_xor(key, message): message = message.replace(' ', '').decode('hex') key = ''.join(key.split()[::-1]).decode('hex') return ''.join([chr(ord(a) ^ ord(b)) for a,b in zip(message, cycle(key))]) with open('hexoneline', 'rb') as f: message = f.read() f.close() #message = "FFD8FFE000104A4649460001" key = "46ccf9a571f0ffb17e41cb84" print do_xor(key,message).encode("hex") #http://opentechnotes.blogspot.com/2014/08/xor-string-with-key-in-python.html
$python2.7 test.py > decodedhex
Revert hex back to binary
$ xxd -r -p decoded image.jpg
image.jpg is really an image:
$ file image.jpg image.jpg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, progressive, precision 8, 500x500, frames 3
Open it with your favourite image viewer and catch the flag.
$ open image.jpg
Special thanks goes to @Razvieu for kind support when I got stuck.