Contents

Reversing ELFs on TryHackMe: Part 1

Safe-space

Please don’t ever execute any random ELF/PE binary you find on the web. Even if it comes from a trusted source like HTB or THM, we should always work in a segmented environment. Since these challenges are all ELFs, I spun up a Kali machine (you can literally use any OS to do these, I just had a VM ready to go) to run through these. Once you’ve done that, you can just log in and download each of the challenge files.

Crackme1

The simplest thing we can do when reversing is to start by just executing the program. These binaries are not necessarily going to have execute privilege when we download them. Because of that, we’ll need to run a quick chmod +x crackme to be able to run these. Once we’ve done that, we can just use the syntax ./crackme to execute our binary.

/images/thm/reversing/reversingelf/crackme1.png

Crackme2

We’ll start this challenge the same as the previous - by executing (and will do so going forward so assume that’s always step 1). It was worth a shot, but it looks like we’ll have to try something new.

/images/thm/reversing/reversingelf/crackme2-exec.png

So now on this second challenge, we’ll need to expand our toolset slightly. One thing that’s useful to know about compiled programs, is that they need to contain all the information (for the most part) that the computer would need to know in order to run all the instructions inside. One key thing we should always check for (low-hanging fruit) are hardcoded strings or functions. In linux (and on Windows via sysinternals tools), we can utilize a super straight forward program called strings to output this information. When we run strings we’ll see something like this:

/images/thm/reversing/reversingelf/crackme2-strings.png

As we can see from the above output, we see the input prompt string followed by our ‘Access Denied’ text, so we can assume that the strings around that area have to do with that function - let’s try it!

/images/thm/reversing/reversingelf/crackme2-pw.png

Crackme3

/images/thm/reversing/reversingelf/crackme3-exec.png

/images/thm/reversing/reversingelf/crackme3-strings.png

So it looks like we have to add another tool to our kit here, and it’s something that you’ll learn to love and hate in this field… base64. If you notice, our password looks like gibberish, but it has the telltale sign of being a base64 encoded string (ends in ==). Luckily, linux has a built in program that allows us to work with this type of encoding and it’s called… base64… yeah, not the most exciting name but like everything commandline it’s functional! We’ll use the -d (for decode) flag and feed this string in via a pipe. For a full list of command flags, please reference the man pages!

/images/thm/reversing/reversingelf/crackme3-pw.png

Crackme4

/images/thm/reversing/reversingelf/crackme4-start.png

Well this is interesting, it’s telling us the password is hidden. Sure enough, when we run strings we also don’t see it where we expect to. So now is where we can get into some really cool stuff… debugging! To do that, I’m going to use a souped up version of GDB called pwndbg which can be found here. Don’t let the appearance scare you, I’m using it specifically since it shows me the stack and register values as the program is running. Since the authors were kind enough to tell us they used strcmp we know that at some point the actual password must be placed onto the stack or within a memory register to be used in the string comparison operation. To start debugging you can just run gdb crackme4.

Starting with Debugging

One of the first things we’ll want to do is determine the behavior of this program to determine at what point it performs a string comparison. Remember when I told you that strings can also be used to find functions that are defined in a program? Let’s see if anything of interest can be found:

/images/thm/reversing/reversingelf/crackme4-strings.png

As you can see from the above screenshot, we have some functions that are definitely of interest:

  • get_pwd
  • compare_pwd
  • main
  • and of course, we can see our strcmp@@GLIBC_2.2.5 function that performs the comparison.

Breaking stuff

So what do we do from here? Armed with this knowledge, we can actually set breakpoints in our program using a debugger to stop the program at a certain point in memory temporarily. We can leverage that to stop the program right at the string comparison function and see if we can find the password on the stack. Since we can clearly see a function called compare_pwd, let’s disassemble that function name to see what its contents are. In gdb we can use disass (short for disassemble) and the function name compare_pwd like this:

/images/thm/reversing/reversingelf/crackme4-comparefunc.png

Let’s break down the important portion:

  0x00000000004006c2 <+72>:    call   0x40062d <get_pwd>
  0x00000000004006c7 <+77>:    mov    rdx,QWORD PTR [rbp-0x28]
  0x00000000004006cb <+81>:    lea    rax,[rbp-0x20]
  0x00000000004006cf <+85>:    mov    rsi,rdx
  0x00000000004006d2 <+88>:    mov    rdi,rax
  0x00000000004006d5 <+91>:    call   0x400520 <strcmp@plt>
  0x00000000004006da <+96>:    test   eax,eax
  0x00000000004006dc <+98>:    jne    0x4006ea <compare_pwd+112>
  0x00000000004006de <+100>:   mov    edi,0x4007e8
  0x00000000004006e3 <+105>:   call   0x4004e0 <puts@plt>
  0x00000000004006e8 <+110>:   jmp    0x400700 <compare_pwd+134>
  0x00000000004006ea <+112>:   mov    rax,QWORD PTR [rbp-0x28]
  0x00000000004006ee <+116>:   mov    rsi,rax
  0x00000000004006f1 <+119>:   mov    edi,0x4007f4
  0x00000000004006f6 <+124>:   mov    eax,0x0
  0x00000000004006fb <+129>:   call   0x400500 <printf@plt>

We can see that we have a few function calls of compare_pwd and then we have a printf which likely is our final output telling us if our password was correct (or incorrect). What I’m going to do is set a breakpoint at the point the program executes call 0x400520 <strcmp@plt> by using the command b * 0x400520. I’m using this memory address because I’m operating under the assumption that if we’re calling a comparison, then our hidden password must already be loaded into memory somewhere by this point. Once we set our breakpoint, we can execute the program and it will pause right at the string comparison. Once there, we can view the registers:

/images/thm/reversing/reversingelf/crackme4-break.png

Stack-em up

This may look insane at first, but the concept here is super simple. Let’s break it down

LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────[ REGISTERS ]─────────────────────────
 RAX  0x7fffffffded0 ◂— 'my_m0r3_secur3_pwd'
 RBX  0x400760 (__libc_csu_init) ◂— push   r15
 RCX  0x11
 RDX  0x7fffffffe38a ◂— 0x4c4f430041414141 /* 'AAAA' */
 RDI  0x7fffffffded0 ◂— 'my_m0r3_secur3_pwd'
 RSI  0x7fffffffe38a ◂— 0x4c4f430041414141 /* 'AAAA' */

So our password input of “AAAA” we can see is being stored in RDX and RSI and the hidden password is being passed to both RAX and RDI (if you want to know what the hell these register names are for please take a look at this awesome resource from MIT here). This confirms the earlier suspicion that the passwords are being stored in physical memory during the function calls (spoiler, it won’t always be this easy).

As we continue through these, and the difficulty ramps up… understanding how to view the stack and see what’s happening instruction by instruction will allow us to test behavior and start to build a deeper understand of a program’s internal flow without even seeing the source code.

Part 2 — to be continued!

Find Part 2 HERE