26
Jan
2012

MozillaCTF 2012 – Awesome Corp. Secured Ranges

This challenge entails reversing two (packed) Windows executables in order to retrieve an encrypted message. Once the algorithm and key generation method have been determined, a bruteforce search within a limited keyspace yields the valid key.

Soon enough (insert random webhacking stuff here), you get access to an administrators inbox. It seems like they use a custom authentication system which manages access to internal resources like SVN servers and such. It is called ‘Secured Range’ and is in use since January 2011, as the logs state. All you manage to retrieve before an administrator throws you out of the system are two binaries of their login system:

AwsmCrp.PRKG-for-Secured-Ranges.exe
AwsmCrp.Auth-Token-Retrieval.exe

The first seems to update masterkeys every few months, the latter produces authentication tokens for the employees. Try to get hold of the system’s current master key to solve this challenge.

A quick tryout of the two executables gives us an idea of how they interact. First, the PRKG:

AwesomeCorp. PRKG for Secured Ranges (c)
Thu Jan 26 16:01:57 2012

Program version 1.45 build 147

c3bdcb43
c8830075
d8e5760a
374147f3

The key has been generated.

The PRKG appears to generate a 128-byte bits key, which is printed to the console and output to srange.key.

The ‘Auth Token Retrieval’ executable expects a srange keyfile as it’s sole argument. However, the key we’ve generated is invalid:

AwesomeCorp. Authentication Token (c) Retrieval
Thu Jan 26 16:02:24 2012

Program version 1.04 build 245

Application uses Secured Range (c) technology. Pass a valid key to use the appli
cation or ask your local administrator.

Using CFF Explorer’s identifier feature, we learn that both binaries have been packed using PESpin:


File Name: C:\Users\Admin\Desktop\AwsmCrp.PRKG-for-Secured-Ranges.exe
Number of Matching Signatures: 1
Deep Scan: Yes
Best Match: PE Spin v0.4x

Regardless, there doesn’t seem to be any noticable antidebug, just ignore the exceptions generated and both executables run fine in OllyDbg.

Let’s start by reversing the PRKG. Load it up in Olly and hit Shift-F9. Once PESpin has done its magic you should be able to easily locate the key generation procedure (0x0040153E) by searching for strings.

Here’s the interesting part:

The PRKG (Pseudo Random Key Generator, I suppose) algorithm LCG resembles the Microsoft VC implementation (with a slight twist). As program execution reaches the RNG loop, EAX reflects the RNG seed, which is initialized from time(NULL), the current thread ID and 24-bits from GetCurrentTickCount().

void gen_key(uint32_t state, uint32_t *key)
{
    uint32_t rand;
    int i, j;

    for(i = 0; i < 4; i++) {
        rand = 0;

        for(j = 0; j < 4; j++) {
            state = (state * 0x343FD) + 0x269EC3;
            rand = (rand << 8) + ((state >> 16) & 0x7FFF);
        }

        key[i] = rand;
    }
}

As stated in the challenge description, the PRKG has supposedly been in use since January 2011 (~ a year), which reduces the RNG seeds’ space to 2^25 (roughly the number of seconds in a year). [1]

Now we know how the Secured Range keys are generated, let’s move on to the second executable. In a similar fashion to the PRKG you should be able to track down main() at 0x004014F8. The full disassembly is a bit long and not that interesting, so I’ll summarize what’s going on:

  1. The key is loaded from the filename supplied as a parameter
  2. XTEA is used to decrypt an embedded 112-byte payload
  3. The resulting plaintext is then checked against a set of character constraints

For step 2 there are two small twists to classic XTEA. First, the full decryption is performed using two ‘decrypt’ operations (32 & 7 rounds, respectively). Secondly the XTEA delta constant was replaced to fool crypto scanners (ironically, this fact was found by googling the delta).

The character constraints allow us to verify whether a decryption operation was successful. Combined with the limited RNG seed space [1] this allows us to launch a brute-force attack to retrieve the XTEA key and subsequently the plaintext.

An implementation of the key brute force (and payload decryption, we’re lazy after all) can be found at the bottom of this post. To wrap things up:

$ ./cup_of_xtea 
Seed: 4ea80f8e (1319636878)

Did you know? The solution (an act) was introduced the very same day your Secure Range (c) key was generated.   

As the seed approximates the (unix) time at which the seed was generated, let’s just convert it to something readable: Wed, 26 Oct 2011 13:47:58 GMT

An act? SOPA, PIPA and ACTA spring to mind. Wikipedia confirms that SOPA was introduced on October the 26nd. The bill’s name in full is the solution of this challenge: Stop Online Piracy Act

Source code (gcc cup_of_xtea.c -ocup_of_xtea -O3):

/**
* MozillaCTF 2012 Awesome Corp. Secured Ranges bruteforcer
* by ius/eindbazen
*/

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define TRIAL_SIZE 16

#define SEED_START 1293840000
#define SEED_END   1325396926

const unsigned char data[112] = {
    0xD7, 0x69, 0xDC, 0x2A, 0xAA, 0x72, 0x21, 0x83,
    0x57, 0xF3, 0x48, 0x3D, 0x76, 0xA7, 0x65, 0x5C,
    0x86, 0x98, 0xFD, 0xB0, 0x7E, 0x6B, 0xB4, 0xAC,
    0x02, 0xDD, 0x69, 0x35, 0xF9, 0xCB, 0xA1, 0x83,
    0x09, 0x44, 0x6D, 0x6F, 0xF2, 0x1B, 0xE6, 0x29,
    0x07, 0x44, 0xDB, 0xB2, 0xC3, 0xA0, 0xFD, 0x0B,
    0xEC, 0xB2, 0xEF, 0xF5, 0x7A, 0x41, 0xB9, 0x1A,
    0xF3, 0x62, 0xF9, 0xEB, 0x8F, 0xB1, 0x1A, 0x52,
    0x9C, 0xBF, 0xE8, 0xF6, 0x02, 0xA4, 0xC6, 0x0C,
    0xDD, 0xB9, 0xC2, 0x17, 0xC4, 0x43, 0x92, 0xC5,
    0x6B, 0x3F, 0x9E, 0x06, 0xC7, 0x6E, 0x64, 0x23,
    0x9A, 0x26, 0x17, 0x55, 0x0E, 0xCB, 0xE3, 0xE2,
    0x02, 0x64, 0xE4, 0x71, 0xCD, 0x83, 0x63, 0x38,
    0xAA, 0x7A, 0xAF, 0x3F, 0x7B, 0xDB, 0x00, 0x51 
};

void gen_key(uint32_t state, uint32_t *key)
{
    uint32_t rand;
    int i, j;

    for(i = 0; i < 4; i++) {
        rand = 0;

        for(j = 0; j < 4; j++) {
            state = (state * 0x343FD) + 0x269EC3;
            rand = (rand << 8) + ((state >> 16) & 0x7FFF);
        }

        key[i] = rand;
    }
}

/* XTEA with modified delta */
void xtea_decrypt(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
    unsigned int i;
    uint32_t v0=v[0], v1=v[1], delta=0x61C88646, sum=delta*num_rounds;

    for (i=0; i < num_rounds; i++) {
        v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
        sum -= delta;
        v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
    }

    v[0]=v0; v[1]=v1;
}

/* Decrypts multiple blocks using XTEA */
void decrypt(unsigned char *buf, const int length, const uint32_t key[4])
{
    int i;

    memcpy(buf, data, length);

    for(i = 0; i < length; i += 8) {
        xtea_decrypt(7, (uint32_t*)(buf+i), key);
        xtea_decrypt(32, (uint32_t*)(buf+i), key);
    }
}

/* Checks decrypted message constraints */
int validate(unsigned char *data, unsigned int len)
{
    int i;
    
    for(i = 0; i < len; i++) {
        if(!((data[i] >= '?' && data[i] <= 'z') || data[i] == '('
            || data[i] == ')' || data[i] == ' ' || data[i] == '.'))
            return 0;
    }

    return 1;
}

int main(int argc, char *argv[])
{
    uint8_t buf[sizeof(data)+1];
    uint32_t key[4];
    int i;

    memset(buf, 0, sizeof(buf));

    for(i = SEED_START; i < SEED_END; i++) {
        gen_key(i, key);

        /* Lazy coders' optimisation */
        decrypt(buf, TRIAL_SIZE, key);

        if(validate(buf, TRIAL_SIZE)) {
            decrypt(buf, sizeof(data), key);
            printf("Seed: %08x (%d)\n\n%s\n", i, i, buf);
            return 0;
        }
    }

    return 0;
}

Comments are closed.