For this challenge we are provided with a source code `one_time_beach.py`

and an encrypted file `cipher.enc`

. Also we have a hint that the encrypted file is a JPEG image.

Let’s look at the source code:

We see that the code operates on two files: `flag.bin`

and `beaches.txt`

. `flag.bin`

is obviously the encrypted file, `beaches.txt`

is used to initialize bunch of variables that are lately combined into the `key`

array. After that, the flag data from `flag.bin`

is XORed with the `key`

array and written to the `cipher.enc`

. Also first 20 bytes of `beaches.txt`

is written to the `cipher.enc`

, but I don’t see how that would help us in this challenge, so we will ignore that 20 bytes and assume that `cipher.enc`

is just an encrypted flag.

So basically this is yet another XOR challenge. How do we solve this? Well, the key contains 14 unique bytes, there is too much to brute force. Let’s use the knowledge of the file format. Let’s draft the solution source code:

We know that the encrypted file is a JPEG image. This means that it has to start with `ff d8`

and end with `ff d9`

according to the JPEG specification. This means that we can find first two bytes of the flag just XORing first two bytes of encrypted flag with `ff d8`

. So we XOR the first encrypted byte with `ff`

to get the value of `c`

and the second byte with `d8`

to get the value of `o`

Next, as we know that flag ends with `ff d9`

we can XOR last two bytes of encrypted file with these values to determine two more bytes of the key. What bytes exactly? To find that out we calculate

So last two bytes of the flag are encrypted with 21st and 22nd bytes of the key. These bytes correspond to variables `a`

and `_`

respectively:

To move further we need to use JPEG format specification. I used this document:

According to the document, after `ff d8`

(SOI) should go `ff e0`

(APP0), so we can use that to find variables `t`

and `h`

:

Now, the APP0 itself is not only `ff e0`

but also a few other fields

There are two bytes of length (skip those for now), and after those there is `JFIF`

string, which takes 5 bytes (`4a 46 49 46 00`

). Those bytes are 6th, 7th, 8th, 9th and 10th bytes of the image respectively. This reveals corresponding key bytes (values `_`

(we know that already), `s`

, `a`

(also know that), and `y`

)

After `JFIF`

comes major and minor versions, but we don’t care, because those values corresponds to variables `_`

and `y`

and we already know them, so move on. We also skip `units`

field, because we already know the value of `o`

.

Next goes field `x-density`

which is 2 bytes long and the document tells us that this is normally `00 01`

. Could be another value, but let’s assume that this should be `00 01`

. This field is encrypted with bytes `u`

and `_`

. We already know `_`

so we just calculate `u`

After `x-density`

goes `y-density`

, which is also normally `00 01`

. This field is encrypted with `a`

and `r`

so again we only calculate value `r`

.

Next goes `width`

and `height`

of the thumbnail. We could say that we don’t know these values, but look at our current decrypted file (width and height fields are highlighted with red rectangle):

We see that the thumbnail `height`

is `00`

and we know for sure it is because this byte is decrypted correctly (it is encrypted with `_`

). And if the `height`

is `00`

we may assume that the `width`

is also `00`

. We use that to calculate value `e`

The APP0 segment is finished, but we should take a step back and look at the field `length`

again (bytes 4 and 5). We know 4th byte, but the 5th byte is encrypted with `n`

and we don’t know that value yet. What we know is the length of APP0 segment now, which is 16 bytes (`10`

in hex)

We use that to calculate value `n`

Next image segment is `ff db`

(DQT)

We already know the `marker identifier`

field, but `length`

field is encrypted with yet unknown to us values `b`

and `i`

. To calculate these values we search for the next marker. This marker is `ff db`

again and it is on the offset `59`

.

This yields the length of the current `ff db`

segment: `59 — 16 = 43`

. We use that to calculate values `b`

and `i`

Running that code gives us the flag