ECW - 150 - Dilemme - Misc

Information#

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