Rating: 3.0

### Requirements

Install zbar
```bash
brew install zbar
```

### Code

1. Crop a face with 3x3 grid
2. Rotate every blocks to the same direction
3. Bruteforce with `zbarimg` (return 0 if QR code is decoded)

```python
import os, itertools
from urllib.request import urlopen
import requests, re, subprocess
from PIL import Image

base = 'http://qubicrube.pwn.seccon.jp:33654'

def download(url, path):
cmd = ['curl', '-s', url, '-o', path]
return subprocess.call(cmd) == 0

def scan_qr(image):
image.save('/tmp/tmpqr.png')
cmd = ['zbarimg', '/tmp/tmpqr.png']
try:
output = subprocess.check_output(cmd, stderr=subprocess.DEVNULL).decode()
output = output.replace('QR-Code:', '')
output = output[:output.rfind('\n')]
except:
output = None
return output

def get_blocks(im):
blocks = []
corners = []
edges = []
middles = []
for i in range(3):
x = i * 82
for j in range(3):
y = j * 82
blocks.append(im.crop((x, y, x + 82, y + 82)))
# rotate blocks to the same direction
corners.append(blocks[0])
corners.append(blocks[2].rotate(-90))
corners.append(blocks[6].rotate(90))
corners.append(blocks[8].rotate(180))
edges.append(blocks[1])
edges.append(blocks[3].rotate(90))
edges.append(blocks[5].rotate(-90))
edges.append(blocks[7].rotate(180))
middles.append(blocks[4])
middles.append(blocks[4].rotate(-90))
middles.append(blocks[4].rotate(-180))
middles.append(blocks[4].rotate(90))
return blocks, corners, edges, middles

def get_color(im):
return list(set(map(lambda c: c[1], im.getcolors())) - {(0, 0, 0)})[0]

def read_images(url):
for i, path in enumerate(re.findall('/images/.*\\.png', requests.get(url).text)):
print('image', i, path, download(base + path, '/tmp/qr_{}.png'.format(i)))
return [Image.open('/tmp/qr_{}.png'.format(i)) for i in range(6)]

def make_qr(blocks):
assert len(blocks) == 9
qr = Image.new('RGB', (246, 246))
for i in range(3):
for j in range(3):
x, y = i * 82, j * 82
b = blocks[i * 3 + j]
assert b.size == (82, 82)
qr.paste(b, (x, y))
return qr

def solve(url):
images = read_images(url)
data = [get_blocks(im) for im in images]
colors = set()
result = []
all_corners, all_edges, all_middles = [], [], []
for _, corners, edges, middles in data:
all_corners += corners
all_edges += edges
all_middles += middles
for edge in all_edges:
color = get_color(edge)
colors.add(color)
for color in colors:
is_color = lambda block: get_color(block) == color
corners = list(filter(is_color, all_corners))
edges = list(filter(is_color, all_edges))
middles = list(filter(is_color, all_middles))
assert len(corners) == len(edges) == len(middles) == 4
c0 = corners.pop(0)
done = False
print('color', color)
for i, (c2, c6, c8) in enumerate(itertools.permutations(corners)):
for j, (e1, e3, e5, e7) in enumerate(itertools.permutations(edges)):
for k, m4 in enumerate(middles):
qr = make_qr([
c0, e1, c2.rotate(90),
e3.rotate(-90), m4, e5.rotate(90),
c6.rotate(-90), e7.rotate(180), c8.rotate(180)
])
ret = scan_qr(qr)
print('.', end='', flush=True)
if ret is not None:
print('[%s]' % ret.strip())
done = True
break
if done:
break
if done:
break
result.append(ret)
return result

import re
url = base
url_regex = 'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+'
results = []
while True:
result = solve(url)
results.append(result)
output = '\n'.join(result)
print('======================================')
print(output)
print('======================================')
url = re.findall(url_regex, output)[0]

```

### Final output
```text
~
color (196, 30, 58)
..............................................................................[No. 49 / 50]
color (0, 81, 186)
.....................................................................[Go! Go!]
color (0, 158, 96)
.....................[Next URL is:]
color (255, 88, 0)
..........[Qubic Rube]
color (255, 213, 0)
...............................................[http://qubicrube.pwn.seccon.jp:33654/504ded069e4db4e3bef9]
color (255, 255, 255)
...........................................................................[SECCON 2017 Online CTF]
======================================
No. 49 / 50
Go! Go!
Next URL is:
Qubic Rube
http://qubicrube.pwn.seccon.jp:33654/504ded069e4db4e3bef9
SECCON 2017 Online CTF
======================================
image 0 /images/504ded069e4db4e3bef9_R.png True
image 1 /images/504ded069e4db4e3bef9_L.png True
image 2 /images/504ded069e4db4e3bef9_U.png True
image 3 /images/504ded069e4db4e3bef9_D.png True
image 4 /images/504ded069e4db4e3bef9_F.png True
image 5 /images/504ded069e4db4e3bef9_B.png True
color (196, 30, 58)
...........................................................[EAN-8:74370788]
color (0, 81, 186)
............................................[SECCON{Thanks to Denso Wave for inventing the QR code}]
color (0, 158, 96)
.............[Congratulations!]
color (255, 88, 0)
....................................[The flag is:]
color (255, 213, 0)
.....................................................................................[SECCON 2017 Online CTF]
color (255, 255, 255)
....[No. 50 / 50]
======================================
EAN-8:74370788
SECCON{Thanks to Denso Wave for inventing the QR code}
Congratulations!
The flag is:
SECCON 2017 Online CTF
No. 50 / 50
======================================
---------------------------------------------------------------
IndexError Traceback (most recent call last)
~/Space/SECCON/qreader/test.py in <module>()
119 print(output)
120 print('======================================')
--> 121 url = re.findall(url_regex, output)[0]

IndexError: list index out of range
```