Skip to content

Latest commit

 

History

History
184 lines (122 loc) · 12.1 KB

File metadata and controls

184 lines (122 loc) · 12.1 KB

=== Danofred's KeygenMe - 01 (update) solution ===

Source: https://crackmes.one/crackme/6214dd6233c5d46c8bcbff61

Solution

In previous crackmes we learned how to use Ghidra basic internal tools like the disassembler view and decompiler to solve those puzzles. But to be honest those riddles were pretty easy and had no other goal but to teach you how to do BASIC REVERSE ENGINEERING analysis. On the contrary in this, 3rd crackme, we've finally come to something serious.

Are you ready? Good. Let's have a look at this one.

First, run the program.

And give it some input. What should we choose? Maybe 1?

Author claims that this crackme has to be solved only by keygen creation. We should run this program again to look into the password input process.

The program asks for username (4 characters length minimum) and then asks for password. As you can see our naive endeavor had a mythological size of failure. But is it the end of our journey for solving this mystery? Of course not! And that is why we have GHIDRA!

For the sake of readability and to make this lesson more about reversing and problem solving I've already renamed the variables and function names in some decompiled sections of the crackme, so you, dear reader, can concentrate on the main thing: KNOWLEDGE.

First, we have to find the main() function if it's signature isn't missing of course.

We use the Symbol Tree window to find the symbols in our program and the main() function is there. Like in other 2 lessons, this crackme will be solved using static analysis approach and mostly interpreting the decompiled code.

In the decompiler window we have the "bricks" of C code of our program or something that GHIDRA tried to understand and give us some essay about the history of crackmes origin.

Most of the decompiled code is very self explanatory. Right from the start inside the main() function we can observe the usage of standard C library functions for input/output like puts(), printf(), scanf(). Section [13-15] takes us to the rules segment of program.

Here we can see the rules that author gives us which has to be followed by us, the Knights of Decompiled Enigmas.

Return to main() function. Section [16-18] brings us to the inner part of mechanism that processes the input of user/password strings: function tryToKeygenMe() with number 10 as parameter. This number will be used further as attempts left for password input.

In the name of all cybernetic gods! We have a lot to dig in! Don't worry, because every single step of this program's logic will be uncovered.

  • Lines [5-10] variables declaration
  • Lines [12-18] do {...} while (the length of username < 4) cycle where the name length is checked with _strlen() function and where our name string is stored to input_name_str variable. Interesting thing that is_Equal variable will be used again for another comparison
  • Lines [19-36] do {...} while (attempts_left != 0) cycle is the main domain, the castle of the whole crackme where everything happens.
    • Line 20 each time user enters the password the variable attempts_left is decreased by 1
    • Line 22 scanf() function writes the user password input to input_pass_str string variable
    • Line 24 is the heart of the whole program! It is the place where the REAL password is generated by getEncrytKey() function using input_name_str variable and then the valid password is copied to real_pass_str variable. We'll return to understanding what is going on inside this "encryption" function later
    • Line 25 again the program uses is_Equal variable to store the result of _strcmpKey() action where real_pass_str and input_pass_str strings are compared
    • Lines [26-29] if is_Equal is 0 (our password and real password ARE NOT THE SAME) then the program outputs current attempt number and the message using printf() which informs us that input password is incorrect
    • Lines [30-32] else if is_Equal is 1 (input_pass_str == real_pass_str) then the program gives us nice goodbye using puts() function and exits the program
    • Lines [33-35] if there are no attempts left then exit the program. This time "goodbye" is not very nice for us

The things we know are:

  1. Valid password is somehow generated by calling getEncrytKey() and it uses input_name_str string variable
  2. And only then _strcmpKey() function is executed where the program decides are we the one who is destined to meet his princess or to meet something worse (dragon maybe?)

And while the inner composition of _strcmpKey() function is pretty linear and easy to understand

  • Lines [5-8] variables declaration
  • Line 10 is where is_Equal (it's local variable, and it's not the same is_Equal that's in tryToKeygenMe()) is initialized with 1 by default, meaning that until proven wrong the input_pass_str and the real_pass_str ARE THE SAME STRINGS.
  • Lines [11-12] _strlen() function is called to evaluate the length of both the input and real password strings and then assign the length values to its corresponding variables
  • Lines [13-19] if these length values are equal ONLY THEN dive into the for (...) {...} cycle where every single character of both input_pass_str and real_pass_str are compared through accessing the arrays ([counter + input_pass_str] and [counter + real_pass_str] where the 0 index of both arrays (beginning) is used and then incremented by counter)
  • Line 15 if the current processed characters of these strings are not the same _strcmpKey() immediately returns 0
  • Lines [20-22] is the place where is_Equal is re initialized with 0 if the check at line 13 returns false
  • Line 23 return the value of is_Equal

The parts of getEncrytKey() is where we have to unleash the full power of understanding the logic behind this crackme

  • Lines [5-8] variables declaration
  • Line 10 is the place where special variable key_str_len is initialized with the result of _strlen() evaluation of some mysterious value from unknown place in memory. What's that variable for? It will be used to generate the real password string. Nevertheless, we should take a look what is hidden inside this adress 0x4051f8

Very interesting! So the ds section in assembly representation gives us the string tryHarderToMakeAGoodKeyGen. We already know that this string will be taken as a parameter to _strlen() at line 10 of our encryption function. So the length will be 26. Good. Now we have key_str_len = 26

  • Line 11 the length of input_name_str is written to input_name_str_len by calling _strlen() later it will be used the main encrypted password generation cycle

  • Lines [12-15] demonstrates the memory allocation process using malloc() function. It is a standard procedure for a program to determine memory chunk it can work with

  • Lines [16-21] the moment of truth! Here everything happens. It is the place where the real password is generated

    • Line 16 classic for (...) {...} cycle that goes through all the length of input_name_str
    • Line [17-20] is the encryption algorithm. If we get rid of decompiler interpreted type casting and just write the expression we'll have this:

The principle is very simple. Every cycle iteration (through input_name_str length):

  1. We take current input_name_str char representation as byte (accessing by indexing mechanism through adding counter to the beginning of input_name_str)
  2. Raise the value we got to the power of 26 (key_str_len)
  3. Take the result and apply modulo operation using input_name_str_len
  4. Assign the calculated value to the current index of real_pass_str (yes, address arithmetic)
  • Line 22 finally we get the real password stored in real_pass_str which is then returned by function

There is only thing left. We have to write the keygen program that generates password for every name we want. And we'll do this in a right way. In C.

Create the source file with this code (you can always find keygen.c in this crackme folder):

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

int main(int argc, char const *argv[])
{
    
    char input_name_str[100] = "";
    char real_pass_str[100] = "";
    int input_name_str_len;
    int key_str_len = 26;
    int index;

    printf("Enter name: \n");
    scanf("%s", input_name_str);

    input_name_str_len = strlen(input_name_str);
    printf("%d\n", input_name_str_len);

    for (int i=0; i < input_name_str_len; i++)
    {
        int char_int = input_name_str[i];
        index = (char_int ^ key_str_len) % input_name_str_len;
        real_pass_str[i] = input_name_str[index];

    }
    
    printf("Your password is: \n");
    printf("%s", real_pass_str);

    return 0;
}

Yeah, yeah, this program does not have all this "true blood C" things like memory management and pointers, but it does what it has to. And the main thing, the encryption algorithm, is implemented here:

    input_name_str_len = strlen(input_name_str);
    printf("%d\n", input_name_str_len);

    for (int i=0; i < input_name_str_len; i++)
    {
        int char_int = input_name_str[i];
        index = (char_int ^ key_str_len) % input_name_str_len;
        real_pass_str[i] = input_name_str[index];

    }

We use the same approach as in the crackme.

  1. Get the length of input_name_str string and assign it to input_name_str_len
  2. Open the for (...) {...} cycle which goes through all the length input_name_str
  3. On every iteration of this cycle we convert the current input name string character to int representation and assign the value to cahr_int variable
  4. Calculate the index variable value using the formula which we deducted above
  5. Use the index value we found to get the input_name_str[index] cahracter and write it to real_pass_str[i]

The result password string will be printed here

    printf("Your password is: \n");
    printf("%s", real_pass_str);

Then we need to compile this file. I use GCC compiler

gcc keygen.c -o keygen.exe

Why don't we test our keygen? Let's say we want to generate password for the name Ares (remember the 4 characters minimum length requirement)

And it is time to run the crackme to prove that we've made it!

Who is the Master of binary spirits? You're! Congratulations!