Title: brainfun
Competition: CSAW quals 2016
Category: Forensics
Points: 150
Description: Scrambled Fun for Everyone! Author: fang0654 <brainfun.png>
- There's nothing hidden in the file other than the pixel data.
- The image is 512x512, but can be scaled down 32x32 to match the blocks to pixels.
- The RGB values are all multiples of 0x10. An example pixel could be
(0x40, 0xf0, 0x20)
. - There is also transparency.
- There aren't that many different alpha values. Many occur around the same number of times (28-30).
- The alpha values are in the printable ASCII range.
- The values that occur around the same number of times are lowercase letters,
and the others are
+
,-
,.
, and a single newline. - The name of the challenge is brainfun, which sounds kind of like brainfuck, which is an esoteric programming language which uses those symbols.
- The challenge description says that it's scrambled.
- Pige0n noticed that the letters can be rearranged to spell "my rainbow", which suggests that there is a definite order to the pixels.
- There are probably sections of lowercase letters and sections of brainfuck symbols.
- Brainfuck symbols had red values around the middle, and lowercase letters had low and high values.
- Sort the pixel values by RGB value, using the key
red<<8 + green<<4 + blue
. Edit (thanks BookGin):- It should be
red<<16 + green<<8 + blue
to sort by full values, but I abused the fact that all the RGB values take the format of0xN0
, so a pixel(0x10, 0x20, 0x30)
will get converted to0x1230
this way. - I probably should have used
struct.pack("hhh", red, green, blue)
- It should be
- It turns out that they spell "owmybrain", but whatever.
- Running that as brainfuck spits out the flag:
flag{w3_r_th3_h0llow_m3n}
- Here's a one-liner (it's not very efficient):
python3 -c '__import__("pybrainfuck").BrainFck().run("".join([chr(p[3]) for p in sorted([list(__import__("PIL", globals(), locals(), ["Image"]).Image.open("brainfun.png").getdata())[r*512 + c] for r in range(0, 512, 16) for c in range(0, 512, 16)], key=lambda p: (p[0]<<8) + (p[1]<<4) + p[2])]))'
@C0deH4cker I didn't do that because that would also sort by alpha value, which isn't what I want. In this case it would have the same result, but semantically it isn't as clear. If the challenge had had multiple pixels with the same RGB but different alpha, the pixels would keep the ordering they had in the image, since the Python sort I used is stable, while sorting with the whole tuple would sort by the alpha value too.
tl;dr yeah that would work here