Skip to main content

0x00 - easy_reverse by cbm-hackers

This is a beginner-friendly crackme by cbm-hackers which you can find here and here.

binary rev50_linux64-bit
sha256 bb28a152966bed0a369f30149a912982ea33b408794bfbd82e73c87ff4e184ff

Running file reveals that this is a 64-bit ELF, and that it is not stripped which means it still contains symbols and debugging info.

file rev50_linux64-bit
rev50_linux64-bit: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=6db637ef1b479f1b821f45dfe2960e37880df4fe, not stripped

We can use readelf to confirm that it is not stripped, as the symbol table still exists:

readelf -S ./rev50_linux64-bit | grep '.symtab'
  [27] .symtab           SYMTAB           0000000000000000  00003068

Since this is a simple program, Ghidra 11.x does a nice job of decompiling this one, and we can see that the entire logic of this sample is contained in the main function:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
undefined8 main(int param_1,undefined8 *param_2)

{
  size_t sVar1;
  
  if (param_1 == 2) {
    sVar1 = strlen((char *)param_2[1]);
    if (sVar1 == 10) {
      if (*(char *)(param_2[1] + 4) == '@') {
        puts("Nice Job!!");
        printf("flag{%s}\n",param_2[1]);
      }
      else {
        usage(*param_2);
      }
    }
    else {
      usage(*param_2);
    }
  }
  else {
    usage(*param_2);
  }
  return 0;
}

This looks like a standard main function which takes an int and a pointer in the function signature, so we can assume that param_1 is int argc and param_2 is char *argv[]; a pointer to an array of 1-byte ASCII characters. The program uses both of these arguments to check both the number of arguments provided, and the length of the first argument.

Running the program from the command line shows us the following output, which describes the input the program expects:

./rev50_linux64-bit
USAGE: ./rev50_linux64-bit <password>
try again!

Given what we know so far, this looks like a “keygen” variant crackme, and so the goal is to come up with a <password> value that passes whatever checks are happening. This is the key validation algorithm, and it’s just the logic that determines whether or not a valid key has been presented.

The first part of this logic checks that the key is provided in the correct format: if argc is anything other than 2 (meaning one command-line argument), or if the command-line argument is not exactly 10 characters long, the program prints a usage statement and exits, using this usage function:

1void usage(undefined8 param_1)
2
3{
4  printf("USAGE: %s <password>\n",param_1);
5  puts("try again!");
6                    /* WARNING: Subroutine does not return */
7  exit(0);
8}

The second part of the logic checks if argv[1] + 4, the 5th character of the command-line argument, is the ASCII character @. This means any key that fits this format will work:

xxxx@xxxxx

We can run the program with this key to confirm:

./rev50_linux64-bit xxxx@xxxxx
Nice Job!!
flag{xxxx@xxxxx}

And that’s it! With this simple crackme we’ve learned how to do a few things:

  • Use the file program to determine the file type of a binary
  • Use the readelf program to view information about the symbol table of a binary
  • Use Ghidra to decompile the main function of a binary
  • Use our brains to work through the logic of a key validation algorithm

Although the exact tools and techniques will vary, nearly any reverse engineering challenge in a crackme will involve these general steps. In future posts, I’ll talk more about each step, the tools used, and some of the structures involved.