Skip to content

Instantly share code, notes, and snippets.

@raphaelm
Last active August 29, 2015 14:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save raphaelm/6e89495f5042e7e297f5 to your computer and use it in GitHub Desktop.
Save raphaelm/6e89495f5042e7e297f5 to your computer and use it in GitHub Desktop.
GPN14 CTF WriteUp
#!/usr/bin/env python
# Author: Nils Martin Klünder
# License: Apache License
# Do no evil
from PIL import Image
import traceback
import json
import shutil
import time
def solve(debug=False, train=False):
debug = debug or train
try:
varcount = 0
bgs = []
if debug: print('Lade Captcha')
img = Image.open('tmp.png')
imgpx = img.load()
if debug: print('Hintergrund herausfinden')
bgcount = int(open('bgcount').read())
for i in range(bgcount):
bgi = i
bg = Image.open('%02d_filtered.png' % i)
bgpx = bg.load()
samecount = 0
for y in range(50):
for x in range(175):
if imgpx[x,y] == bgpx[x,y]:
samecount += 1
if samecount > 50*175/2:
break
if debug: print('Text extrahieren')
textimg = Image.new('L', (175,50))
textimgpx = textimg.load()
for y in range(50):
for x in range(175):
textimgpx[x,y] = 255 if imgpx[x,y] == bgpx[x,y] else 0
if debug: print('Buchstaben sammeln')
buchstaben = []
newbuchstabe = None
for x in range(175):
leer = True
for y in range(50):
if textimgpx[x,y] == 0:
leer = False
break
if leer and newbuchstabe is not None:
buchstaben.append((newbuchstabe, x))
newbuchstabe = None
elif not leer and newbuchstabe is None:
newbuchstabe = x
if newbuchstabe is not None:
buchstaben.append((newbuchstabe, 151))
if debug: print(buchstaben)
if debug: print('Buchstaben ausschneiden')
buchstabenalt = buchstaben
buchstaben = []
for buchstabe in buchstabenalt:
top = None
bottom = None
for y in range(50):
leer = True
for x in range(buchstabe[0], buchstabe[1]):
if textimgpx[x,y] == 0:
leer = False
break
if leer and top is not None:
bottom = y
break
elif not leer and top is None:
top = y
if top is None: top = 0
if bottom is None: bottom = 51
if debug: print((buchstabe[0], top, buchstabe[1], bottom))
buchstaben.append(textimg.crop((buchstabe[0], top, buchstabe[1], bottom)))
if debug: print('Auf gehts OCR… mit händischer hilfe')
try:
ocr = json.load(open('ocr%d' % bgi))
except:
ocr = {}
loesung = []
for buchstabe in buchstaben:
#buchstabe.show()
px = buchstabe.load()
randpixel = 0
flaechepixel = 0
lochpixel = 0
for y in range(buchstabe.size[1]):
hadtoner = False
for x in range(buchstabe.size[0]):
p = (px[x,y] == 0)
if p:
flaechepixel += 1
hadtoner = True
if p:
isrand = False
for dy in (-1, 0, 1):
for dx in (-1, 0, 1):
try:
if px[x+dx,y+dy] == 255:
isrand = True
break
except:
pass
if isrand:
break
if isrand:
randpixel += 1
if debug: print('#' if px[x,y] > 0 else ' ', end='')
if debug: print('')
# Lochpixel
# randpixel rot
for y in range(buchstabe.size[1]):
if px[0,y] == 255: px[0,y] = 200
if px[buchstabe.size[0]-1,y] == 255: px[buchstabe.size[0]-1,y] = 200
for x in range(buchstabe.size[0]):
if px[x,0] == 255: px[x,0] = 200
if px[x,buchstabe.size[1]-1] == 255: px[x,buchstabe.size[1]-1] = 200
for i in range(int(max(buchstabe.size)/2-1)):
for y in range(buchstabe.size[1]):
for x in range(buchstabe.size[0]):
for dy in (-1, 0, 1):
for dx in (-1, 0, 1):
try:
if px[x,y] == 255 and px[x+dx,y+dy] == 200:
px[x,y] = 200
break
except:
pass
lochpixel = 0
for y in range(buchstabe.size[1]):
for x in range(buchstabe.size[0]):
if px[x,y] == 255:
lochpixel += 1
mine = (flaechepixel, randpixel, lochpixel)
if debug: print(mine)
minscore = 20000
minval = None
for knownn, val in ocr.items():
if val == '': continue
known = [int(i) for i in knownn.split('-')]
diff = (mine[0]-known[0], mine[1]-known[1], mine[2]-known[2])
score = pow(sum([i*i for i in diff]), 0.5)
if score < minscore:
minscore = score
minval = val
if debug: print(minscore)
if minscore < 1500:
loesung.append(minval)
else:
if not train:
return None
if debug: print('')
bla = ''
if debug: print('known: %s', ''.join(sorted(ocr.values())))
if debug: print('one letter or „empty“')
while (len(bla) != 1 or bla not in 'QWERTZUIOPASDFGHJKLYXCVBNM1234567890') and bla != 'empty':
bla = input('>')
if bla == 'empty':
bla = ''
ocr['-'.join([str(i) for i in mine])] = bla
loesung.append(bla)
json.dump(ocr, open('ocr%d' % bgi, 'w'))
ll = ''.join(loesung)
shutil.copyfile('tmp.png', 'tries/' + ll + '_' + str(time.time()) + '.png')
return ll
except:
traceback.print_exc()
return None
#!/usr/bin/env python
# Author: Nils Martin Klünder
# License: Apache License
# Do no evil
from PIL import Image
import urllib.request
url = 'http://ctf.gpn.entropia.de:50234/download.php?q=7bf63ebe6646c009'
varcount = 0
bgs = []
print('Schritt 1: So lange Captchas laden (mindestens 100) bis wir jede Variation 50 mal haben')
total = 0
while True:
urllib.request.urlretrieve('http://ctf.gpn.entropia.de:50234/image.php', 'tmp.png')
img = Image.open('tmp.png')
for bg in bgs:
px = img.load()
comp = bg[0]
comppx = comp.load()
samecount = 0
for y in range(50):
for x in range(175):
if px[x,y] == comppx[x,y]:
samecount += 1
if samecount > 50*175/2:
bg.append(img)
break
else:
bgs.append([img])
img.save('%02d.png' % varcount)
varcount += 1
open('bgcount', 'w').write(str(varcount))
total += 1
if total%10 == 0:
print(total)
if total > 100:
bla = [len(bg) for bg in bgs]
if total%10 == 0:
print(tuple(bla))
if min(bla) >= 50: break
print('Schritt 2: Jetzt die Hintergründe extrahieren, dann glücklich sein.')
i = 0
for bg in bgs:
print('Hintergrund %d/%d' % (i, len(bgs)))
loaded = [img.load() for img in bg[:50]]
print('Loaded')
result = Image.new('RGB', (175,50))
loaded_result = result.load()
for y in range(50):
print(y)
for x in range(175):
tmp = [px[x,y] for px in loaded]
loaded_result[x,y] = max(set(tmp), key=tmp.count)
result.save('%02d_filtered.png' % i)
i += 1
open('bgcount', 'w').write(str(len(bgs)))
#!/usr/bin/env python
# Author: Raphael Michel
# License: Apache License
# Do no evil
import requests
import re
import time
import captchasolver
import os.path
s = requests.Session()
# Load list
listfile = open('list.txt')
ids = listfile.read().split('\n')
for i in ids:
while True:
# Load detail page
r = s.get('http://ctf.gpn.entropia.de:50234/download.php?q=' + i)
html = r.content.decode('utf-8')
mg = re.search('flag.part[0-9]{3}.rar', html)
print(mg.group(0))
if os.path.exists(mg.group(0)):
# Skip downloaded
print("SKIP")
break
# Load CAPTCHA
rc = s.get('http://ctf.gpn.entropia.de:50234/image.php', stream=True)
if rc.status_code == 200:
with open('tmp.png', 'wb') as f:
for chunk in rc.iter_content(1024):
f.write(chunk)
# Solve CAPTCHA
print('Enter CAPTCHA: ', end="")
captcha = captchasolver.solve()
if captcha is None:
continue
else:
print(captcha)
r = s.post('http://ctf.gpn.entropia.de:50234/download.php', data={
'x': 1,
'y': 1,
'captcha': captcha,
'q': i
})
if 'html' in r.headers['Content-Type']:
html = r.content.decode('utf-8')
if 'expired' in html:
print("Captcha expired")
elif 'Wrong' in html:
print("Wrong!")
continue
elif 'octet-stream' in r.headers['Content-Type']:
with open(r.headers['Content-Disposition'].split('filename=')[1], 'wb') as f:
for chunk in r.iter_content(1024):
f.write(chunk)
print('YESS!')
break
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment