# CSAW Quals 2016 – brainfun

We are given a 512×512 image split into 16×16 squares of color, so we essentially have a 32×32 grid of colors. If we open up the image in GIMP or some other tool, we can see that the colors have different alpha values/transparency, so that’s probably important. Let’s look at these RGBA values with PIL:

```>>> from PIL import Image
>>> im = Image.open('brainfun.png')
>>> pixels = []
>>> for row in xrange(32):
...     pixel_row = []
...     for column in xrange(32):
...         pixel_row.append(im.getpixel((16*column,16*row)))
...     pixels.append(pixel_row)
...
```

pixels is now a 32×32 array containing the RGBA values, so let’s take a look at some of them:

```>>> pixels[0]
[(240, 32, 96, 105), (80, 0, 64, 45), (80, 224, 192, 43), (240, 128, 128, 114), (48, 128, 192, 43), (48, 160, 96, 43), (144, 224, 96, 45), (0, 160, 32, 114), (176, 128, 32, 45), (80, 160, 32, 43), (224, 128, 64, 109), (32, 32, 32, 43), (16, 128, 0, 97), (128, 224, 192, 43), (224, 224, 64, 110), (96, 224, 64, 43), (240, 96, 160, 105), (160, 96, 128, 43), (0, 64, 192, 98), (80, 64, 160, 45), (128, 64, 96, 43), (176, 224, 64, 45), (96, 32, 224, 46), (176, 0, 96, 43), (16, 192, 224, 109), (160, 128, 32, 43), (32, 64, 192, 43), (16, 224, 160, 110), (176, 224, 32, 45), (0, 160, 224, 109), (48, 96, 192, 43), (144, 96, 64, 45)]
```

Looking at the colors in the first row, we notice two things:

1. The RGB values are all multiples of 16, so when written in hex, the second hex digit is always 0.
2. Out of the A values, there seem to be a lot of 43s and 45s.

At this point, I got distracted by the first observation and tried a lot of things that didn’t work, such as concatenating all the hex RGB digits together in various orders (the description was “Scrambled Fun for Everyone!” which implied that something was out of order) and seeing if there was any readable text in the result. Finally I followed up on the second observation and decided to look at the frequency distribution of the A values:

```>>> freq = 256*[0]
>>> for row in pixels:
...     for (r,b,g,a) in row:
...         freq[a]+=1
...
>>> for i in range(len(freq)):
...     if freq[i]>0:
...         print(str(i)+" "+str(freq[i]))
...
10 1
43 430
45 305
46 25
97 29
98 30
105 28
109 30
110 28
111 30
114 28
119 30
121 30
```

As you can see, there are a ton of 43s and 45s, as well as a handful of larger values and a single 10. At this point, I went through some more detours, such as filtering the image to only display the pixels with any given A value, but eventually I realized that the larger values all looked like ASCII lowercase letters and tried converting all the alpha values to ASCII:

```>>> alpha_str = ''
>>> for row in pixels:
...     for (r,g,b,a) in row:
...         alpha_str+=chr(a)
...
>>> alpha_str
'i-+r++-r-+m+a+n+i+b-+-.+m++n-m+-+m+-a-+-iiw+yr++n+--b+---y-nn++-++n-+--+m-+b-.--+--++--+---++-bbm-++-o+w++++----.+o-+b-o++++m++----+-r-+w++nm+y+++-+--aib+++++++n.+-++n-+w--++r-wm+-r+-+a+++--m+++r+i--++b-++++-bia-my+---+++b+--+w-om--o-m-++rw++-+m++++-++b+o-++---+-+ym++w+++.+wy+i+rn+n++-++w+-n+.-++o-a--a.+ar++ba--+++++++-.iw+aa+-+-+-++r-++--+r+----br-+-+a-w+w+-.-+-++--+-+-+++----++++a-anm--++o+.-++++n-+-i--mw---y--+-n++++-++-++-++-oby-r+-+++m-++y----++y++-m+wy-mr-++.r--r-++++-++-++w-+-on---++oyn+++-+--++--r+++---+n-+++++-+-+b+om-+--by+-+-++-+r-oa++-+-.-+++ny-++y+++i+--++aiyy++aai-a+++--++b+-o+++-i++-yw+-o-b++++a-+y-+-y-++---+-m-+-++--.o-++-++--.--bw-r-+-++-++++\n+a-++rr+-a---ma+--++b-++---o--++--+-i++i-.wawo-.+r.-o+n+ib+no+++.-a-+o+++.----i-+o++--+r+-bm+++o+.w-+---++++y+o--++b+-++o+om+w++.-+-++-+-++-i--++ow-+n-.+n++i-+y++++++br++y+----b+.+w+-+ii--o+n+-+++r++o++++-+in++++-++yb--by+-++++bao+y--+-+ar-.-++-+++-i--++--+wmnib---+---+-+-w+++--++-+-y+w++++m+--yi++---+w-i-++++n++m+w-iay----+mm+a-+-.+++n--b+w+y+++++r+--++'
```

At this point I slapped my face and realized upon seeing all of the +-. symbols that the title brainfun was referencing Brainfuck. So now we need to figure out the correct order for these symbols, and hopefully we’ll make a Brainfuck program that prints the answer (and also understand what the lowercase letters mean).

The first thing to try is ordering by the RGB values since we haven’t used them yet. Let’s start by assuming that the R value is the most significant digit:

```>>> rgb = 4096*[0] # 16^3 because each of R, G, and B have 16 possible values
>>> for row in pixels:
...     for (r,g,b,a) in row:
...         index = 16*r+g+b/16
...         rbg[index] = chr(a) #I realize that there are better ways of sorting but this is what I did in the moment
...
>>> ''.join(rgb).replace('\x00','')
'owmybrainowmybrainowmybrainowmybrainowmybrainowmybrainowmybrainowmybrainowmybrainowmybrainowmybrainowmybrainowmybrainowmybrainowmyb++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.++++++.-----------.++++++.++++++++++++++++++++.----.--------------------------------------------------------------------.++++++++++++++++++++++++++++++++++++++++++++.+++++++++++++++++++.-------------------.+++++++++++++++++++++.------------.-----------------------------------------------------.++++++++++++++++++++++++++++++++++++++++++++.+++++++++.--------------------------------------------------------.++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..+++.++++++++.------------------------.++++++++++++++.----------------------------------------------------------.+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.+++++++++++++++.\nowmybrainowmybrainowmybrainowmybrainowmybrainowmybrainowmybrainowmybrainowmybrainowmybrainowmybrainowmybrainowmybrainowmybrainowmyba'
```

Cool! So the letters repeat ‘owmybrain’ and there is a Brainfuck program in the middle. Running this in an online interpreter printed the flag, ‘flag{w3_r_th3_h0llow_m3n}’.

Advertisements

## One thought on “CSAW Quals 2016 – brainfun”

1. […] [Forensics 150] brainfun […]

Like