Crackme Open
A Beginner’s Guide to Cracking Password-Protected Files
Get ready for some hacking fun! In this blog post, we’ll show you how to break into a simple C executable file secured with a password using Ubuntu 20.04 LTS. Whether you’re just starting out or a seasoned pro, this tutorial has something for everyone. Time to flex those cyber skills!
All the files used in this tutorial can be found on GitHub here. You can clone the repository into your local environment to access the encrypted files.
To begin, we can execute the crackme
file to observe its behavior by navigating to the crackme directory and entering the following command:
$ ./crackme
The following will be printed to standard output:
Access Denied
This suggests that the crackme
file cannot be run in the usual way. As a result, we need to determine the cause of this issue. However, before we can do that, we need to identify the type of file it is.
The file
command is used to identify the type of a file. It does this by performing a series of tests in a specific order: filesystem tests, magic number tests, and language tests.
These tests are designed to determine the file's type based on its content and any metadata associated with it. If you want to use the file
command to identify the type of a specific file, you can do so by running file [filename]
in your terminal. For example:
$ file crackme
would show you the type of the file crackme
is as:
crackme: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=e06e0b0229279a69506925702a7f79e36bdc3eb2, not stripped
The information provided in the output of the file command reveals that the crackme
file is:
- An executable file designed to be run on a 64-bit Linux operating system
- Compiled for the x86–64 architecture and linked dynamically, meaning it relies on shared libraries to function
- Interpreted by the /lib64/ld-linux-x86–64.so.2 interpreter
- Built for GNU/Linux 2.6.24 and has a unique identifier called a “BuildID”
- Has not been stripped, so it still contains debugging information and symbols that can be used for analysis.
ltrace
is a utility that traces the dynamic library calls made by a program. Since we now know the file relies on shared libraries, ltrace
can be used to analyze the crackme file. We may be able to see the specific library functions that the file calls and how they are used, which can provide additional insight into the behavior and purpose of the program.
It’s also worth noting that ltrace
can be used in conjunction with other tools like gdb
(GNU Debugger) to further analyze the behavior of the program and potentially reverse engineer it which we will not be using for this tutorial.
We can run ltrace
on the crack me file by entering the following command:
$ ltrace ./crackme
the output of the command will be as follows:
__libc_start_main(0x40087d, 1, 0x7ffeef764458, 0x400a70 <unfinished ...>
strncmp("SHELL=/bin/bash", "Passw0rd=", 9) = 3
strncmp("PWD=/home/vagrant/crackme_tutori"..., "Passw0rd=", 9) = -10
strncmp("LOGNAME=vagrant", "Passw0rd=", 9) = -4
strncmp("XDG_SESSION_TYPE=tty", "Passw0rd=", 9) = 8
strncmp("MOTD_SHOWN=pam", "Passw0rd=", 9) = -3
strncmp("HOME=/home/vagrant", "Passw0rd=", 9) = -8
strncmp("LANG=C.UTF-8", "Passw0rd=", 9) = -4
strncmp("LS_COLORS=rs=0:di=01;34:ln=01;36"..., "Passw0rd=", 9) = -4
strncmp("SSH_CONNECTION=10.0.2.2 61015 10"..., "Passw0rd=", 9) = 3
strncmp("LESSCLOSE=/usr/bin/lesspipe %s %"..., "Passw0rd=", 9) = -4
strncmp("XDG_SESSION_CLASS=user", "Passw0rd=", 9) = 8
strncmp("TERM=xterm-256color", "Passw0rd=", 9) = 4
strncmp("LESSOPEN=| /usr/bin/lesspipe %s", "Passw0rd=", 9) = -4
strncmp("USER=vagrant", "Passw0rd=", 9) = 5
strncmp("SHLVL=1", "Passw0rd=", 9) = 3
strncmp("XDG_SESSION_ID=22", "Passw0rd=", 9) = 8
strncmp("XDG_RUNTIME_DIR=/run/user/1000", "Passw0rd=", 9) = 8
strncmp("SSH_CLIENT=10.0.2.2 61015 22", "Passw0rd=", 9) = 3
strncmp("XDG_DATA_DIRS=/usr/local/share:/"..., "Passw0rd=", 9) = 8
strncmp("PATH=/usr/local/sbin:/usr/local/"..., "Passw0rd=", 9) = -32
strncmp("DBUS_SESSION_BUS_ADDRESS=unix:pa"..., "Passw0rd=", 9) = -12
strncmp("SSH_TTY=/dev/pts/0", "Passw0rd=", 9) = 3
strncmp("OLDPWD=/home/vagrant/crackme_tut"..., "Passw0rd=", 9) = -1
strncmp("_=/usr/bin/ltrace", "Passw0rd=", 9) = 15
puts("Access Denied"Access Denied
) = 14
+++ exited (status 1) +++
Lets take a closer look:
__libc_start_main
is a function that sets up a program’s environment and calls its main function. It takes in various arguments and is provided by the C standard library (libc). It is called by the program’s main entry point function.- The lines beginning with
strncmp
show that the program is calling thestrncmp
function, which is a function that compares a given number of characters (in this case, 9 characters) of two strings. The strings being compared appear to be various environment variables (e.g. "SHELL", "PWD", "LOGNAME", etc.) and the string"Passw0rd="
. It seems that the program is checking whether any of the environment variables begin with the string"Passw0rd="
. - The
puts
function is called at the end with the string "Access Denied", which may indicate that the program is printing this message if none of the environment variables begin with"Passw0rd="
.
Thanks to ltrace
, it’s clear that this binary file is searching for an environment variable named Passw0rd. Time to create one with a random value and see what happens!
To set an environment variable in Linux, we can use the export
command in the terminal. For example, to set an environment variable named Passw0rd
to a value of test
, we can run:
export Passw0rd=test
It is now necessary to rerun ltrace
on the crackme file after exporting a value to the Passw0rd environment variable. This can be done by executing the following command:
$ ltrace ./crackme
the output of the command will be as follows:
__libc_start_main(0x40087d, 1, 0x7fff085f1d38, 0x400a70 <unfinished ...>
strncmp("SHELL=/bin/bash", "Passw0rd=", 9) = 3
strncmp("PWD=/home/vagrant/crackme_tutori"..., "Passw0rd=", 9) = -10
strncmp("LOGNAME=vagrant", "Passw0rd=", 9) = -4
strncmp("XDG_SESSION_TYPE=tty", "Passw0rd=", 9) = 8
strncmp("MOTD_SHOWN=pam", "Passw0rd=", 9) = -3
strncmp("HOME=/home/vagrant", "Passw0rd=", 9) = -8
strncmp("LANG=C.UTF-8", "Passw0rd=", 9) = -4
strncmp("LS_COLORS=rs=0:di=01;34:ln=01;36"..., "Passw0rd=", 9) = -4
strncmp("SSH_CONNECTION=10.0.2.2 61015 10"..., "Passw0rd=", 9) = 3
strncmp("LESSCLOSE=/usr/bin/lesspipe %s %"..., "Passw0rd=", 9) = -4
strncmp("XDG_SESSION_CLASS=user", "Passw0rd=", 9) = 8
strncmp("TERM=xterm-256color", "Passw0rd=", 9) = 4
strncmp("LESSOPEN=| /usr/bin/lesspipe %s", "Passw0rd=", 9) = -4
strncmp("USER=vagrant", "Passw0rd=", 9) = 5
strncmp("SHLVL=1", "Passw0rd=", 9) = 3
strncmp("XDG_SESSION_ID=22", "Passw0rd=", 9) = 8
strncmp("XDG_RUNTIME_DIR=/run/user/1000", "Passw0rd=", 9) = 8
strncmp("SSH_CLIENT=10.0.2.2 61015 22", "Passw0rd=", 9) = 3
strncmp("XDG_DATA_DIRS=/usr/local/share:/"..., "Passw0rd=", 9) = 8
strncmp("PATH=/usr/local/sbin:/usr/local/"..., "Passw0rd=", 9) = -32
strncmp("Passw0rd=test", "Passw0rd=", 9) = 0
MD5_Init(0x7fff085f1b90, 0x400b06, 9, 6) = 1
strlen("test") = 4
MD5_Update(0x7fff085f1b90, 0x7fff085f2f68, 4, 0x7fff085f2f68) = 1
MD5_Final(0x7fff085f1bf0, 0x7fff085f1b90, 0x7fff085f1b90, 0) = 1
sprintf("09", "%02x", 0x9) = 2
sprintf("8f", "%02x", 0x8f) = 2
sprintf("6b", "%02x", 0x6b) = 2
sprintf("cd", "%02x", 0xcd) = 2
sprintf("46", "%02x", 0x46) = 2
sprintf("21", "%02x", 0x21) = 2
sprintf("d3", "%02x", 0xd3) = 2
sprintf("73", "%02x", 0x73) = 2
sprintf("ca", "%02x", 0xca) = 2
sprintf("de", "%02x", 0xde) = 2
sprintf("4e", "%02x", 0x4e) = 2
sprintf("83", "%02x", 0x83) = 2
sprintf("26", "%02x", 0x26) = 2
sprintf("27", "%02x", 0x27) = 2
sprintf("b4", "%02x", 0xb4) = 2
sprintf("f6", "%02x", 0xf6) = 2
strcmp("c4032d9e95a93d91b9b595dcfcca640b"..., "098f6bcd4621d373cade4e832627b4f6"...) = 51
puts("Access Denied"Access Denied
) = 14
+++ exited (status 1) +++
What do we have here?
- We already know the first line shows that the
__libc_start_main
function was called with four arguments: the main function’s address, the number of program arguments, a pointer to an array of the program’s arguments, and a pointer to a function to be called when the main function returns. - The following lines show a series of calls to the
strncmp
function, which compares two strings and returns an integer indicating their lexicographical ordering. It appears that the program is trying to compare each of the environment variables in the system with the string"Passw0rd="
. - After this, there are calls to the
MD5_Init
,MD5_Update
, andMD5_Final
functions, which are part of the MD5 cryptographic hash function. It looks like the program is computing the MD5 hash of the value of the Passw0rd environment variable. - Next, there are calls to the
sprintf
function, which formats a string and stores it in a buffer. It appears that the program is formatting the bytes of the MD5 hash as hexadecimal values and storing them in strings. - The output includes a call to the
strcmp
function with two strings as arguments: "c4032d9e95a93d91b9b595dcfcca640b" and "098f6bcd4621d373cade4e832627b4f6". This indicates that the program is comparing the MD5 hash of the password to the hash of the Passw0rd environment variable that was exported. - The
puts
function is called at the end with the string "Access Denied", indicating that the Passw0rd is incorrect. However, this will likely be resolved soon.
It is not possible to reverse an MD5 hash, but there are methods to crack it. You can use a dictionary that has strings and their corresponding MD5 hashes, or try brute force. However, simple passwords are often used, so an MD5 dictionary with a large number of entries may be able to convert the hash back to a string..
We can use this website to reverse the hashed strings to their values.
Now that we have determined that the hashed password is LionelMessi, we can set the Passw0rd
environment variable to this value and rerun the ltrace
command to see if it produces a different result. Let’s give it a try!
$ export Passw0rd=LionelMessi
$ ./crackme
Access Granted
We’ve successfully cracked the file using ltrace and a dictionary attack. This demonstrates how even seemingly secure programs can be vulnerable to simple cracking techniques.
It’s always important to remember to use strong, unique passwords and to never reuse passwords across different accounts.
Keep an eye out for our upcoming release on GitHub of a C program that compares MD5 hashes to a dictionary and reverses them to their original string form. Stay tuned!
We hope you enjoyed this tutorial on cracking a simple file. If you have any comments or questions, please don’t hesitate to leave them in the comments section below. Happy hacking!