Eset crackme challenge
Table of Contents
Eset challenges
So far I only worked on the first challenges and it seems that a multitude of solutions for this can be found via Google. The challenges seem to be multiple years and the start page can be found here.
Description
1. Download the crackme.exe program
The program was designed to test your skillset in reverse engineering, which might come in handy when you work at ESET in one of the positions described above.
2. Analyze the crackme.exe program and look for 3 passwords hidden in its programming code
The program can contain hidden files, texts, conditional tasks, protection against debuggers and other pitfalls, which could complicate your arriving at the solution.
Send your analysis to: crackme@eset.com
Don’t worry too much if you can’t make it past all the hurdles and uncover the hidden passwords. Even if you did not find the solution, we still want to see your analysis, most importantly the progression of steps you took. We are interested in your way of thinking.
Challenge 1
The hash for this file is e3ea2ca8a1ab8500c71cba1298a45efaf516a4d8f99e7f1c17db870c9aa344d9.
This is a windows executable file, that asks for a password to decrypt the flag. The main functionality is in the function at 004013f0. This function contains a few anti-debugging tricks, but in this short writeup the flag will be decrypted in a static way with a short binja Python script.
The function at 004013a0 is used to decrypt and reencrypt the instruction text blocks. The code works by calculating the XOR of the current byte with a key, after which the key is modified by adding a value to it.
void mw_decrypt_encrypt(char *buffer, int buffer_len, char key, char key_modifier){
for(int index = 0; index < buffer_len; index++){
buffer[index] = buffer[index] ^ key;
key += key_modifier;
}
}
This method is used 6 times, so 3 blocks of text are decrypted and encrypted again afterwards.
The application asks for a 10 byte long password, 6 equations are given from the code and 4 from the hint, therefore the values for the final decryption function can be calculated.
Longwinded calculations
These can probably just be skipped by using something that solves linear equations (like WolframAlpha).
Initial equtions
in[7] + in[6] = 0xcd
in[8] + in[5] = 0xc9
in[7] + in[6] + in[3] = 0x13a
in[9] + in[4] + in[8] + in[5] = 0x16f
in[1] + in[0] = 0xc2
in[0] + in[1] + in[2] + in[3] + in[4] + in[5] + in[6] + in[7] + in[8] + in[9] = 0x39b
At this point we know part of the password but not all of it. Using the decryption python script for the two blocks that get decrypted and encrypted we get some hints regarding the challenge:
Here is a small binja script that decodes the values and prints the to binjas log window:
def decrypt(start, buf_len, key, key_mod):
out = ""
for i in range(buf_len):
byte_to_decrypt = bv.read_int(start + i , 1)
decode = (key ^ byte_to_decrypt) % 256
key += key_mod
out += chr(decode)
print("Decrypted:", out)
start = 0x418000
buf_len = 0x1f
key = 0x25
key_mod = 3
decrypt(start, buf_len, key, key_mod)
start = 0x418020
buf_len = 0x11
key = 0x16
key_mod = 7
decrypt(start, buf_len, key, key_mod)
start = 0x418038
buf_len = 0x6e
key = 0x12
key_mod = 5
decrypt(start, buf_len, key, key_mod)
Script output:
Decrypted: Please enter valid password :
Decrypted: Wrong password!
Decrypted: !Good work. Little help:
in[8] = 85
in[0] + in[2] = 128
in[4] - in[7] = -50
in[6] + in[9] = 219
Some basic calculations:
in[7] + in[6] = 0xcd
in[7] + in[6] + in[3] = 0x13a
-> 0xcd + in[3] = 0x13a
-> in[3] = 0x6d
in[9] + in[4] + in[8] + in[5] = 0x16f
in[8] + in[5] = 0xc9
-> in[9] + in[4] + 0xc9 = 0x16f
-> in[9] + in[4] = 0xa6
in[0] + in[1] + in[2] + in[3] + in[4] + in[5] + in[6] + in[7] + in[8] + in[9] = 0x39b
in[9] + in[4] + in[8] + in[5] = 0x16f
in[7] + in[6] + in[3] = 0x13a
in[1] + in[0] = 0xc2
-> 0xc2 + in[2] + 0x13a + 0x16f = 0x39b
-> in[2] = 0x39b - 0x13a - 0x16f - 0xc2
-> in[2] = 0x30
in[1] + in[0] = 0xc2
in[2] = 0x30
in[3] = 0x6d
in[9] + in[4] = 0xa6
in[8] + in[5] = 0xc9
in[7] + in[6] = 0xcd
Using the hints we get the following in addition:
in[8] = 0x55
in[0] + in[2] = 0x80
in[4] - in[7] = -50
in[6] + in[9] = 219
in[0] + in[2] = 0x80
in[2] = 0x30
-> in[0] = 0x50
in[1] + in[0] = 0xc2
-> in[1] = 0xc2 - 0x50 = 0x72
Values now:
in[0] = 0x50
in[1] = 0x72
in[2] = 0x30
in[3] = 0x6d
in[4] - in[7] = -50
in[6] + in[9] = 0xdb
in[9] + in[4] = 0xa6
in[8] + in[5] = 0xc9
in[7] + in[6] = 0xcd
0x50 + 0x72 + 0x30 + 0x6d + in[4] + in[5] + in[6] + in[7] + in[8] + in[9] = 0x39b
0x15f + in[4] + in[5] + in[6] + in[7] + in[8] + in[9] = 0x39b
Using a linear equation solver (that in turn used WolframAlpha), nearly the entire equation system can be solved, leaving only two values uncertain.
in[4] = 0x33
in[6] = 0x68
in[7] = 0x65
in[9] = 0x73
in[8] + in[5] = 0xc9
0x15f + 0x33 + in[5] + 0x68 + 0x65 + in[8] + 0x73 = 0x39b
0x2d2 + in[5] +in[8] = 0x39b
-> in[5] + in[8] = 0xc9
in[0] = 0x50 = P
in[1] = 0x72 = r
in[2] = 0x30 = 0
in[3] = 0x6d = m
in[4] = 0x33 = 3
in[5] = ?
in[6] = 0x68 = h
in[7] = 0x65 = e
in[8] = ?
in[9] = 0x73 = s
in[5] + in[8] = 0xc9
The parts of the password recoverd so far look like Prometheus, so testing combinations for t an u or leetspeak versions thereof might result in the missing value for the equation: in[5] + in[8] = 0xc9
>>> hex(ord("7"))
'0x37'
>>> hex(ord("u"))
'0x75'
>>> hex(0x37 + 0x75)
'0xac'
>>> hex(ord("t"))
'0x74'
>>> hex(0x74 + 0x75)
'0xe9'
>>> hex(ord("t"))
'0x74'
>>> hex(ord("U"))
'0x55'
>>> hex(0x74 + 0x55)
'0xc9'
Correct password for decrypting the flag:
in[0] = 0x50 = P
in[1] = 0x72 = r
in[2] = 0x30 = 0
in[3] = 0x6d = m
in[4] = 0x33 = 3
in[5] = t
in[6] = 0x68 = h
in[7] = 0x65 = e
in[8] = U
in[9] = 0x73 = s
If one failed to recover the password or rather the two missing characters, it should be possible to use the “hashing” function close to the end of the main function to bruteforce those two characters. As 8 characters are known and the hash should result in the value 0x1928f914, it is possible to reconstruct the algorithm and test all possible combinations.
Using the password and reversing the function at 0x401350 we can statically recover the flag by writing a short binja/Python script.
def decrypt(start, buf_len, key, len_key, key_mod):
out = ""
for index in range(buf_len):
byte_to_decrypt = bv.read_int(start + index , 1)
decode = ((ord(key[index % len_key]) + key_mod) ^ byte_to_decrypt) % 256
out += chr(decode)
print("Decrypted:", out)
start = 0x4180a8
buf_len = 0x100
key = "Pr0m3theUs"
key_mod = 2
decrypt(start, buf_len, key, len(key), key_mod)
#Decrypted: Congratulations! You guessed the right password, but the message you see is wrong.
#Try to look for some unreferenced data, that can be decrypted the same way as this text.
start = 0x4181a8
buf_len = 0x100
key = "Pr0m3theUs"
key_mod = 2
decrypt(start, buf_len, key, len(key), key_mod)
#Decrypted: https://join.eset.com/[someHash]/crackme.zip
Challenge 2
I have not solved this one yet, so there might be a future update for this challenge here.