Here’s the solution for a programming challenge I did during SECCON 2017.
The challenge is a programming challenge, we get greeted with a three.js cube with 6 textures, one for each faces with a QR-Code on them. Using zbar
to read them, we will see this message:
Seems like we are going to code a rubiks solver.
After seeing some other write-ups for this challenge, it seems like there’s a way to scramble the cube enough to give the solution with enough permutations. My solution is a little bit different in the fact that I am going to solve it using a rubiks solver rather than using a brute-forcy way.
import kociemba
from PIL import Image
import requests
import argparse
import logging as log
import zbar
import sys
import os
import random
from subprocess import *
# last 504ded069e4db4e3bef9
url = "http://qubicrube.pwn.seccon.jp:33654/"
parser = argparse.ArgumentParser()
parser.add_argument("prefix", help="the prefix to use, example 504ded069e4db4e3bef9")
parser.add_argument("-v", "--verbose", help="increase output verbosity",
action="store_true")
parser.add_argument("-d", "--download", help="download the challenge",
action="store_true")
args = parser.parse_args()
prefix = str(args.prefix.split("/")[-1:][0])
if args.verbose:
log.basicConfig(level=log.DEBUG, format="[+] %(message)s")
else:
log.basicConfig(level=log.WARNING)
log.info("Using prefix: "+prefix)
suffix = ["_R", "_L", "_U", "_B", "_F", "_D"]
extension = ".png"
requests_queue = []
if args.download:
for suf in suffix:
current_name = prefix + suf + extension
current_url = url + "images/" + current_name
log.info(current_url)
r = requests.get(current_url)
path = current_name
if r.status_code == 200:
with open(path, 'w+') as f:
for chunk in r.iter_content(1024):
f.write(chunk)
log.info("Getting "+ prefix + suf)
s = 82
up = Image.open(prefix+"_U.png")
right = Image.open(prefix+"_R.png")
front = Image.open(prefix+"_F.png")
down = Image.open(prefix+"_D.png")
left = Image.open(prefix+"_L.png")
back = Image.open(prefix+"_B.png")
cube2 = []
def cropImages(img):
for i in range (0, 9):
cube2.append(img.copy().crop(((i % 3) * s, ((i / 3) % 3) * s, ((i % 3) + 1) * s, (((i / 3) % 3) + 1) * s)))
cropImages(up)
cropImages(right)
cropImages(front)
cropImages(down)
cropImages(left)
cropImages(back)
#URFDLB
def getColorBasic(img):
x = 0
while (img.getpixel((x, 0)) == (0, 0, 0)):
x += 1
return img.getpixel((x, 0))
U = getColorBasic(cube2[4])
R = getColorBasic(cube2[13])
F = getColorBasic(cube2[22])
D = getColorBasic(cube2[31])
L = getColorBasic(cube2[40])
B = getColorBasic(cube2[49])
# U = (255, 255, 255)
# D = (0, 81, 186) # blue
# R = (196, 30, 58) # red
# B = (255, 213, 0) # yellow
# F = (0, 158, 96) # green
# L = (255, 88, 0) # orange
def getColor(img):
x = 0
while (img.getpixel((x, 0)) == (0, 0, 0)):
x += 1
color = img.getpixel((x, 0))
if color == U:
return "U"
elif color == R:
return "R"
elif color == F:
return "F"
elif color == D:
return "D"
elif color == L:
return "L"
elif color == B:
return "B"
else:
logging.error("wtf color")
img.save("wtf.png")
sys.exit()
solve = ""
cube = ""
for i in cube2:
cube += getColor(i)
log.info(cube)
solve = kociemba.solve(cube)
log.info(solve)
def regenerateCube():
global up
global right
global front
global down
global left
global back
up = Image.new("RGB", (s*3, s*3))
for i in range(0,9):
up.paste(cube2[i], ((i % 3) * s,((i / 3) % 3) * s))
up.save("up.png")
right = Image.new("RGB", (s*3, s*3))
for i in range(9,18):
right.paste(cube2[i], ((i % 3) * s,((i / 3) % 3) * s))
right.save("right.png")
front = Image.new("RGB", (s*3, s*3))
for i in range(18,27):
front.paste(cube2[i], ((i % 3) * s,((i / 3) % 3) * s))
front.save("front.png")
down = Image.new("RGB", (s*3, s*3))
for i in range(27,36):
down.paste(cube2[i], ((i % 3) * s,((i / 3) % 3) * s))
down.save("down.png")
left = Image.new("RGB", (s*3, s*3))
for i in range(36,45):
left.paste(cube2[i], ((i % 3) * s,((i / 3) % 3) * s))
left.save("left.png")
back = Image.new("RGB", (s*3, s*3))
for i in range(45,54):
back.paste(cube2[i], ((i % 3) * s,((i / 3) % 3) * s))
back.save("back.png")
# 00-01-02
# 03-04-05
# 06-07-08
#36-37-38 18-19-20 09-10-11 45-46-47
#39-40-41 21-22-23 12-13-14 48-49-50
#42-43-44 24-25-26 15-16-17 51-52-53
# 27-28-29
# 30-31-32
# 33-34-35
def moveR():
log.info("Moving right")
copy = []
copy.append(cube2[2])
copy.append(cube2[5])
copy.append(cube2[8])
cube2[2] = cube2[20]
cube2[5] = cube2[23]
cube2[8] = cube2[26]
cube2[20] = cube2[29]
cube2[23] = cube2[32]
cube2[26] = cube2[35]
cube2[29] = cube2[51].rotate(180)
cube2[32] = cube2[48].rotate(180)
cube2[35] = cube2[45].rotate(180)
cube2[51] = copy[0].rotate(180)
cube2[48] = copy[1].rotate(180)
cube2[45] = copy[2].rotate(180)
i = right.rotate(270)
cube2[9] = i.crop((0, 0, s, s))
cube2[10] = i.crop((s, 0, s*2, s))
cube2[11] = i.crop((s*2, 0, s*3, s))
cube2[12] = i.crop((0, s, s, s*2))
cube2[13] = i.crop((s, s, s*2, s*2))
cube2[14] = i.crop((s*2, s, s*3, s*2))
cube2[15] = i.crop((0, s*2, s, s*3))
cube2[16] = i.crop((s, s*2, s*2, s*3))
cube2[17] = i.crop((s*2, s*2, s*3, s*3))
def moveD():
log.info("Moving down")
copy = []
copy.append(cube2[24])
copy.append(cube2[25])
copy.append(cube2[26])
cube2[24] = cube2[42]
cube2[25] = cube2[43]
cube2[26] = cube2[44]
cube2[42] = cube2[51]
cube2[43] = cube2[52]
cube2[44] = cube2[53]
cube2[51] = cube2[15]
cube2[52] = cube2[16]
cube2[53] = cube2[17]
cube2[15] = copy[0]
cube2[16] = copy[1]
cube2[17] = copy[2]
i = down.rotate(270)
cube2[27] = i.crop((0, 0, s, s))
cube2[28] = i.crop((s, 0, s*2, s))
cube2[29] = i.crop((s*2, 0, s*3, s))
cube2[30] = i.crop((0, s, s, s*2))
cube2[31] = i.crop((s, s, s*2, s*2))
cube2[32] = i.crop((s*2, s, s*3, s*2))
cube2[33] = i.crop((0, s*2, s, s*3))
cube2[34] = i.crop((s, s*2, s*2, s*3))
cube2[35] = i.crop((s*2, s*2, s*3, s*3))
def moveL():
log.info("Moving left")
copy = []
copy.append(cube2[27])
copy.append(cube2[30])
copy.append(cube2[33])
cube2[27] = cube2[18]
cube2[30] = cube2[21]
cube2[33] = cube2[24]
cube2[18] = cube2[0]
cube2[21] = cube2[3]
cube2[24] = cube2[6]
cube2[0] = cube2[53].rotate(180)
cube2[3] = cube2[50].rotate(180)
cube2[6] = cube2[47].rotate(180)
cube2[53] = copy[0].rotate(180)
cube2[50] = copy[1].rotate(180)
cube2[47] = copy[2].rotate(180)
i = left.rotate(270)
cube2[36] = i.crop((0, 0, s, s))
cube2[37] = i.crop((s, 0, s*2, s))
cube2[38] = i.crop((s*2, 0, s*3, s))
cube2[39] = i.crop((0, s, s, s*2))
cube2[40] = i.crop((s, s, s*2, s*2))
cube2[41] = i.crop((s*2, s, s*3, s*2))
cube2[42] = i.crop((0, s*2, s, s*3))
cube2[43] = i.crop((s, s*2, s*2, s*3))
cube2[44] = i.crop((s*2, s*2, s*3, s*3))
def moveU():
log.info("Moving up")
copy = []
copy.append(cube2[18])
copy.append(cube2[19])
copy.append(cube2[20])
cube2[18] = cube2[9]
cube2[19] = cube2[10]
cube2[20] = cube2[11]
cube2[9] = cube2[45]
cube2[10] = cube2[46]
cube2[11] = cube2[47]
cube2[45] = cube2[36]
cube2[46] = cube2[37]
cube2[47] = cube2[38]
cube2[36] = copy[0]
cube2[37] = copy[1]
cube2[38] = copy[2]
i = up.rotate(270)
cube2[0] = i.crop((0, 0, s, s))
cube2[1] = i.crop((s, 0, s*2, s))
cube2[2] = i.crop((s*2, 0, s*3, s))
cube2[3] = i.crop((0, s, s, s*2))
cube2[4] = i.crop((s, s, s*2, s*2))
cube2[5] = i.crop((s*2, s, s*3, s*2))
cube2[6] = i.crop((0, s*2, s, s*3))
cube2[7] = i.crop((s, s*2, s*2, s*3))
cube2[8] = i.crop((s*2, s*2, s*3, s*3))
def moveF():
log.info("Moving front")
copy = []
copy.append(cube2[15])
copy.append(cube2[12])
copy.append(cube2[9])
cube2[15] = cube2[8].rotate(270)
cube2[12] = cube2[7].rotate(270)
cube2[9] = cube2[6].rotate(270)
cube2[8] = cube2[38].rotate(270)
cube2[7] = cube2[41].rotate(270)
cube2[6] = cube2[44].rotate(270)
cube2[38] = cube2[27].rotate(270)
cube2[41] = cube2[28].rotate(270)
cube2[44] = cube2[29].rotate(270)
cube2[27] = copy[0].rotate(270)
cube2[28] = copy[1].rotate(270)
cube2[29] = copy[2].rotate(270)
i = front.rotate(270)
cube2[18] = i.crop((0, 0, s, s))
cube2[19] = i.crop((s, 0, s*2, s))
cube2[20] = i.crop((s*2, 0, s*3, s))
cube2[21] = i.crop((0, s, s, s*2))
cube2[22] = i.crop((s, s, s*2, s*2))
cube2[23] = i.crop((s*2, s, s*3, s*2))
cube2[24] = i.crop((0, s*2, s, s*3))
cube2[25] = i.crop((s, s*2, s*2, s*3))
cube2[26] = i.crop((s*2, s*2, s*3, s*3))
def moveB():
log.info("Moving back")
copy = []
copy.append(cube2[0])
copy.append(cube2[1])
copy.append(cube2[2])
cube2[0] = cube2[11].rotate(90)
cube2[1] = cube2[14].rotate(90)
cube2[2] = cube2[17].rotate(90)
cube2[11] = cube2[35].rotate(90)
cube2[14] = cube2[34].rotate(90)
cube2[17] = cube2[33].rotate(90)
cube2[35] = cube2[42].rotate(90)
cube2[34] = cube2[39].rotate(90)
cube2[33] = cube2[36].rotate(90)
cube2[42] = copy[0].rotate(90)
cube2[39] = copy[1].rotate(90)
cube2[36] = copy[2].rotate(90)
i = back.rotate(270)
cube2[45] = i.crop((0, 0, s, s))
cube2[46] = i.crop((s, 0, s*2, s))
cube2[47] = i.crop((s*2, 0, s*3, s))
cube2[48] = i.crop((0, s, s, s*2))
cube2[49] = i.crop((s, s, s*2, s*2))
cube2[50] = i.crop((s*2, s, s*3, s*2))
cube2[51] = i.crop((0, s*2, s, s*3))
cube2[52] = i.crop((s, s*2, s*2, s*3))
cube2[53] = i.crop((s*2, s*2, s*3, s*3))
def checkStatus():
global cube
cube = ""
for i in cube2:
cube += getColor(i)
log.info(cube)
log.info(kociemba.solve(cube))
def parseSolve(solve):
for i in solve.split(" "):
if i[-1:] == "'":
eval("move"+i[0:1]+"()")
regenerateCube()
eval("move"+i[0:1]+"()")
regenerateCube()
eval("move"+i[0:1]+"()")
elif i[-1:] == "2":
eval("move"+i[0:1]+"()")
regenerateCube()
eval("move"+i[0:1]+"()")
else:
eval("move"+i[0:1]+"()")
regenerateCube()
checkStatus()
parseSolve(solve)
scanner = zbar.ImageScanner()
scanner.parse_config('enable')
def decodeImage(img):
image = zbar.Image(s*3, s*3, 'Y800', img.copy().convert('L').tostring())
scanner.scan(image)
for symbol in image:
print "Found: [%s]" % symbol.data
del(image)
def zbard():
decodeImage(up)
decodeImage(front)
decodeImage(down)
decodeImage(left)
decodeImage(right)
decodeImage(back)
def rotateCenter():
cube2[4] = cube2[4].rotate(90)
cube2[13] = cube2[13].rotate(90)
cube2[22] = cube2[22].rotate(90)
cube2[31] = cube2[31].rotate(90)
cube2[40] = cube2[40].rotate(90)
cube2[49] = cube2[49].rotate(90)
log.info("Solved")
zbard()
for i in range(3):
rotateCenter()
regenerateCube()
zbard()