ECW - 150 - Dilemme - Misc

Informations

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' # to decode base64 images
require 'zxing' # to solve qr code
require 'curb' # for get/post requests
### Crawling the website to get the base64 QRCode ###
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'
#curl.verbose = true
end # Curl
c.perform
#puts c.body_str
### Parse the output ###
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'
### QRCode : base64 to png ###
File.open(qrcode_file, 'wb') do |f|
f.write(Base64.decode64(qrcode_b64))
end
### Captcha : base64 to png ###
File.open(captcha_file, 'wb') do |f|
f.write(Base64.decode64(captcha_b64))
end
### QRCode : png to text string ###
qrcode_answer = ZXing.decode qrcode_file
puts 'QRCode : '.concat(qrcode_answer)
### Captcha : png to text string ###
captcha_answer = `python2 myCaptchaSolver.py`.chomp
puts 'Captcha : '.concat(captcha_answer)
### Send the 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]
# Redo the whole process one more time
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
### Wait for avoiding Jruby to crash ###
sleep(0.5)
### Remove temporary file ###
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' # to decode base64 images
### Crawling the website to get the base64 QRCode ###
hostname = 'https://challenge-ecw.fr/chals/divers200'
cookies = 'session=mySessionCookie'
body_str = `curl https://challenge-ecw.fr/chals/divers200 --cookie '#{cookies}'`
### Parse the output ###
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'
### QRCode : base64 to png ###
File.open(qrcode_file, 'wb') do |f|
f.write(Base64.decode64(qrcode_b64))
end
### Captcha : base64 to png ###
File.open(captcha_file, 'wb') do |f|
f.write(Base64.decode64(captcha_b64))
end
### QRCode : png to text string ###
qrcode_answer = `python2 myQRCodeSolver.py`.chomp
puts 'QRCode : '.concat(qrcode_answer)
### Captcha : png to text string ###
captcha_answer = `python2 myCaptchaSolver.py`.chomp
puts 'Captcha : '.concat(captcha_answer)
### Send the 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]
# Redo the whole process one more time
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
### Remove temporary file ###
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 Image
from pytesseract import image_to_string
im = 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: # these are the numbers to get
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 qrtools
qr = qrtools.QR()
qr.decode("qrcode.png")
print qr.data

Flag: ECW{20cbf8e17eb7e62936e3602b498776e6}.

Share