CrackMe III: Impossible Password
This is the "Impossible Password" challenge from hackthebox. The file is an ELF binary, so I couldn't run it on my Windows box (without using a VM). It turns out that I didn't need to in any case.
The start routine nicely cleans up to
int __fastcall main(int a1, char **a2, char **a3)
{
const char *second_key; // rax
int result; // eax
char flag_alphabet[32]; // [rsp+10h] [rbp-40h] BYREF
char input[20]; // [rsp+30h] [rbp-20h] BYREF
char *first_key; // [rsp+48h] [rbp-8h]
first_key = "SuperSeKretKey";
qmemcpy(flag_alphabet, "A]Kr=9k0=0o0;k1?k81t", 20);
printf("* ");
__isoc99_scanf("%20s", input); // read first input
printf("[%s]\n", input);
if ( strcmp(input, first_key) )
exit(1);
printf("** ");
__isoc99_scanf("%20s", input); // read second input
second_key = generate_second_key(20);
result = strcmp(input, second_key);
if ( !result )
result = print_flag(flag_alphabet);
return result;
}
which reveals the program to be a two-step process: First a trivially readable key is requested. Second, another key is generated and requested. Finally, the flag is printed out.
Taking a look at the key generation algorithm
BYTE *__fastcall generate_second_key(int size)
{
int v1; // eax
_BYTE *buffer; // [rsp+20h] [rbp-10h]
int i; // [rsp+2Ch] [rbp-4h]
v1 = time(0LL);
++dword_601074;
srand(size * v1 + dword_601074);
buffer = malloc(size + 1);
if ( !buffer )
exit(1);
for ( i = 0; i < size; ++i )
buffer[i] = rand() % 94 + 33;
buffer[size] = 0;
return buffer;
}
reveals that it uses both time
and rand
, so it is very much nontrivial to generate a key ahead of time. This is presumably also where the challenge gets its name from, it would be impossible for a regular user to get past this check.
I was basically already resigned to spinning up a Linux box and using a debugger at this point. But taking a look at the flag printing method
// alphabet = A]Kr=9k0=0o0;k1?k81t
int __fastcall print_flag(_BYTE *alphabet)
{
int i; // eax
int j; // [rsp+14h] [rbp-Ch]
j = 0;
while ( *alphabet != 9 )
{
i = j++;
if ( i > 19 )
break;
putchar((char)(*alphabet++ ^ 9));
}
return putchar(10);
}
reveals that nothing about it depends on any random variables. Compiling the above code function and feeding it with the given alphabet prints the correct flag. It's interesting to note that the while
loop condition is never true, it's just an anti-reversing decoy. The real loop condition is the check for i > 19
.
Conclusion
This was a really fun one, first time I've used exclusively IDA to solve a crackme. Usually I only understand very little about what is going on in a program when analyzing it, but this one was simple enough to fully grasp in a couple of minutes. That made it more enjoyable I think.
It would have probably been easier to patch out the offending checks in a debugger and just plow straight to the flag, but this was certainly more interesting.