Hacking Tube

Site no longer updated. Migrated to https://bruce30262.github.io/

SCTF 2014 -- Code400

| Comments

Code400 gave us a python script

code400.py
import json
import hashlib
import os
import base64
from Crypto.Cipher import AES

fp = open("secret.json", "r")
secret = json.load(fp)
fp.close()

if type(secret["the answer to life the universe and everything"]) != type(u"77"):
    destroy_the_universe()

answer = hashlib.sha1(secret["the answer to life the universe and everything"]).hexdigest()[0:16]
key = hashlib.sha1(secret["Don't google what it is"]).digest()[0:6]

if ord(key[4])*(ord(key[5])-5) != 17557:
    destroy_the_universe()

keys = ["hey"+key[2]+"check"+key[3]+"it"+key[0]+"out", 
        "come"+key[1]+"on"+key[4]+"baby"+key[5]+"~~!"]
answer = AES.new(keys[1], AES.MODE_ECB).encrypt(AES.new(keys[0], AES.MODE_ECB).encrypt(answer))

if base64.b64encode(answer) == "fm2knkCBHPuhCQHYE3spag==":
    fp = open("%s.txt" % hashlib.sha256(key).hexdigest(), "w")
    fp.write(secret["The entrance to the new world"])
    fp.close()

After we analyze the code, we found that the key point is the key variable.
The key variable is a 6 bytes data. Since key[4] * (key[5]-5) has to be 17557, we know that there's only 2 conditions:
1. key[4] = 97 & key[5] = 186
2. key[4] = 181 & key[5] = 102

But we still know nothing about key[0] ~ key[4], so we'll have to use brute-force attack to get the rest of these 4 bytes.

The script told us the ciphertext:

base64.decode("fm2knkCBHPuhCQHYE3spag==")
= "\x7e\x6d\xa4\x9e\x40\x81\x1c\xfb\xa1\x09\x01\xd8\x13\x7b\x29\x6a"

But it didn't tell us what's the plaintext, which is

hashlib.sha1(secret["the answer to life the universe and everything"]).hexdigest()[0:16]

We kind of stuck in here for a while, before I google what the hell is "the answer to life the universe and everything"

And this is what google told me:

42

.........................WHAT THE F*CK?
How the hell are we suppose to know that?! (╯°д°)╯ ︵ ┻━┻
God damn it.....

So apparently, the plaintext is:

hashlib.sha1("42").hexdigest()[0:16]
= "92cfceb39d57d914"

Now we got the ciphertext & plaintext, we can use brute-force attack to crack the whole key. The time complexity's about 256*256*256*256*2 ( key[4] & key[5] has only 2 conditions ), which is about 8.5 billion calculations. For me, I wrote a C++ program, with the help of the OpenSSL library, spent about 25 minutes to crack the key out.

#include <openssl/aes.h>
#include <stdlib.h>
#include <stdio.h>

//ciphertext
static unsigned char ori[] = {
    0x7e, 0x6d, 0xa4, 0x9e, 0x40, 0x81, 0x1c, 0xfb,
    0xa1, 0x09, 0x01, 0xd8, 0x13, 0x7b, 0x29, 0x6a,
};

//2: index 3
//3: index 9
//0: index 12
static unsigned char key0[] = {
    0x68, 0x65, 0x79, 0x02, 0x63, 0x68, 0x65, 0x63,
    0x6b, 0x03, 0x69, 0x74, 0x00, 0x6f, 0x75, 0x74,
};

//1: index 4
//4: index 7
//5: index 12
static unsigned char key1[] = {
    0x63, 0x6f, 0x6d, 0x65, 0x01, 0x6f, 0x6e, 0x04,
    0x62, 0x61, 0x62, 0x79, 0x05, 0x7e, 0x7e, 0x21,
};

bool out = false;

int main()
{
    unsigned char text[] = "92cfceb39d57d914"; //plaintext
 
    unsigned char * enc_out = (unsigned char*)malloc(16*sizeof(unsigned char)); 

    AES_KEY enc_key;

    int i;
    unsigned char i0,i1,i2,i3;

    for (i0 = 0x00 ; i0 <= 0xff ; i0+=0x1 )
    {
        printf("i0: %X\n", i0); //monitor the cracking process
    
        if(out) break;

        for (i1 = 0x00 ; i1 <= 0xff ; i1+=0x1 )
        {
            if(out) break;

            for (i2 = 0x00 ; i2 <= 0xff ; i2+=0x1 )
            {
                if(out) break;

                for (i3 = 0x00 ; i3 <= 0xff ; i3+=0x1 )
                {
                    key0[3] = i2; //key[2]
                    key0[9] = i3; //key[3]
                    key0[12] = i0; //key[0]

                    key1[4] = i1; //key[1]
                    key1[7] = 0x61; //key[4]
                    key1[12] = 0xBA; //key[5]

                    AES_set_encrypt_key(key0, 128, &enc_key);
                    AES_ecb_encrypt(text, enc_out, &enc_key, AES_ENCRYPT);  
                    AES_set_encrypt_key(key1,128, &enc_key);
                    AES_ecb_encrypt(enc_out, enc_out, &enc_key, AES_ENCRYPT);  

                    int cnt = 0;

                    for(i=0;*(enc_out+i)!=0x00;i++)
                    {
                        //printf("%X ",*(enc_out+i));
                        if ( *(enc_out+i) == ori[i] ) cnt++;
                        else break;
                    }

                    if (cnt == 16)
                    {
                        puts("got!!");
                        out = true;
                        break;
                    }

                    key1[7] = 0xB5; //key[4]
                    key1[12] = 0x66; //key[5]

                    AES_set_encrypt_key(key0, 128, &enc_key);
                    AES_ecb_encrypt(text, enc_out, &enc_key, AES_ENCRYPT);  
                    AES_set_encrypt_key(key1,128, &enc_key);
                    AES_ecb_encrypt(enc_out, enc_out, &enc_key, AES_ENCRYPT);  

                    cnt = 0;

                    for(i=0;*(enc_out+i)!=0x00;i++)
                    {
                        //printf("%X ",*(enc_out+i));
                        if ( *(enc_out+i) == ori[i] ) cnt++;
                        else break;
                    }

                    if (cnt == 16)
                    {
                        puts("got!!");
                        out = true;
                        break;
                    }
                    if(i3 == 0xff) break;
                }
                if(i2 == 0xff) break;
            }
            if(i1 == 0xff) break;
        }
        if(i0 == 0xff) break;
    }


    printf("k0: %X\n", key0[12]); //key[0]
    printf("k1: %X\n", key1[4]); //key[1]
    printf("k2: %X\n", key0[3]); //key[2]
    printf("k3: %X\n", key0[9]); //key[3]
    printf("k4: %X\n", key1[7]); //key[4]
    printf("k5: %X\n", key1[12]); //key[5]

    free(enc_out);

    return 0;
} 

key : \x81\x69\x37\x88\x61\xBA

But we're not done yet. After we got the right key, it will help us generate the right filename, which is:

key = "\x81\x69\x37\x88\x61\xBA"
filename = hashlib.sha256(key).hexdigest()+".txt"
         = "5bd15779b922c19ef9a9ba2f112df1f2dbb0ad08bbf9edac27a28a0f3ba753f4.txt"

After we enter the filename in the url (under the Code400 domain of course), we found a message. It gave us a ciphertext and a plaintext which is partialy decrypted:

#base64.encode(ciphertext)

ciphertext = "Or18/xSC2xW5pT7BLbIE7YPGLwWytbZsxupMp4w6iaa0QvtYZUMefkf43wmzR36MekHm23wgI4buIJLGk7m7gTq9fP8UgtsVuaU+wS2yBO2Dxi8FsrW2bMbqTKeMOommtEL7WGVDHn5H+N8Js0d+jHpB5tt8ICOG7iCSxpO5u4E6vXz/FILbFbmlPsEtsgTtg8YvBbK1tmzG6kynjDqJprRC+1hlQx5+R/jfCbNHfox6QebbfCAjhu4gksaTubuBOr18/xSC2xW5pT7BLbIE7YPGLwWytbZsxupMp4w6iaa0QvtYZUMefkf43wmzR36MekHm23wgI4buIJLGk7m7gTq9fP8UgtsVuaU+wS2yBO2Dxi8FsrW2bMbqTKeMOommtEL7WGVDHn5H+N8Js0d+jHpB5tt8ICOG7iCSxpO5u4E="

#partialy decrypted plaintext

plaintext = "*****n**M****H***j***Wx*******d************h*****3****=*******==******t**F**M**f***hM************3***H*w**J*********=**==*******U******E**95**V*c*N****5**t*M*****J*c*Q*****c*h5**0******==*==****NUR*******************X2*u*H**Y************G**P****=***********0*****************************f***5****OX*********=*******=****"

I decode the base64 ciphertext, and found that there're 320 bytes data, which is same as the plaintext. And then I try to decrypt the whole plaintext, but unfortunately I failed -_- (I suck at crypto !). So I send this message to one of my teammate, who is good at it.

He found out that the actual ciphertext & plaintext were both only 64 bytes, they just repeat themselves 5 times (64*5 = 320). So he splitted the plaintext into 5 groups, observed the known plaintext ,do the cross-comparison and complete the whole plaintext:

U0NURntEMF95MHVfcjNhMWx5X2tuMHdfY3J5cHQwOXJhcGh5P30=============

It was another base64-encode string. So he base64-decode the whole string:

SCTF{D0_y0u_r3a1ly_kn0w_crypt09raphy?}

Thank goodness! Praise Jesus!
Finally, we get the flag!!!

Comments

comments powered by Disqus