Hare121's crackme in C for Unix/Linux

Andrew Magid · April 1, 2021

Setup

After watching stacksmashing’s Ghidra tutorial, I downloaded Ghidra for myself and found this very easy crackme on crackmes.one for my very first practice.

I realized the Linux binaries wouldn’t run on macOS, so I fired up a tiny CLI Ubuntu VM for testing.

I didn’t have to work directly with the x86 assembly since Ghidra decompiled into C, but getting better with x86 is my goal in the near future.

High Level

The program asks for a name and then a serial number. It determines the serial number by doing computations on the length of the name and ascii values of the letters in the name. I was able to re-implement the same functions that the binary performed to generate serial that would be accepted. The program just checks that the inputted serial matches the serial generated by the program.

Disassembly

After Ghidra analysis, I looked at the entry function which called __libc_start_main(...) which had the main function as a parameter.

Main:

undefined8 main(void)
{
  int namelen_computation;
  long in_FS_OFFSET;
  int serial;
  char *name_string;
  long offset_plus_40;
  
  offset_plus_40 = *(long *)(in_FS_OFFSET + 0x28);
  puts("Enter your name");
  __isoc99_scanf(&DAT_00102060,name_string);
  namelen_computation = namelen_compute(name_string);
  puts("Enter the serial");
  __isoc99_scanf();
  credcheck(namelen_computation,serial);
  if (offset_plus_40 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

Great, we found some strings that were printed when I ran the binary. I renamed some functions after realizing what they did (like namelen_compute or credcheck).

namelen_compute

This function was responsible for computing the serial which was then later checked by credcheck.

It looked like this:

int namelen_compute(char *name)
{
  int namelen;
  size_t namelen_size;
  
  namelen_size = strlen(name);
  namelen = (int)namelen_size;
        /* if namelen is odd */
  if ((namelen - 1U & 1) == 0) {
    namelen = namelen + -1;
        /* check if namelen-1 is larger than 2 (so namelen is 3 or more chars) */
    if (2 < namelen) {
        /* multiply ascii value of 3rd letter with 9230, divide by 2 and add 1 */
      namelen = (name[2] * 0x240e) / 2 + 1;
    }
  }
  else {
        /* if even, -1 from namelen and multiply by 645629 */
    namelen = (namelen + -1) * 0x9d9fd;
    if (namelen < 0) {
      namelen = namelen + 3;
    }
        /* bit shift left 2, multiply by 13 and divide by 7 */
    namelen = ((namelen >> 2) * 0xd) / 7;
  }
  return namelen;
}

I commented what exactly the code was doing for each step. It was trivial to rewrite this in python.

keygen.py

import sys

if(len(sys.argv) != 2):
    print("Usage: python3 keygen.py <name>")

name = sys.argv[1]
nameLen = len(name)

if(nameLen % 2 == 1): # if odd
    nameLen -= 1
    if(nameLen>2):
        nameLen = ord(name[2])*4615+1 # 4615 = 0x240e/2
    print("Serial: " + str(nameLen)) 
else: # if even
    nameLen = (nameLen -1 ) * 645629
    if(nameLen<0):
        nameLen += 3
    nameLen = int((nameLen >> 2) * 13 / 7)
    print("Serial: " + str(nameLen))

Result

Luckily, the program accepts :)

andrewmagid@ubuntu:~/Documents/pwn/hare121_keygen$ ./crackme
Enter your name
andrew
Enter the serial
1498781
yay you have the correct creds :)

Takeaway

This was my first time working with a disassembler/analyzer like Ghidra and doing a crackme. The problem was very easy (as per the difficulty) but helped me better understand the tools used along the way.