CrazyLooper

BreizhCTF 2019 - Prog (150 pts).

BreizhCTF 2019: CrazyLooper

Challenge details

Event Challenge Category Points Solves
BreizhCTF 2019 CrazyLooper Programming 150 2

Challenge: chall md5sum : 339457087fe3c986e45937eaf26cbfea

TL;DR

We had a file recursively encoded with multiple algorithms. Each algorithm was given at the beginning of the decoded file. We wrote a program that recursively decode the first file until we got the last clear text.

Methodology

First look at the file

The file command gave us only “data” but a deeper look at the head of the file gave us more information:

xxd chall | head -n 1
00000000: 786f 725f 4a3a 3225 3815 0170 637b 335e  xor_J:2%8..pc{3^

We saw that the file start with “xor_J:” which means that we need to decode the file by xoring with the “J” letter.

with open("chall","rb") as f:
    c = f.read()

algo,content = c.split(":",1)

out = ""
for l in content:
    out += chr(ord(l)^ord("J"))  # Xor content with "J"

with open("challoutput","wb") as f2:
    f2.write(out)
xxd challoutput | head -n 1
00000000: 786f 725f 4b3a 2931 7914 2824 2f2e 2871  xor_K:)1y.($/.(q

We got a second xor with another letter.

Time to script

Here is my final script. Note that we got multiple algorithm (not only xor but also base32, base64, uuencode, bzip2, …). You can also notice that, sometimes, xor key is given as an integer.

cp chall chall0
import bz2
import base64
import zlib

def isInt(s):
    try: 
        int(s)
        return True
    except ValueError:
        return False

n = 0
while 1:
    with open("chall"+str(n),"rb") as f:
        l = f.read()
    algo,content = l.split(":",1)
    if "xor" in algo:
        key = algo.replace("xor_","")
        if not isInt(key):
            key = ord(key)
        else:
            key = int(key)
        out = ""
        for l in content:
            out += chr(ord(l)^key)
        with open("chall"+str(n+1),"wb") as f:
            f.write(out)
    elif "base16" in algo:
        with open("chall"+str(n+1),"wb") as f:
            f.write(base64.b16decode(content))
    elif "base32" in algo:
        with open("chall"+str(n+1),"wb") as f:
            f.write(base64.b32decode(content))
    elif "base64" in algo:
        with open("chall"+str(n+1),"wb") as f:
            f.write(base64.b64decode(content))
    elif "uu_codec" in algo:
        with open("chall"+str(n+1),"wb") as f:
            f.write(content.decode("uu"))
    elif "bz2_codec" in algo:
        with open("chall"+str(n+1),"wb") as f:
            f.write(bz2.decompress(content))
    elif "zlib_codec" in algo:
        with open("chall"+str(n+1),"wb") as f:
            f.write(zlib.decompress(content))
    else:
        print(algo)
    n += 1

The program crash for the chall149 file.

cat chall149
breizhctf{crazy_encodings_looperz}

Flag

breizhctf{crazy_encodings_looperz}

Zeecka