Crackmes.de - crackme.02.32 by geyslan

This is a crackme level 3 (Medium) from https://crackmes.one/crackme/5ab77f6533c5d40ad448cbc2

Fast static analysis Link to heading

First, use the file command to see what kind of file it is:

$ file crackme.02.32
crackme.02.32: ELF 32-bit LSB executable, Intel 80386, (SYSV), too many section (65535)

A 32 bit ELF, with a nice “too many sections”, no other userfull information.

Checking the strings in the binary :

GitHub Logo

Judging by the “tracing not allowed” and “I’m sorry GDB…” there are a few anti-debug methods out there.

Anti-debug Link to heading

Lets run the program :

$ ./crackme.02.32 
Please tell me my password: testtest
No! No! No! No! Try again.

Classic, the binary expects a password. This challenge isn’t difficult to solve without debugging, but it’s interesting to see what protections are in place and how they can be bypassed to make analysis easier.

Here is the codeflow that determines if the programme is run with a debugger, we can find it by looking at where the “anti-debug” strings are used:

GitHub Logo

There are two differents tests :

  • Test if the program is run under gdb using this technic:

“When a process uses fork(2) to spawn a child process, the parent’s file descriptors are inherited to the new child process. In normal cases, the new process should have only FDs 0 (stdin), 1 (stdout) and 2 (stderr). However, GDB opens up some more FDs (3, 4 and 5) and never closes them, so they are inherited.”

Source: https://xorl.wordpress.com

As we can see, a file descriptor is created (pointing to /tmp), and his _fileno is checked, if fileno > 5 the program leaves.

GitHub Logo

Another block calls the ptrace function to check if the process is being traced (used by ltrace or gdb for example). If it is, the program stops:

GitHub Logo

Bypass this is easy, we can just the change the :

mov eax, ds:dword_8049C08

into :

mov eax, 0

and it will no more jump to the “tracing is not allowed” block.

Get the password Link to heading

Continuing with the main function, we can see that our input is used as a parameter and passed to a function. This function loops over the chars and performs a binary OR with the value 0x90h.

GitHub Logo

Then, the return value is compared with a char array :

GitHub Logo

Little sum up :

  • We enter a string
  • This string is ORed with 0x90
  • The result must match this char array :
[0xF7, 0xF8 ,0xF1 ,0xF4 ,0xF1 ,0xF8 ,0xB3 ,0xFC ,0xFC] #(I'll call it "tab")

One thing i’m sure :

OR(tab,tab) == tab

What if the input is exactly the same tab ?

$ python2 -c 'print "\xF7\xF8\xF1\xF4\xF1\xF8\xB3\xFC\xFC"' | ./crackme.02.32 
Please tell me my password: The password is correct!
Congratulations!!!

If we want an ascii solution, we can remove the MSB in tab (in order to have smaller numbers which fit in the readable range of the ascii table), lets script that :

#!/usr/bin/python3

tab = [0xF7, 0xF8 ,0xF1 ,0xF4 ,0xF1 ,0xF8 ,0xB3 ,0xFC ,0xFC]

res =  ""
for elt in tab:
        h1 = str(bin(elt))[2:] # remove '0b'
        res += chr(int(h1[1:],2)) # remove first '1'

print("result : ", res)

And the result is : wxqtqx3||

Please tell me my password: wxqtqx3||
The password is correct!
Congratulations!!!

In fact, there are a lot of solutions (512) because we can OR a lot of values to match the key.