VolgaCTF 2015 captcha

@mrexcessive WHA
& @DeathsPirate WHA

The problem

We've got a rather strange png file.
Very strange png.
Something isn't right about it...

The solution

So foremost reveals tehre are 1892 PNG files in here

Pull them all out... they each have a single character.

DeathsPirate mentions in teamchat that he's found that if you take the images one by one in sequence number order, you get characters for base64 which decodes to a PNG file header...

   (decimal)              137  80  78  71  13  10  26  10
   (hexadecimal)           89  50  4e  47  0d  0a  1a  0a
   (ASCII C notation)    \211   P   N   G  \r  \n \032 \n

We're all working on something else then...

But at about 3am.. sigh...

I think, so maybe... are the same characters represented by identical files - so I could hash the files.. store the hashes and then only have to show a human the things to recognise once per character..

Yes !

OK ... I can code this before bedtime... (author's note, during CTFs bedtime is approximately equal to point of maximum coffee dosage, when the production of melatonin has caught up with the absorption of caffeine and any further coffee will not help.

So... some python later.

# try.py for VolgaCTF / captcha
# categorise lots of png files, only ask for new hashes
# @mrexcessive @WHA - solving algos and python

import os, sys, code
import readline, rlcompleter
import socket,time
import random
import re
import itertools
import subprocess
import operator
import hashlib
from PIL import Image

pngfname = "output/png/0000%04i.png"
outputfile = "myoutput.png"
progressfile = "hashes.txt"
filehashes = {}

debug = True
flagGoInteractive = True        # go interactive after running stuff
alphanums = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
printables =   alphanums + ".,<>?/!$%^&*()_-+=@'#][{}`#"

def GetLoadsOfRandomForSeed(seed):
   p = subprocess.Popen(['./random', '%i' % seed, '%i' % count], stdout=subprocess.PIPE, stderr = subprocess.PIPE)
   out,err = p.communicate()
   randomstrings = out.split()

def Log(s,alwaysLog = False):
   if logfname <> None:
      f.write("%s\n" % s)
   if debug:
      print s

def SerialiseProgress():
   global filehashes
   ser = ""
   for k in filehashes.keys():
      ser += "%s=%02x\n" % (k,ord(filehashes[k]))

def RestartProgress():
   global filehashes
   filehashes = {}
   if os.path.isfile(progressfile):
      ser = ""
      data = f.read()
      h = data.split("\n")
      for d in h:
         if "=" in d:
            (key,hexvalue) = d.split("=")
            filehashes[key] = hexvalue.decode("hex")

def WriteOutput(fname,data):
   f = open(fname,"wb")

if __name__ == "__main__":
   vars = globals()
   readline.parse_and_bind("tab: complete")
   shell = code.InteractiveConsole(vars)


   output = ""
   for i in xrange(0,3169):      # file numbers
      fname = pngfname % i
      if os.path.isfile(fname):
         f = open(fname,"rb")
         data = f.read()
         m = hashlib.md5()
         dig = m.hexdigest()
         if filehashes.has_key(dig):
            output += filehashes[dig]
            im = Image.open(fname)
            sys.stdout.write("\nImage Char :")
            d = raw_input()
            ch = d[0]
            filehashes[dig] = ch
            output += ch
   print output

   # go interactive   
   if flagGoInteractive:

Run the program... look at about 60 images... type in a single letter.
Make no massive mistakes. Good job I made the program restartable!

Get Capital I and lowercase l mixed up... Fix that by deleting rows from the hashfile and running again - get re-asked just those two.

Now we have a base64 string:


OK... so decode that,

Python 2.7.3 (default, Mar 13 2014, 11:03:55) 
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import base64
>>> f=open("op.txt","rb")
>>> d=f.read()
>>> f.close()
>>> pic=base64.b64decode(d)
>>> f=open("flag.png","wb")
>>> f.write(pic)
>>> f.close()
>>> # to the gimp-mobile...
... # it's a thing...

look at the image.. and ... FLAGG !!!