A crypto challenge! We’re given a zip-file, containing flag.jpg.enc
and rotate.py
.
rotate.py
handles the encryption, but only does so one way:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
The script takes two bytes at a time and transforms them into two floats via some goniometric functions, using a key. These floats are then dumped as DWORDS into the encrypted output file. Our first job is to determine what key was used.
Assuming the flag really is a JPEG, we have some prior knowledge. Namely, the first two bytes are 0xff,0xd8
. I made a small file that contains only these two bytes and I ripped the first 8 bytes from flag.jpg.enc
. I encoded the small 2-byte file with all possible keys and then compared the md5sum of each file to the md5sum of the 8-byte encoded file:
1 2 3 4 5 |
|
So I now know the key that was used was 123
. I then started building a decoder. I hit a few snags on the way. It turns out that rotate.py
doesn’t use the literal value of each byte, but instead uses struct.unpack('b', byte)
to generate a signed representation. That threw me off for a good while.
The decoder first builds a lookup table of each possible two-byte combination. Then, to decode, it reads eight bytes at a time and looks up the corresponding values from the lookup table. If there are no more floats to be read, it catches the error and outputs the decrypted data:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
|
It’s horrible code, but it got the job done:
The flag was ADCTF_TR0t4T3_f4C3
.