SHA2017 teaser - crypto engine

Intro

flag.png
given flag

First we notice that color square is added for every three letters in text to encrypt. At the end of image we have n mod 3 two-decimal hex numbers. Also, color of previous squares didn’t change if we add more letters. Knowing that the flag must be in format flag{md5hash} we have tried encrypt flag{x

flagx.png
encrypted 'flag{x'

And we get what we want to. RGB code of the first color was exactly the same as the color of the flag, and also second color had only different blue value in comprasion with the flag. So, it’s brute force time!

Preparing

First, We prepared code for downloading image and changing it to the color values. Every square is 40x40 with some border, so we take pixel at (5, 20). Also, we didn’t analyze the text on the flag, we now that the last character must be }, so it will leave us only one character to guess. Here code comes!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from PIL import Image
import requests
import numpy as np

def download_img(url):
    img = Image.open(requests.get(url, stream=True).raw)
    img = np.array(img.getdata())
    img = np.resize(img, (40, int(len(img)/40), 3))
    return img

def img_to_code(url):
    img = download_img(url)
    length = len(img[0])
    column = 20
    array = []
    while column < length:
        for i in img[5][column]:
            array.append(i)
        column = column + 40
    return array

Brute force

We now that the flag is in the flag{md5hash} format, so we only have to brute force values in [a-fA-F0-9]. To get new square encrypted text must be multiplication of 3. This leaded to not-so-beautiful if statement in our solution, but it works :D

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
flag_url = "https://cryptoengine.stillhackinganyway.nl/flag"
flag = img_to_code(flag_url)
print(flag)

url = "https://cryptoengine.stillhackinganyway.nl/encrypt?text="
text = "fla"

enc_text = img_to_code(url+text)
possible = "abcdefABCDEF0123456789"

while len(enc_text) < len(flag):
    len_enc = len(enc_text)
    cur_three = ""
    start = 0
    if(text == "fla"):
        cur_three = "g{"
        start = 2
    for i in range(start, 3):
        for x in possible:
            temp = cur_three + x #guess next letter
            temp = temp + "a"*(3-len(temp)) #fill to three letters
            temp_url = url+text+temp #concatenate url
            print(temp_url)
            attempt = img_to_code(temp_url)
            if attempt[len_enc+i] == flag[len_enc+i]: #if guess is correct
                cur_three = cur_three + x
                break
    text = text + cur_three
    enc_text = img_to_code(url+text)
flagalmost.png
result of program, only one char to guess

Get the flag

After that we bruteforced by hand last char of md5hash and get the flag! flag{deaf983eb34e485ce9d2aff0ae44f852}