diff --git a/count.py b/count.py index 9623aba..bf76c05 100644 --- a/count.py +++ b/count.py @@ -20,6 +20,7 @@ for problem_name in problem_names: pass # print traceback.format_exc() +problems.sort(key=lambda p: p.get("value"), reverse=True) print "Grand Total: %d" % len(problems) print "Category Breakdown:" @@ -29,4 +30,4 @@ for category, count in categories: print " %s: %s" % (category, count) for problem in problems: if problem.get("category") != category: continue - print " %s" % problem.get("title") + print " %s [%s]" % (problem.get("title"), problem.get("value")) diff --git a/qrt/description.md b/qrt/description.md new file mode 100644 index 0000000..8872c46 --- /dev/null +++ b/qrt/description.md @@ -0,0 +1 @@ +I've stumbled upon a very strange [QR code](${flag_png})... seems like it was generated with [this generator](http://hexqr.web.easyctf.com). What could it mean? \ No newline at end of file diff --git a/qrt/flag.png b/qrt/flag.png new file mode 100644 index 0000000..d03a93f Binary files /dev/null and b/qrt/flag.png differ diff --git a/qrt/grader.py b/qrt/grader.py new file mode 100644 index 0000000..f0acc68 --- /dev/null +++ b/qrt/grader.py @@ -0,0 +1,22 @@ +from cStringIO import StringIO +from qrt import generate + +FLAG = "are_triangles_more_secure_than_squares?_%s}" + +def get_salt(random): + return "".join([random.choice("0123456789abcdef") for i in range(8)]) + +def generate(random): + salt = get_salt(random) + im = generate("easyctf{%s}" % (FLAG % salt)) + flag = StringIO() + im.save(buf, format="PNG") + return dict(files={ + "flag.png": flag + }) + +def grade(random, key): + salt = get_salt(random) + if key.find(FLAG % salt) >= 0: + return True, "Correct!" + return False, "Nope." \ No newline at end of file diff --git a/qrt/problem.yml b/qrt/problem.yml new file mode 100644 index 0000000..35df231 --- /dev/null +++ b/qrt/problem.yml @@ -0,0 +1,5 @@ +title: Hex QR +category: Programming +value: 200 +author: mzhang +autogen: true \ No newline at end of file diff --git a/qrt/qrt.py b/qrt/qrt.py new file mode 100644 index 0000000..d72640e --- /dev/null +++ b/qrt/qrt.py @@ -0,0 +1,311 @@ +from PIL import Image, ImageDraw +from math import ceil, sqrt + +ALTCONST = sqrt(3) / 2.0 +ISIZE = 784.0 +IMARGIN = 22.0 + +def empty_hex(size): + c = 0 + points = [] + for i in range(2, size * 2 + 2, 2): + points.append([0] * i) + middle = [] + for i in range(size * 2 - 1): + row = [0] * (size * 2) + middle.append(row) + epoints = list(row[:] for row in points[::-1]) + return points + middle + epoints + +def intify(points): + return map(lambda c: (int(c[0]), int(c[1])), points) + +def draw_triangle(im, points, cell, size): + draw = ImageDraw.Draw(im) + # draw.polygon(points, outline=(0, 0, 0)) + if cell: + draw.polygon(intify(points), fill=(0, 0, 0)) + return im + +def getsection(i, size): + return 0 if i <= size - 1 else (1 if i < 3 * size - 1 else 2) + +def getspace(size): + return max(0, sum(len(filter(lambda a: a == 0, b)) for b in empty_hex(size)) - 2) + +def generate(string, debug=False): + size = 0 + binstring = bin(int(string.encode("hex"), 16)).strip("0b") + while size < 2 or getspace(size + 1) < len(binstring): + size += 1 + n = getspace(size) + # print size, len(binstring) + if debug: print "SIZE", size, " n =", n + im = Image.new("RGB", (int(ISIZE), int(ISIZE)), "white") + pattern = empty_hex(size) + # print len(binstring) + # print sum(len(i) for i in pattern) + pattern[0][len(string) % 2] = 1 + curr = (1, 1 + len(string) % 2, 3 - len(string) % 2) + # 0 1 + # 3 2 + for c in range(n): # range(len(binstring)): + i, j, d = curr + row = pattern[i] + if c < len(binstring): + b = int(binstring[c]) + pattern[i][j] = b + if c % 2 == 0: + pattern[i][j] ^= 1 + + section = getsection(i, size) + leftfacing = [j % 2 == 0, j % 2 != (i - size) % 2, j % 2 == 0][section] + if debug: print "c =", c, ", i =", i, ", j =", j, ", d =", d, ", s =", section, ", l =", leftfacing + if section == 0: + if getsection(i + 1, size) == 0: + if leftfacing: + if d == 0: + if j == 0: + curr = i + 1, j, (d + 2) % 4 + else: + curr = i - 1, j - 1, d + elif d == 1 or d == 2: + curr = i, j + 1, d + elif d == 3: + curr = i + 1, j + 1, d + else: + if d == 0: + curr = i, j - 1, d + elif d == 1: + if j == len(row) - 1: + curr = i + 1, j + 2, (d + 2) % 4 + else: + curr = i - 1, j - 1, d + elif d == 2: + curr = i + 1, j + 1, d + elif d == 3: + curr = i, j - 1, d + elif getsection(i + 1, size) == 1: + if leftfacing: + if d == 0: + if j == 0: + curr = i + 1, j, (d + 2) % 4 + else: + curr = i - 1, j - 1, d + elif d == 1 or d == 2: + curr = i, j + 1, d + elif d == 3: + curr = i + 1, j, d + else: + if d == 0: + curr = i, j - 1, d + elif d == 1: + if j == len(row) - 1: + curr = i + 1, j, (d + 2) % 4 + else: + curr = i - 1, j - 1, d + elif d == 2: + curr = i + 1, j, d + elif d == 3: + curr = i, j - 1, d + elif section == 1: + if getsection(i - 1, size) == 0: + if leftfacing: + if j == len(row) - 1: + if d == 1: + curr = i + 2, j, (d + 2) % 4 + elif d == 2: + curr = i + 2, j, (d + 2) % 4 + elif d == 3: + curr = i + 1, j, d + else: + if d == 0: + curr = i - 1, j, d + elif d == 1 or d == 2: + curr = i, j + 1, d + elif d == 3: + curr = i + 1, j, d + else: + if j == 0: + if d == 0: + curr = i + 2, j, (d + 2) % 4 + elif d == 2: + curr = i + 1, j, d + elif d == 3: + curr = i + 2, j, (d + 2) % 4 + else: + if d == 0 or d == 3: + curr = i, j - 1, d + elif d == 1: + curr = i - 1, j, d + elif d == 2: + curr = i + 1, j, d + elif getsection(i - 1, size) == 1: + if getsection(i + 1, size) == 1: + if leftfacing: + if j == len(row) - 1: + if d == 0: + curr = i - 1, j, d + elif d == 1: + curr = i + 2, j, (d + 2) % 4 + elif d == 2: + curr = i + 2, j, (d + 2) % 4 + elif d == 3: + curr = i + 1, j, d + else: + if d == 0: + curr = i - 1, j, d + elif d == 1 or d == 2: + curr = i, j + 1, d + elif d == 3: + curr = i + 1, j, d + else: + if j == 0: + if d == 0: + curr = i + 2, j, (d + 2) % 4 + elif d == 1: + curr = i - 1, j, d + elif d == 2: + curr = i + 1, j, d + elif d == 3: + curr = i + 2, j, (d + 2) % 4 + else: + if d == 0 or d == 3: + curr = i, j - 1, d + elif d == 1: + curr = i - 1, j, d + elif d == 2: + curr = i + 1, j, d + elif getsection(i + 1, size) == 2: + if leftfacing: + if j == len(row) - 1: + if d == 0: + curr = i - 1, j, d + elif d == 2: + curr = i + 1, j, (d + 2) % 4 + else: + if d == 0: + curr = i - 1, j, d + elif d == 1: + curr = i, j + 1, d + elif d == 2: + curr = i, j + 1, d + elif d == 3: + curr = i + 1, j, d + else: + if d == 0: + curr = i, j - 1, d + elif d == 1: + curr = i - 1, j, d + elif d == 2: + curr = i + 1, j, d + elif d == 3: + if j == 0: + curr = i + 1, j, (d + 2) % 4 + else: + curr = i, j - 1, d + elif section == 2: + if getsection(i - 1, size) == 1: + if leftfacing: + if d == 0: + curr = i - 1, j, d + elif d == 1: + curr = i, j + 1, d + elif d == 2: + curr = i, j + 1, d + elif d == 3: + if j == 0: + curr = i + 1, j, (d + 2) % 4 + else: + curr = i + 1, j - 1, d + else: + if d == 0 or d == 3: + curr = i, j - 1, d + elif d == 1: + curr = i - 1, j, d + elif d == 2: + if j == len(row) - 1: + curr = i + 1, j - 2, (d + 2) % 4 + else: + curr = i + 1, j - 1, d + elif getsection(i - 1, size) == 2: + if leftfacing: + if d == 0: + curr = i - 1, j + 1, d + elif d == 1 or d == 2: + curr = i, j + 1, d + elif d == 3: + if j == 0: + curr = i + 1, j, (d + 2) % 4 + else: + curr = i + 1, j - 1, d + else: + if j == len(row) - 1: + if d == 0: + curr = i, j - 1, d + elif d == 1: + curr = i - 1, j + 1, d + elif d == 2: + curr = i + 1, j - 2, (d + 2) % 4 + else: + if d == 0 or d == 3: + curr = i, j - 1, d + elif d == 1: + curr = i - 1, j + 1, d + elif d == 2: + curr = i + 1, j - 1, d + # for i in range(len(pattern)): + # for j in range(len(pattern[i])): + # section = 0 if i <= size - 1 else (1 if i < 3 * size - 1 else 2) + # leftfacing = [j % 2 == 0, j % 2 != (i - size) % 2, j % 2 == 0][section] + # if not leftfacing: + # print j, + # print + sidelen = (ISIZE - IMARGIN * 2) / (2.0 * size) + altitude = sidelen * ALTCONST + # print sidelen, altitude + for i in range(len(pattern)): + section = getsection(i, size) + row = pattern[i] + rowleft = (ISIZE / 2) - (altitude * len(row) / 2) + if section % 2 == 0: + evenrow = i % 2 == 0 + for j in range(len(row)): + cell = row[j] + top = ceil(i / 2.0) * sidelen - (0.5 * sidelen if not evenrow else 0) + IMARGIN + bottom = top + sidelen + if j % 2 == 0: + points = [ + (rowleft + altitude * j, top + sidelen / 2), + (rowleft + altitude * (j + 1), top), (rowleft + altitude * (j + 1), bottom) + ] + else: + points = [ + (rowleft + altitude * (j + 1), top + sidelen / 2), + (rowleft + altitude * j, top), (rowleft + altitude * j, bottom) + ] + draw_triangle(im, points, cell, size) + else: + evenrow = (i - size) % 2 == 1 + for j in range(len(row)): + cell = row[j] + top = ceil(i / 2.0) * sidelen - (0.5 * sidelen if i % 2 == 1 else 0) + IMARGIN + bottom = top + sidelen + if j % 2 == (i - size) % 2: + points = [ + (rowleft + altitude * (j + 1), top + sidelen / 2), + (rowleft + altitude * j, top), (rowleft + altitude * j, bottom) + ] + else: + points = [ + (rowleft + altitude * j, top + sidelen / 2), + (rowleft + altitude * (j + 1), top), (rowleft + altitude * (j + 1), bottom) + ] + draw_triangle(im, points, cell, size) + return im + +# im = generate("easyctf{are_triangles_more_secure_than_squares?}") +# im.save("flag.png") + +# for i in range(40, 60): +# generate("A" * i, "/tmp/%d.png" % i) \ No newline at end of file diff --git a/qrt/qrt.pyc b/qrt/qrt.pyc new file mode 100644 index 0000000..02316a0 Binary files /dev/null and b/qrt/qrt.pyc differ diff --git a/qrt/server.py b/qrt/server.py new file mode 100644 index 0000000..51c70de --- /dev/null +++ b/qrt/server.py @@ -0,0 +1,30 @@ +from flask import Flask, request +from qrt import generate +from base64 import b64encode +from binascii import b2a_base64 +from cStringIO import StringIO +from traceback import format_exc + +app = Flask(__name__) + +@app.route("/", methods=["GET", "POST"]) +def index(): + try: + html = "hex qr
enter a string:
" + if request.method == "POST": + if not request.form.get("text"): + html %= "" + html += "

empty

" + else: + html %= request.form["text"] + im = generate(request.form["text"]) + buf = StringIO() + im.save(buf, format="JPEG") + html += "" % b2a_base64(buf.getvalue()) + else: + html %= "" + return html + except: + return "" % format_exc() + +app.run(host="0.0.0.0", port=5000) \ No newline at end of file