Monday, July 24, 2017

Palo Alto Labyrenth Threat 03 Writeup

Labyrenth 2017 Threat 03

Threat 03 is different from all other challenges (except random 6) as it presents us with a website instead of a download http://youwontfind.me/

First step I took was to visit the website

and check the source for each page I would find

the source for the main site is given above and contains a suspicious comment

642C740D0C297E3A5E1B4D6A70346C24175D56485F7F2B3C0E1F1C6D716F3C2013095B405B2C2F385D491C62763930231A560E13507879390B414E36216B327C1A065E42022C2032

Next steps I took were to try multiple decodings for that string and use all kinds of steg tools on the images which turned out to be a dead end. If you look at the given source you can see a copyright by s. williams.
Next thing I did was a whois on youwontfind.me which returned
I googled the email address Sarah.Williams.1986@yandex.com and found a password dump, so I tried to login into the email account with the given password which obviously didn't work out (I realized it wouldn't make much sense as in everyone could just overtake the account).
I tried other hits and found a linkedin profile that looked promising https://www.linkedin.com/in/sarahwilliams1986


The profile contained many hints referring to PAN or labyrinth and also a link to a stackoverflow user
https://stackoverflow.com/users/7794824/babytoby which asked one specific question https://stackoverflow.com/questions/43807871/python-script-isnt-working/43807928

Having a look at the given python code immediately raised some suspicions. The code contained weird and obvious errors. I cleaned the code and tried the given test parameters

def encrypt(varAble1, varAble2):
    varAble1_size = len(varAble1)/float(len(varAble2))
    if str(varAble1_size).split(".")[1] == "0":
        pass
    else:
        while str(varAble1_size).split(".")[1] != "0":
            varAble1 += "@"
            varAble1_size = len(varAble1)/float(len(varAble2))
    code = []
    varAble1 = list(varAble1)
    varAble2 = list(varAble2)
    multiply_size = int(str((varAble1_size)).split(".")[0]) * 8
    while varAble1 != []:
        p_varAble1 = varAble1[0:8]
        p_varAble2 = varAble2[0:8]
        temp_list = []
        for i in xrange(0,8):
            if type(p_varAble2[i]) == type(int):
                new_ct = (ord(chr(p_varAble2[i])) ^ ord(p_varAble1[0]))
            else:
                try:
                    new_ct = (ord(p_varAble2[i]) ^ ord(p_varAble1[0]))
                except:
                    new_ct = ((p_varAble2[i]) ^ ord(p_varAble1[0]))
            code.append(new_ct)
            temp_list.append(new_ct)
            varAble1.pop(0)
            p_varAble1.pop(0)
            varAble2 = temp_list
        varAble2.reverse()
    code.reverse()
    #varAble1 = code.reverse()
    code_text = []
    for i in code:
        hex_value = hex(i)
        if len(hex_value) != 4:
            code_text.append("0" + hex(i)[2:])
        else:
            code_text.append(hex(i)[2:])
    code_text = "".join(code_text).upper()
    return code_text
I realized that the password parameter is only important for the first 8 bytes of the output, afterwards the result of the last xor calculation is used as the password for the next 8 byte chunk. To revert the operation one can simply xor the last two chunks of the encoded output with each other and repeat that process. To decode the last chunk the user supplied password is required. If n is the last chunk the decryption algorithm is to xor chunk[n] ^ chunk[n-1], chunk[n-1] ^ chuck[n-2] and concatenate the results. Running the following code with the comment from the website and without supplying a password

def decryptor(encoded_text,password=''):
    chunks = [encoded_text[i:i+2] for i in range(0, len(encoded_text), 2)]
    x = len(chunks)
    res=[]
    p_1 = ''
    p_2 = ''
    while(x > 8):
        p_1 = chunks[x-8:x]
        p_2 = chunks[x-16:x-8][::-1]
        temp = ''
        for i in range(0,8):
            temp+=chr(int(p_1[i],16)^int(p_2[i],16))
        res.append(temp)
        x-=8
    if len(password) == 8:
        x = len(chunks)
        p_1 = chunks[x-8:x][::-1]
        temp = ''
        for i in range(0,8):
            temp+=chr(int(p_1[i],16)^ord(password[i]))
        res = [temp]+res
    print ''.join(res)

prints

f45c4ba9286f2edf9f7e2d0def096b903541600624c299a731b8520bdedf}@@@


which looks promising. We know that the flag has to start with PAN{ so I calculated the first 4 chars of the password to be baby. I remembered the username of the stackoverflow account was babytoby, calling the function using the password babytoby gives us the flag
PAN{61dcf45c4ba9286f2edf9f7e2d0def096b903541600624c299a731b8520bdedf}

No comments:

Post a Comment