Version
By
Version
Comment
noraj
1.0
Creation
CTF
Name : European Cyber Week CTF Quals 2016
Website : challenge-ecw.fr
Type : Online
Format : Jeopardy - Student
Description
N.A.
Solution
For this challenge we needed to solve a captcha and a QR code as faster as possible.
For this I wrote a ruby script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 #!/usr/bin/env ruby require 'base64' require 'zxing' require 'curb' hostname = 'https://challenge-ecw.fr/chals/divers200' c = Curl::Easy .new(hostname) do |curl | curl.headers['Cookie' ] = 'session=mySessionCookie' curl.headers['Referer' ] = hostname curl.headers['Host' ] = 'challenge-ecw.fr' curl.headers['Connection' ] = 'keep-alive' curl.headers['Upgrade-Insecure-Requests' ] = '1' end c.perform qrcode_b64 = c.body_str.match(/QRCode" src="data:image\/png;base64,(.*)" height/ ).captures[0 ] qrcode_file = 'qrcode.png' captcha_b64 = c.body_str.match(/Captcha" src="data:image\/png;base64,(.*)" height/ ).captures[0 ] captcha_file = 'captcha.png' File .open(qrcode_file, 'wb' ) do |f | f.write(Base64 .decode64(qrcode_b64)) end File .open(captcha_file, 'wb' ) do |f | f.write(Base64 .decode64(captcha_b64)) end qrcode_answer = ZXing .decode qrcode_file puts 'QRCode : ' .concat(qrcode_answer) captcha_answer = `python2 myCaptchaSolver.py` .chomp puts 'Captcha : ' .concat(captcha_answer) nonce = 'myNonce' c.http_post(Curl : :PostField .content('captcha' , captcha_answer), Curl : :PostField .content('qrcode' , qrcode_answer), Curl : :PostField .content('nonce' , nonce)) c.perform puts c.body_str.match(/<center>(.*)<\/center>/ ).captures[0 ] qrcode_b64 = c.body_str.match(/QRCode Win" src="data:image\/png;base64,(.*)" height/ ).captures[0 ] qrcode_file = 'qrcode.png' captcha_b64 = c.body_str.match(/Captcha Win" src="data:image\/png;base64,(.*)" height/ ).captures[0 ] captcha_file = 'captcha.png' File .open(qrcode_file, 'wb' ) do |f | f.write(Base64 .decode64(qrcode_b64)) end File .open(captcha_file, 'wb' ) do |f | f.write(Base64 .decode64(captcha_b64)) end qrcode_answer = ZXing .decode qrcode_file puts 'QRCode : ' .concat(qrcode_answer) captcha_answer = `python2 myCaptchaSolver.py` .chomp puts 'Captcha : ' .concat(captcha_answer) c.http_post(Curl : :PostField .content('captcha' , captcha_answer), Curl : :PostField .content('qrcode' , qrcode_answer), Curl : :PostField .content('nonce' , nonce)) c.perform puts c.body_str.match(/<center>(.*)<\/center>/ ).captures[0 ] puts "Flag : " + captcha_answer + qrcode_answer sleep(0.5 ) File .delete(qrcode_file)File .delete(captcha_file)File .delete("output.gif" )
Script is working but curb
is not very quick and the ZXing
ruby gem is just a port of the java version of ZXing
with Jruby and is amazingly slow and buggy! So the whole script process take 9 seconds. That's too slow!
Let's try to optimize the execution speed with more quicker tools:
replace curb
gem using libcurb directly with curl
replace ZXing
with a custom script using PIl
and pytesseract
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #!/usr/bin/env ruby require 'base64' hostname = 'https://challenge-ecw.fr/chals/divers200' cookies = 'session=mySessionCookie' body_str = `curl https://challenge-ecw.fr/chals/divers200 --cookie '#{cookies} '` qrcode_b64 = body_str.match(/QRCode" src="data:image\/png;base64,(.*)" height/ ).captures[0 ] qrcode_file = 'qrcode.png' captcha_b64 = body_str.match(/Captcha" src="data:image\/png;base64,(.*)" height/ ).captures[0 ] captcha_file = 'captcha.png' File .open(qrcode_file, 'wb' ) do |f | f.write(Base64 .decode64(qrcode_b64)) end File .open(captcha_file, 'wb' ) do |f | f.write(Base64 .decode64(captcha_b64)) end qrcode_answer = `python2 myQRCodeSolver.py` .chomp puts 'QRCode : ' .concat(qrcode_answer) captcha_answer = `python2 myCaptchaSolver.py` .chomp puts 'Captcha : ' .concat(captcha_answer) nonce = 'myNonce' server_answer = `curl https://challenge-ecw.fr/chals/divers200 --cookie '#{cookies} ' -X POST -F "captcha=#{captcha_answer} " -F "qrcode=#{qrcode_answer} " -F "nonce=#{nonce} "` puts '>>> ' + server_answer.match(/<center>(.*)<\/center>/ ).captures[0 ] qrcode_b64 = server_answer.match(/QRCode Win" src="data:image\/png;base64,(.*)" height/ ).captures[0 ] qrcode_file = 'qrcode.png' captcha_b64 = server_answer.match(/Captcha Win" src="data:image\/png;base64,(.*)" height/ ).captures[0 ] captcha_file = 'captcha.png' File .open(qrcode_file, 'wb' ) do |f | f.write(Base64 .decode64(qrcode_b64)) end File .open(captcha_file, 'wb' ) do |f | f.write(Base64 .decode64(captcha_b64)) end qrcode_answer = `python2 myQRCodeSolver.py` .chomp puts 'QRCode : ' .concat(qrcode_answer) captcha_answer = `python2 myCaptchaSolver.py` .chomp puts 'Captcha : ' .concat(captcha_answer) puts "Flag : " + captcha_answer + qrcode_answer File .delete(qrcode_file)File .delete(captcha_file)File .delete("output.gif" )
Here is my custom captcha solver myCaptchaSolver.py
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from PIL import Imagefrom pytesseract import image_to_stringim = Image.open ("captcha.png" ) im = im.convert("P" ) im2 = Image.new("P" ,im.size,255 ) im = im.convert("P" ) temp = {} for x in range (im.size[1 ]): for y in range (im.size[0 ]): pix = im.getpixel((y,x)) temp[pix] = pix if pix == 1 : im2.putpixel((y,x),0 ) im2.save("output.gif" ) im2.load() print (image_to_string(im2))
Here is my custom QR code solver myQRCodeSolver.py
:
1 2 3 4 import qrtoolsqr = qrtools.QR() qr.decode("qrcode.png" ) print qr.data
Flag: ECW{20cbf8e17eb7e62936e3602b498776e6}
.