HACKvent 2020 - Day 6

01-01-2021 - 2 minutes, 31 seconds - CTF

Challenge - Twelve steps of christmas

On the sixth day of Christmas my true love sent to me...

six valid QRs,
five potential scrambles,
four orientation bottom and right,
and the rest has been said previously.

Rubiks Cube with QR codes as sides

as PDF

Requirements: a printer

Hints: selbmarcs

Solution

Since I am not that much into rubik cubes, I tried the brute force method. I digitally cut the quarters of each side of the cube into seperate pictures. This gave me 24 images. Each image can be part of the QR Code in 4 different orientations. That makes 84.934.656 possible combinations, and I was willing to try them all. So lets write some code:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using ZXing;

Console.WriteLine("Enter base bath: ");
var path = Console.ReadLine();

var files = Directory.EnumerateFiles(path, "", SearchOption.TopDirectoryOnly);

var qrCodeQuarters = files.Select(file => new QrCodeQuarter(file)).ToList();
var originalLength = qrCodeQuarters.Count;
for (var j = 0; j < originalLength; j++)
{
    qrCodeQuarters.Add(new QrCodeQuarter(qrCodeQuarters[j].Path, 90));
    qrCodeQuarters.Add(new QrCodeQuarter(qrCodeQuarters[j].Path, 180));
    qrCodeQuarters.Add(new QrCodeQuarter(qrCodeQuarters[j].Path, 270));
}

Console.WriteLine($"Got {qrCodeQuarters.Count} possible parts. Makes Math.Pow(qrCodeQuarters.Count, 4) possibilities");

var q = new BarcodeReader();
q.AutoRotate = false;
q.TryInverted = false;
q.Options.TryHarder = false;
q.Options.PossibleFormats = new List<BarcodeFormat>();
q.Options.PossibleFormats.Add(BarcodeFormat.QR_CODE);

for (var i1 = 0; i1 < qrCodeQuarters.Count; i1++)
{
    for (var i2 = 0; i2 < qrCodeQuarters.Count; i2++)
    {
        for (var i3 = 0; i3 < qrCodeQuarters.Count; i3++)
        {
            for (var i4 = 0; i4 < qrCodeQuarters.Count; i4++)
            {
                var tmp = CombineQuarters(qrCodeQuarters[i1], qrCodeQuarters[i2], qrCodeQuarters[i3], qrCodeQuarters[i4]);

                var r = q.Decode(tmp);

                if (r != null)
                {
                    Console.WriteLine($"found in {i1:00}-{i2:00}-{i3:00}-{i4:00}");
                    Console.WriteLine($"\t{qrCodeQuarters[i1].Path}");
                    Console.WriteLine($"\t{qrCodeQuarters[i2].Path}");
                    Console.WriteLine($"\t{qrCodeQuarters[i3].Path}");
                    Console.WriteLine($"\t{qrCodeQuarters[i4].Path}");
                    Console.WriteLine(r.Text);
                }
            }
        }
    }
}

Console.WriteLine("done");
Console.ReadLine();

static Bitmap CombineQuarters(QrCodeQuarter q1, QrCodeQuarter q2, QrCodeQuarter q3, QrCodeQuarter q4)
{
    const byte quarterSize = 92;

    var bitmap = new Bitmap(quarterSize * 2, quarterSize * 2);

    using var g = Graphics.FromImage(bitmap);
    g.DrawImage(q1.Image, 0, 0);
    g.DrawImage(q2.Image, quarterSize, 0);
    g.DrawImage(q3.Image, 0, quarterSize);
    g.DrawImage(q4.Image, quarterSize, quarterSize);

    return bitmap;
}

public class QrCodeQuarter
{
    public QrCodeQuarter(string path, short rotation = 0)
    {
        Path = path;
        Rotation = rotation;

        LoadImage();
    }

    public string Path { get; set; }
    public short Rotation { get; set; }
    public Image Image { get; private set; }

    private void LoadImage()
    {
        var tmp = new Bitmap(Image.FromFile(Path));

        for (var i = 0; i < Rotation / 90; i++)
        {
            tmp.RotateFlip(RotateFlipType.Rotate90FlipNone);
        }

        Image = tmp;
    }
}

Of course this is highly unoptimized. A lot combinations could be ruled out easily. For example: A single part can not be used for the other parts in the same QR code. Since I got results fast enough, I didn’t bother too much with optimizing.

Nevertheless scanning 84.934.656 possible QRs is a lot. So I waited some time for the first result. When it was found, I deleted the 4 parts manually, because I knew that they could not possibly be part of one of the other QR Codes. This decreased the search space quite a bit for the others.

After repeating this five times, I had the following results:

HV20{Erno_
Petrus_is
_Valid.
Rubik_would
#HV20QRubicsChal}
_be_proud.

Which, when brought into correct order, yields the flag:

HV20{Erno_Rubik_would_be_proud.Petrus_is_Valid.#HV20QRubicsChal}

Next Post Previous Post