Can you crack it? + Solution

Thanks to a facebook message from my dad yesterday, I was informed of this website: Can you Crack it?. So, promptly, I got onto the job and it was surprisingly easy and I imagine it will be for most people who can reverse engineer and has experience doing so.

Click read more to see how I did it, but I suggest you have a good attempt beforehand. It’s a nice little reverse engineering exercise.

SPOILER – THIS IS THE SOLUTION. RUN AWAY AND HIDE IF YOU WANT TO HAVE A GO YOURSELF.


Stage 1 – Reverse engineering and decryption

Ok, so from the main page, I wrote out all the hexadecimal into a binary file. Like this:

EB04AFC2BFA381EC 0001000031C9880C
0CFEC175F931C0BA EFBEADDE02040C00
D0C1CA088A1C0C8A 3C04881C04883C0C
FEC175E8E95C0000 0089E381C3040000
005C583D41414141 7543583D42424242
753B5A89D189E689 DF29CFF3A489DE89
D189DF29CF31C031 DB31D2FEC0021C06
8A14068A341E8834 0688141E00F230F6
8A1C168A1730DA88 17474975DE31DB89
D8FEC0CD809090E8 9DFFFFFF41414141

I sat around for a good few minutes just reading the hex. However, I noticed something! “EFBEADDE”. This is the little endian storage of “0xDEADBEEF”. Tada, it’s probably code. So shoving it into a disassembler, I get some nasty x86 code. After whimpering at the sight of it, I cracked on and reversed engineered the code into lovely C.

But there was something missing! In the x86, it does a near call which pushes the return address onto the stack. This sneaky little program then pops this off the stack and then sets it as the new top of stack. After the return address, a sneaky pop loads 0x41414141, the last 32 bit value in the file, and then checks it does equal that. Then, it does another pop… wait a second. There is no more defined data, and it is looking for a 0x42424242. So, realising I copied the HEX wrong, I set about correcting it. Except, I didn’t copy it wrong, the data was truely missing! I checked the site source for any html comments; nothing. After downloading the png image on the website (the image with the hex data), I open it up in a hex editor, and I recognise a base64 encoded message in the comments section which indeed turns out to be the missing data!

So further analysis proved that I have all the data required to decrypt and complete this puzzle. I wrote this program to do it:

/*
	http://www.canyoucrackit.co.uk decrypter (and encrypter)
	Reverse Engineered by Davee

	http://lolhax.org
	02/12/2011
*/

#include 
#include 

#include 

typedef uint32_t u32;

typedef uint8_t u8;

#define TABLE_SIZE (0x100)

u8 g_ciphertext[] = 
{ 
	0x91, 0xD8, 0xF1, 0x6D, 0x70, 0x20, 0x3A, 0xAB, 
	0x67, 0x9A, 0x0B, 0xC4, 0x91, 0xFB, 0xC7, 0x66, 
	0x0F, 0xFC, 0xCD, 0xCC, 0xB4, 0x02, 0xFA, 0xD7, 
	0x77, 0xB4, 0x54, 0x38, 0xAB, 0x1F, 0x0E, 0xE3, 
	0x8E, 0xD3, 0x0D, 0xEB, 0x99, 0xC3, 0x93, 0xFE, 
	0xD1, 0x2B, 0x1B, 0x11, 0xC6, 0x11, 0xEF, 0xC8, 
	0xCA, 0x2F, 
};

static __inline__ u32 rotr(u32 data, u32 bits)
{
	/* rotate right */
	return ((data >> bits) | ((data & ((1 << bits) - 1)) << (32 - bits)));
}

void decrypt_data(u8 *table, u8 *data, u32 size)
{
	int i;
	u8 bl = 0, dl = 0, dh = 0;

	/* do the mangle algorithm */
	for (i = 0; i < size; i++)
	{
		/* read the table and swap bytes */
		dl = table[i + 1];
		dh = table[(bl + dl) & 0xFF];
		table[i + 1] = dh;
		table[(bl + dl) & 0xFF] = dl;

		/* set bl */
		bl = table[(dl + dh) & 0xFF];

		/* do the decrypt (or encrypt...) of the data */
		data[i] ^= bl;
	}
}

void generate_table(u8 *table, u32 seed)
{
	int i;
	u8 seed_indx = 0;

	/* stage 1: set table value as index respectfully */
	for (i = 0; i < TABLE_SIZE; i++) table[i] = i;

	/* stage 2: seed the table */
	for (i = 0; i < TABLE_SIZE; i++)
	{
		/* update seed index */
		seed_indx += (table[i] + (seed & 0xFF));

		/* rotate the seed 8 bits right */
		seed = rotr(seed, 8);

		/* backup element */
		u8 temp = table[i];

		/* byte swap */
		table[i] = table[seed_indx];
		table[seed_indx] = temp;
	}
}

int main(void)
{
	int i;
	u8 table[TABLE_SIZE];

	/* do decrypt etc */
	printf("Decrypter, by Davee\nhttp://lolhax.org\n\n");

	/* generate the table */
	generate_table(table, 0xDEADBEEF); //0xAFC2BFA3); //3A3BFC2AF);//0xDEADBEEF);

	/* decrypt data */
	decrypt_data(table, g_ciphertext, sizeof(g_ciphertext));

	/* write decrypted data */
	FILE *fd = fopen("decrypt.bin", "wb");

	/* check for error */
	if (fd == NULL)
	{
		/* rage */
		return printf("fuuuuuuuuuuuuuuuuu (aka cant open decrypt.bin)\n");
	}

	/* write */
	fwrite(g_ciphertext, 1, sizeof(g_ciphertext), fd);
	fclose(fd);

	printf("done. check out decrypt.bin\n");
	getchar();
	return 0;
}

It successfully decrypted the message and decrypt.bin contained:

“GET /15b436de1f9107f3778aad525e5d0b20.js HTTP/1.1.”.

Ok, maybe I’m not done, following this GET request I got to “stage 2”. A VM in javascript.


Stage 2 – Javascript VM

This, is kind of like an emulator, you get a description of a “processor” and you follow the specification. If you do it correctly, you get the answer, easy.

I started off simply decoding the instructions and writing them to a file, like a disassembly. Then once I was happy that it was decoding it correctly, I scrapped together a simple tool interpret the instructions and then dump everything. Code below:

/*
	http://www.canyoucrackit.co.uk VM decoder + interpreter
	Written by Davee

	http://lolhax.org
	02/12/2011
*/
#include 
#include 


typedef uint32_t u32;
typedef uint8_t u8;

u8 prog[] =
{
    0x31, 0x04, 0x33, 0xaa, 0x40, 0x02, 0x80, 0x03, 0x52, 0x00, 0x72, 0x01, 0x73, 0x01, 0xb2, 0x50,
    0x30, 0x14, 0xc0, 0x01, 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

    0x98, 0xab, 0xd9, 0xa1, 0x9f, 0xa7, 0x83, 0x83, 0xf2, 0xb1, 0x34, 0xb6, 0xe4, 0xb7, 0xca, 0xb8,
    0xc9, 0xb8, 0x0e, 0xbd, 0x7d, 0x0f, 0xc0, 0xf1, 0xd9, 0x03, 0xc5, 0x3a, 0xc6, 0xc7, 0xc8, 0xc9,
    0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
    0xda, 0xdb, 0xa9, 0xcd, 0xdf, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
    0x26, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
    0x7d, 0x1f, 0x15, 0x60, 0x4d, 0x4d, 0x52, 0x7d, 0x0e, 0x27, 0x6d, 0x10, 0x6d, 0x5a, 0x06, 0x56,
    0x47, 0x14, 0x42, 0x0e, 0xb6, 0xb2, 0xb2, 0xe6, 0xeb, 0xb4, 0x83, 0x8e, 0xd7, 0xe5, 0xd4, 0xd9,
    0xc3, 0xf0, 0x80, 0x95, 0xf1, 0x82, 0x82, 0x9a, 0xbd, 0x95, 0xa4, 0x8d, 0x9a, 0x2b, 0x30, 0x69,
    0x4a, 0x69, 0x65, 0x55, 0x1c, 0x7b, 0x69, 0x1c, 0x6e, 0x04, 0x74, 0x35, 0x21, 0x26, 0x2f, 0x60,
    0x03, 0x4e, 0x37, 0x1e, 0x33, 0x54, 0x39, 0xe6, 0xba, 0xb4, 0xa2, 0xad, 0xa4, 0xc5, 0x95, 0xc8,
    0xc1, 0xe4, 0x8a, 0xec, 0xe7, 0x92, 0x8b, 0xe8, 0x81, 0xf0, 0xad, 0x98, 0xa4, 0xd0, 0xc0, 0x8d,
    0xac, 0x22, 0x52, 0x65, 0x7e, 0x27, 0x2b, 0x5a, 0x12, 0x61, 0x0a, 0x01, 0x7a, 0x6b, 0x1d, 0x67,
    0x75, 0x70, 0x6c, 0x1b, 0x11, 0x25, 0x25, 0x70, 0x7f, 0x7e, 0x67, 0x63, 0x30, 0x3c, 0x6d, 0x6a,
    0x01, 0x51, 0x59, 0x5f, 0x56, 0x13, 0x10, 0x43, 0x19, 0x18, 0xe5, 0xe0, 0xbe, 0xbf, 0xbd, 0xe9,
    0xf0, 0xf1, 0xf9, 0xfa, 0xab, 0x8f, 0xc1, 0xdf, 0xcf, 0x8d, 0xf8, 0xe7, 0xe2, 0xe9, 0x93, 0x8e,
    0xec, 0xf5, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

    0x37, 0x7a, 0x07, 0x11, 0x1f, 0x1d, 0x68, 0x25, 0x32, 0x77, 0x1e, 0x62, 0x23, 0x5b, 0x47, 0x55,
    0x53, 0x30, 0x11, 0x42, 0xf6, 0xf1, 0xb1, 0xe6, 0xc3, 0xcc, 0xf8, 0xc5, 0xe4, 0xcc, 0xc0, 0xd3,
    0x85, 0xfd, 0x9a, 0xe3, 0xe6, 0x81, 0xb5, 0xbb, 0xd7, 0xcd, 0x87, 0xa3, 0xd3, 0x6b, 0x36, 0x6f,
    0x6f, 0x66, 0x55, 0x30, 0x16, 0x45, 0x5e, 0x09, 0x74, 0x5c, 0x3f, 0x29, 0x2b, 0x66, 0x3d, 0x0d,
    0x02, 0x30, 0x28, 0x35, 0x15, 0x09, 0x15, 0xdd, 0xec, 0xb8, 0xe2, 0xfb, 0xd8, 0xcb, 0xd8, 0xd1,
    0x8b, 0xd5, 0x82, 0xd9, 0x9a, 0xf1, 0x92, 0xab, 0xe8, 0xa6, 0xd6, 0xd0, 0x8c, 0xaa, 0xd2, 0x94,
    0xcf, 0x45, 0x46, 0x67, 0x20, 0x7d, 0x44, 0x14, 0x6b, 0x45, 0x6d, 0x54, 0x03, 0x17, 0x60, 0x62,
    0x55, 0x5a, 0x4a, 0x66, 0x61, 0x11, 0x57, 0x68, 0x75, 0x05, 0x62, 0x36, 0x7d, 0x02, 0x10, 0x4b,
    0x08, 0x22, 0x42, 0x32, 0xba, 0xe2, 0xb9, 0xe2, 0xd6, 0xb9, 0xff, 0xc3, 0xe9, 0x8a, 0x8f, 0xc1,
    0x8f, 0xe1, 0xb8, 0xa4, 0x96, 0xf1, 0x8f, 0x81, 0xb1, 0x8d, 0x89, 0xcc, 0xd4, 0x78, 0x76, 0x61,
    0x72, 0x3e, 0x37, 0x23, 0x56, 0x73, 0x71, 0x79, 0x63, 0x7c, 0x08, 0x11, 0x20, 0x69, 0x7a, 0x14,
    0x68, 0x05, 0x21, 0x1e, 0x32, 0x27, 0x59, 0xb7, 0xcf, 0xab, 0xdd, 0xd5, 0xcc, 0x97, 0x93, 0xf2,
    0xe7, 0xc0, 0xeb, 0xff, 0xe9, 0xa3, 0xbf, 0xa1, 0xab, 0x8b, 0xbb, 0x9e, 0x9e, 0x8c, 0xa0, 0xc1,
    0x9b, 0x5a, 0x2f, 0x2f, 0x4e, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

typedef struct
{
	u8 ip;

	u8 r[6];

	u8 fl;

	u32 firmware[2];
} cpu_info;

cpu_info cpu =
{
	0x00, 
	{ 0x00, 0x00, 0x00, 0x00,
	0x00, 0x10 }, //cs, ds

	0x00,
	{ 0xd2ab1f05, 0xda13f110 },
};

void displayDebug(void)
{
	printf("-----------\nDEBUG INFO:\nip = 0x%02X\n", cpu.ip);

	int i;
	for (i = 0; i < 4; i++) 	{ 		printf("r%i = 0x%02X\n", i, cpu.r[i]); 	} 	 	printf("cs = 0x%02X\n", cpu.r[4]); 	printf("ds = 0x%02X\n", cpu.r[5]); 	 	printf("fl = 0x%02X\n------------\n", cpu.fl); } void execute(FILE *fd, u8 *prog, int debug) { 	int halt = 0; 	int loop = 0; 	int break_on = 0; 	printf("Starting execution...\n"); 	 	while (!halt) 	{ 		u8 cs = cpu.r[4]; 		u32 address = (cs * 0x10) + cpu.ip; 		 		u8 inst = prog[address]; 		 		u8 opcode = (inst >> 5) & 0x7;
		u8 mod = (inst >> 4) & 0x1;
		u8 op1 = (inst & 0xF);
		u8 op2 = prog[address+1];

		if (debug)
		{
                  if (loop)
                  {
                           loop--;
                           }
                           else
                           {
			u8 chr = getchar();

			if (chr == 'r')
			{
				displayDebug();
			}

			if (chr == 'l')
			{
                    loop = 0x10;
                    }

			if (chr == 't')
			{
				loop = 0x50;
			}

			if (chr == 'b')
			{
				break_on = 0x100;
			}
                    }
		}

		if (break_on)
		{
			if ((cs*0x10 + cpu.ip) == break_on)
			{
				loop = 0;
				break_on = 0;
				printf("Break at 0x100\n");
			}
		}
		printf("0x%08X: ", (cs*0x10) + cpu.ip);

		u8 prev_ip = cpu.ip;

		switch (opcode)
		{
			case 0: //jmp
			{
				if (mod)
				{
					printf("jmp 0x%X:r%i" "\n", op2, op1);
					cpu.r[4] = op2;
					cpu.ip = cpu.r[op1];
				}
				else
				{
					printf("jmp r%i" "\n", op1);
					cpu.ip = cpu.r[op1];
				}

				break;
			}

			case 1:
			{
				if (mod)
				{
					printf("movr r%i, 0x%X" "\n", op1, op2);
					cpu.r[op1] = op2;
				}
				else
				{
					printf("movr r%i, r%i" "\n", op1, op2);
					cpu.r[op1] = cpu.r[op2];
				}
				cpu.ip += 2;
								if (cpu.ip < 2 && prev_ip >= 2)
				{
					cpu.r[4] += 0x10;
				}
				break;
			}

			case 2:
			{
				if (mod)
				{
					printf("movm [ds:r%i], r%i" "\n", op1, op2);
					prog[(cpu.r[5]*0x10) + cpu.r[op1]] = cpu.r[op2];
				}
				else
				{
					printf("movm r%i, [ds:r%i]" "\n", op1, op2);
					cpu.r[op1] = prog[(cpu.r[5]*0x10) + cpu.r[op2]];
				}
				cpu.ip += 2;
								if (cpu.ip < 2 && prev_ip >= 2)
				{
					cpu.r[4] += 0x10;
				}
				break;
			}
			case 3:
			{
				if (mod)
				{
					printf("add r%i 0x%X" "\n", op1, op2);
					cpu.r[op1] += op2;
				}
				else
				{
					printf("add r%i, r%i" "\n", op1, op2);
					cpu.r[op1] += cpu.r[op2];
				}

				cpu.ip += 2;
				if (cpu.ip < 2 && prev_ip >= 2)
				{
					cpu.r[4] += 0x10;
				}				break;
			}
			case 4:
			{
				if (mod)
				{
					printf("xor r%i, 0x%X" "\n", op1, op2);
					cpu.r[op1] ^= op2;
				}
				else
				{
					printf("xor r%i, r%i" "\n", op1, op2);
					cpu.r[op1] ^= cpu.r[op2];
				}

				cpu.ip += 2;
								if (cpu.ip < 2 && prev_ip >= 2)
				{
					cpu.r[4] += 0x10;
				}
				break;
			}

			case 5:
			{
				if (mod)
				{
					printf("cmp r%i, 0x%X" "\n", op1, op2);
					if (cpu.r[op1] < op2) 					{ 						cpu.fl = -1; 					} 					 					else if (cpu.r[op1] > op2)
					{
						cpu.fl = 1;
					}

					else
					{
						cpu.fl = 0;
					}
				}
				else
				{
					printf("cmp r%i, r%i" "\n", op1, op2);

					if (cpu.r[op1] < cpu.r[op2]) 					{ 						cpu.fl = -1; 					} 					 					else if (cpu.r[op1] > cpu.r[op2])
					{
						cpu.fl = 1;
					}

					else
					{
						cpu.fl = 0;
					}
				}

				cpu.ip += 2;
				if (cpu.ip < 2 && prev_ip >= 2)
				{
					cpu.r[4] += 0x10;
				}
				break;
			}

			case 6:
			{
				if (mod)
				{
					printf("jmpe 0x%X:r%i" "\n", op2, op1);

					if (cpu.fl == 0)
					{
						cpu.r[4] = op2;
						cpu.ip = cpu.r[op1];
					}
					else
					{
						cpu.ip++;
										if (cpu.ip == 0)
				{
					cpu.r[4] += 0x10;
				}
					}
				}
				else
				{
					printf("jmpe r%i" "\n", op1);

					if (cpu.fl == 0)
					{
						cpu.ip = cpu.r[op1];
					}
					else
					{
						cpu.ip++;
										if (cpu.ip == 0)
				{
					cpu.r[4] += 0x10;
				}
					}
				}

				//i++;
				//cpu.
				break;
			}
			case 7:
			{
				if (mod)
				{
					printf("hlt" "\n");
				}
				else
				{
					printf("hlt" "\n");
				}

				halt = 1;
				cpu.ip++;

				if (cpu.ip == 0)
				{
					cpu.r[4] += 0x10;
				}

				break;
			}
		}
	}

	printf("Execution terminated\n");
}

void disassemble(FILE *fd, u8 *prog, u32 prog_size)
{
	int i = 0;

	while (1)
	{
		u8 opcode = (prog[i] >> 5) & 0x7;
		u8 mod = (prog[i] >> 4) & 0x1;
		u8 op1 = (prog[i] & 0xF);

		printf("0x%08X: ", i);
		int i_prev = i;
		switch (opcode)
		{
			case 0: //jmp
			{
				if (mod)
				{
					printf("jmp 0x%X:r%i" "\n", prog[i+1], op1);
					i += 2;
				}
				else
				{
					printf("jmp r%i" "\n", op1);
					i++;
				}

				break;
			}

			case 1:
			{
				if (mod)
				{
					printf("movr r%i, 0x%X" "\n", op1, prog[i+1]);
				}
				else
				{
					printf("movr r%i, r%i" "\n", op1, prog[i+1]);
				}

				i += 2;
				break;
			}

			case 2:
			{
				if (mod)
				{
					printf("movm [ds:r%i], r%i" "\n", op1, prog[i+1]);
				}
				else
				{
					printf("movm r%i, [ds:r%i]" "\n", op1, prog[i+1]);
				}

				i += 2;
				break;
			}
			case 3:
			{
				if (mod)
				{
					printf("add r%i 0x%X" "\n", op1, prog[i+1]);
				}
				else
				{
					printf("add r%i, r%i" "\n", op1, prog[i+1]);
				}

				i += 2;
				break;
			}
			case 4:
			{
				if (mod)
				{
					printf("xor r%i, 0x%X" "\n", op1, prog[i+1]);
				}
				else
				{
					printf("xor r%i, r%i" "\n", op1, prog[i+1]);
				}

				i += 2;
				break;
			}
			case 5:
			{
				if (mod)
				{
					printf("cmp r%i, 0x%X" "\n", op1, prog[i+1]);
				}
				else
				{
					printf("cmp r%i, r%i" "\n", op1, prog[i+1]);
				}

				i += 2;
				break;
			}
			case 6:
			{
				if (mod)
				{
					printf("jmpe r%i:r%i" "\n", prog[i+1], op1);
					i += 2;
				}
				else
				{
					printf("jmpe r%i" "\n", op1);
					i++;
				}

				//i++;
				break;
			}
			case 7:
			{
				if (mod)
				{
					printf("hlt" "\n");
				}
				else
				{
					printf("hlt" "\n");
				}

				i++;
				break;
			}
		}
	}
}

int main()
{
	FILE *fd =fopen("output_hex.bin", "wb");
	//disassemble(fd, prog, sizeof(prog));
	execute(fd, prog, 0);
	fwrite(prog, 1, sizeof(prog), fd);
	fclose(fd);

	return 0;
}

Then, the output_hex.bin contained another GET method:

“GET /da75370fe15c4148bd4ceec861fbdaa5.exe HTTP/1.0”.

Ok, cool.


Stage 3 – License check

After downloading and having a quick peek at the assembly, I saw it didn’t do that much. After running, it moaned about no hostname, so naturally, I set it to “canyoucrackit.co.uk” BAM, it screamed at me again, complaining about a license.txt.

I fully disassembled the executable and I quickly found the check, it was doing a scanf of a string from the license onto the stack and performing a check of the first 4 hex bytes.

This check looked for the values 67 63 68 71 in LE. This, translates to gchq, a UK government organisation. Regardless, I stormed through the rest of the code and saw that it does a “crypt” call on the license + 4, with a salt (or key or w/e).

char *c = crypt(license+4, "hqDTK7b8K2rvw");

if (strcmp(c, "hqDTK7b8K2rvw") == 0)
{
	valid_license = 1;
}

Now, I know for a fact that crypt is a one-way function, so I didn’t bother with figuring out the original license text needed. If you guys know me, I like exploits. I saw one earlier on aswell. I jumped right back to the “scanf” call onto the stack and checked if I can cause a buffer overflow. It turned out I could! valid_license was stored further on the stack, so overflowing with a big string can set valid_license to non-zero passing that check! huzzah!

I used this license:

gchq------------lolhax.org------Davee-----------

So now, running the application I got this result:

keygen.exe

loading stage1 license key(s)… loading stage2 license key(s)…

request:

GET /hqDTK7b8K2rvw/2d2d2d2d/686c6f6c/6f2e7861/key.txt HTTP/1.0

response:

HTTP/1.1 404 Not Found Content-Type: text/html; charset=us-ascii Server: Microsoft-HTTPAPI/2.0 Date: Sat, 02 Dec 2011 23:44:59 GMT Connection: close Content-Length: 315

Not Found HTTP Error 404. The requested resource is not found.

Error, 404, odd. It tried to request “GET /hqDTK7b8K2rvw/2d2d2d2d/686c6f6c/6f2e7861/key.txt HTTP/1.0”. I recognise those HEX values. The first one looked the the hash check, the 2d2d2d2d, 686c6f6c and 6f2e7861 looked like data out of my license file. After confirming with the assembly, this information was true. the format of the license was:

[4 bytes header]
[8 bytes password]
[4 bytes first hex]
[4 bytes second hex]
[4 bytes third hex]
[0x18 bytes to bypass check]

Now, what the hell were these 4 bytes? They weren’t inside the application. I sat around, stressing over what these numbers are. I guessed a few of course, no luck. After a nice chill and a cup of tea, it struck me. There was a spare value in the first executable, which the program just jumped over. There was also the VM’s firmware version which was not used. These 3 unreferenced values may be the answer!

So I plugged them in, and what do you know! I don’t get the answer. Such a perfect scenario, but I still fall victim to this challenge.

Later on though, I thought I should try plug it into the browser… well, what do you know. It worked.

Pr0t3ct!on#cyber_security@12*12.2011+

Link


Completed

The winners’ page takes you to an application form to apply for a position within GCHQ. Shame I don’t have a degree yet 😛

How about that eh? It’s a nice little challenge and I hoped you all attempted it your best before reading this!