MISSION 013            goo.gl/ZnH1tg               DIFFICULTY: ████████░░ [8/10]
┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅
Lately, our agents managed to capture a PNG file. Supposedly, there's a hidden
message in it, somewhere, but we didn't manage to retrieve it just yet.    
  
Maybe you'll have more luck. 
  
    goo.gl/6vu6cp  
      
Good luck!

Foreword on the delay.

This write-up arrives late. Very late. In fact, the mission was published… two months ago? Yeah, something like that. It’s solely my own fault for poor time management and few other things, like… laziness, which resulted in this little disaster. Anyway, moving on to the mission!

The mission!

In this mission, as the, well, description stated – we receive a PNG file, so… let’s take a look at it! It’s the first thing to do, right?

imgmuch doge, wow

Next up is something I tend to forget to do – use “file”

m13.png: PNG image data, 800 x 800, 8-bit/color RGB, non-interlaced

Okay, so… what to do if we don’t know what to do? Take a look at hex editor, perhaps something is hidden this way.

imgnah, nothing much

Nothing too interesting to see here. Other than your usual hex rubble and reaffirmation that it indeed is a png file, signature and initial bits.

Okay, so, huh, what do to next? Google for inspiration! This, for example, leads to somewhat interesting links like this. (since it’s not how the message was stored I’ll leave it as an interesting read and not delve into it). Now having googled for a bit and learnt a thing or two, read a couple of stackoverflow questions, it’s probably time to take a brief look at PNG specification, maybe there’s something.

Filtering looks interesting. The filer bit doesn’t have too much possible values(0 - 5) and is applied to every line. Our image is 800 x 800px, so there should be 800 bits. That being said, let’s see what our bits are, then!

import png, sys, zlib
from struct import unpack


f = png.Reader(sys.argv[1]).chunk(seek = "IDAT")
data = zlib.decompress(f[1])
size = len(data) // 800
bits = []


for i in range(0, len(data), size):
    filterbit = unpack(str(size) + 'c', data[i:i+size])[0]
    bits.append(filterbit)
        
print("Amount of bits: %d\nBit types: %s" % (len(bits), set(bits)))

which results in

Amount of bits: 800
Bit types: {b'\x00', b'\x01'}

…neat! We may be onto something! Let’s modify our previous script a little, as see how we do.

import png, sys, zlib
from struct import unpack


f = png.Reader(sys.argv[1]).chunk(seek = "IDAT")
data = zlib.decompress(f[1])
size = len(data) // 800
bits = ''

for i in range(0, len(data), size):
    filterbit = unpack(str(size) + 'c', data[i:i+size])[0]
    if filterbit == b'\x00':
        bits += '0'
    else:
        bits += '1'

#[::-1] cause it turns out our bits were reversed :)
ebits = [bits[i:i+8][::-1] for i in range(0, len(bits), 8)]
flag = ''
for x in ebits:
    if x != '0000000':
        flag += chr(int(x, 2))

print("Flag: [%s]" % flag)

and the winner, and the winner, and the winner is… (no, it’s not MC Hammer as we definitely not only touched, but tackled this!)

Flag: [Hmmm, czy to zadanie czasem juz nie bylo gdzies?]
which roughly translates to: "Hmm, wasn't this task somewhere already?"

and yes, in fact, this type of mission was used in CONFidence DS CTF / Teaser CTF
at confidence conference in 2015.