Monday, May 22, 2017

RCTF 2017 - Crackme 714 pts Writeup


Crackme 714 pts (9 solves) :

The crackme is an MFC application :

 

We can locate the routine of interest by setting a breakpoint on GetWindowTextW. Keep in mind that the input is in Unicode.
Later on, we find that the program generates two distinct blocks of values. These are generated from hard-coded values independently from the user input, so they're always the same. We call the first one : static_block1 and the second static_block2.
Then, there's the call to the encrypt function which takes static_block1 as an argument.
 
The encrypted block will then be XORed with static_block2.
We also find a reference to the encrypted valid key here, which we can extract easily during runtime :

 
The loop above performs a double-word by double-word comparison of the encrypted user input with the encrypted valid key that already came with the crackme.

In order to solve the challenge we need to reverse engineer the encrypt function and do the exact reverse. We also don't have to forget the XOR that is done with static_block2. For that matter, we supply to the decrypt function (the one we need to write) encrypted_valid_key XOR static_block2.

The script below has my implementation of the decrypt function, it outputs the key to flag.txt :

/*
RCTF - 2017
Author : SOUHAIL HAMMOU
Crackme 714 pts (9 solves)
Description :
============
Please submit the flag like RCTF{flag}
https://static2017.teamrois.cn/re_b889ffe02c96c38274f76c67f8a1ddf3/crackme_63074830f0b1b6b4fff6ad910bea34fc.zip
*/
#include <stdio.h>
#include <stdlib.h>
#define __ROL__(x, y) _rotl(x, y)
inline unsigned int __ROL4__(unsigned int value, int count) { return __ROL__((unsigned int)value, count); }
inline unsigned int __ROR4__(unsigned int value, int count) { return __ROL__((unsigned int)value, -count); }
typedef unsigned char BYTE;
typedef unsigned int DWORD;
void _decrypt(DWORD* input)
{
BYTE static_block_2[] = {
0xCA,0xF4,0x60,0x16,0xDC,0xB7,0x2F,0x71,0x3D,0xEA,0x7D,0xF3,0xC8,0x2E,0xA8,0x17,
0x2E,0x3A,0x47,0xCE,0x33,0x85,0xE3,0x10,0x4F,0xB1,0x85,0x5D,0x87,0xB0,0x05,0x84,
0xFC,0xCA,0x8F,0x89,0xF4,0x36,0x17,0xBD,0xE1,0x8D,0xF1,0x89,0x5B,0x3F,0x37,0xE4,
0x27,0x26,0xEF,0xD1,0x6A,0xDF,0xB1,0xDA,0xF0,0x63,0x04,0x8F,0xD7,0x79,0x50,0x22,
0x4D,0xD4,0x66,0xD0,0xE1,0xE0,0xCC,0x12,0x3A,0xFB,0xE5,0x04,0xDA,0x24,0x62,0xCA,
0x33,0xC5,0xD4,0x95,0x4D,0xA8,0x81,0x81,0x44,0x20,0xF3,0xBA,0x39,0x77,0x26,0x44,
0xF7,0xBD,0x61,0x6B,0x6C,0x84,0x1D,0x25,0x38,0x73,0x5F,0x76,0xD1,0xE0,0x2B,0x57,
0x1B,0xC2,0x9B,0x36,0xF6,0x23,0xBA,0x82,0xAA,0xF6,0x72,0x4F,0xD3,0xB1,0x67,0x18
};
DWORD prev_1, prev_2;
int ROL_1_res;
int ROL_2_res;
int ROL_4_res;
int ROL_3_res;
input = input + 1;
for (unsigned int n = 0; n < 4; n++)
{
*input += 0x5BF76637;
input[2] += 0x4748DA7A;
int i = 0;
int j = 4;
do
{
//Deduce the ROL values
ROL_2_res = __ROL4__(*input * (2 * *input + 1), 8);
ROL_4_res = ROL_2_res ^ *(input - 1);
ROL_1_res = __ROL4__(input[2] * (2 * input[2] + 1), 8);
ROL_3_res = input[1] ^ ROL_1_res;
///// The values of the 4 double-words in the next loop :
///// next loop's *(ptr-1) is current *ptr
///// next loop's ptr[1] is ptr[2]
///// next loop's ptr[2] is calculated below via interm
///// next loop's *ptr is calculated below via interm2
DWORD interm = __ROR4__(ROL_4_res, 32 - (ROL_1_res & 0x1F));
prev_2 = interm + ((DWORD*)static_block_2)[i / 4];
DWORD interm2 = __ROR4__(ROL_3_res, 32 - (ROL_2_res & 0x1F));
prev_1 = interm2 + ((DWORD*)static_block_2)[j >> 2];
//now that we have all the necessary values, write them to the array for the next loop
*(input - 1) = *input;
*input = prev_1;
input[1] = input[2];
input[2] = prev_2;
i += 8;
j += 8;
} while (j <= 124);
*(input - 1) += 0x7FAF076D;
input[1] -= 0x642805B4;
input += 4;
}
}
int main()
{
FILE* fp = _wfopen(L"flag.txt", L"w, ccs=UTF-16LE");
/*
Result of :
AF C7 E8 A9 8F 75 75 5E 51 3D 9D 5E AD 88 8E 1D <=
7F 78 F2 70 E3 E1 12 9F AF 11 8E D9 2F DF 54 DB <= static block 1
C1 11 1B D5 12 B2 9E 82 1B 12 0B 86 44 60 26 B8 <=
4D 9C 20 73 AF A3 C2 AB B8 17 DC EB 22 C3 4D E6 <=
XOR
7d 34 13 f9 89 07 0e ff a1 3b f8 fa 30 14 9a b7 <=
00 48 29 78 f6 32 3c 31 db 1a c1 c6 98 2e 7f ba <= XORed encrypted key
4c e9 27 81 ce fe 23 5d 06 42 7c 50 d3 07 e4 56 <=
99 ea a6 d2 36 9d a5 52 af 75 82 cc da 14 59 d9 <=
= key's value after the _encrypt routine (sub_013A1040); i.e : it's ready for decryption now
*/
BYTE key[] = {
0x7d,0x34,0x13,0xf9,0x89,0x07,0x0e,0xff,0xa1,0x3b,0xf8,0xfa,0x30,0x14,0x9a,0xb7,
0x00,0x48,0x29,0x78,0xf6,0x32,0x3c,0x31,0xdb,0x1a,0xc1,0xc6,0x98,0x2e,0x7f,0xba,
0x4c,0xe9,0x27,0x81,0xce,0xfe,0x23,0x5d,0x06,0x42,0x7c,0x50,0xd3,0x07,0xe4,0x56,
0x99,0xea,0xa6,0xd2,0x36,0x9d,0xa5,0x52,0xaf,0x75,0x82,0xcc,0xda,0x14,0x59,0xd9
};
_decrypt((DWORD*)key);
//
// Decrypted key : 你好,欢迎进入rctf2017,希望你学到东西。
//
fwprintf(fp,(wchar_t*)key);
}
All we need to do now, is provide the decrypted key and the flag will be displayed.

The flag is : RCTF{rtf2017crackmebyw31y1l1n9}

See you again soon
Follow me on Twitter : here