TryHackMe: Reverse Engineering Room Writeup
This is my writeup for the Reverse Engineering room created by ashu on TryHackMe. The room contains three “crackme” challenges that involve finding the correct passwords.
I have redacted these in my writeup, and encourage readers to solve these challenges on their own. Enjoy!
crackme1
When working with ELF binaries, I normally always start with running ltrace or strace.
This allows me to see:
- the logic and flow of a program
- what functions or syscalls are used
- any hidden strings or data
In this case, the ltrace output shows the correct password (redacted below):
jason@lapras:/tmp$ ltrace ./crackme1.bin
puts("enter password"enter password
) = 15
\_\_isoc99\_scanf(0x7fd32ee008a3, 0x7ffffa955ef2, 0x7fd32e9ed8c0, 0x7ffff2161010hello
) = 1
strcmp("hello", XXXXXX) = 4
puts("password is incorrect"password is incorrect
) = 22
+++ exited (status 0) +++
Run the crackme with the correct password to win:
jason@lapras:/tmp$ ./crackme1.bin
enter password
XXXXXX
password is correct
crackme2
For the second challenge, I again ran ltrace and strace, but the password was not shown.
So I opened the binary in gdb. Below is the disassembly output of the main() function:
jason@lapras:/tmp$ gdb -q crackme2.bin
Reading symbols from crackme2.bin...(no debugging symbols found)...done.
(gdb) disass main
Dump of assembler code for function main:
0x000000000000071a <+0>: push rbp
0x000000000000071b <+1>: mov rbp,rsp
0x000000000000071e <+4>: sub rsp,0x10
0x0000000000000722 <+8>: mov rax,QWORD PTR fs:0x28
0x000000000000072b <+17>: mov QWORD PTR \[rbp-0x8\],rax
0x000000000000072f <+21>: xor eax,eax
0x0000000000000731 <+23>: lea rdi,\[rip+0xec\] # print "enter your password"
0x0000000000000738 <+30>: call 0x5d0 <puts@plt>
0x000000000000073d <+35>: lea rax,\[rbp-0xc\] # buffer for scanf to store our input
0x0000000000000741 <+39>: mov rsi,rax
0x0000000000000744 <+42>: lea rdi,\[rip+0xed\] # "%d" format string for scanf
0x000000000000074b <+49>: mov eax,0x0
0x0000000000000750 <+54>: call 0x5f0 <\_\_isoc99\_scanf@plt>
0x0000000000000755 <+59>: mov eax,DWORD PTR \[rbp-0xc\] # our input stored here
0x0000000000000758 <+62>: cmp eax,0xXXXXX # compare our input to the (redacted) value
0x000000000000075d <+67>: jne 0x76d <main+83> # branch if incorrect
0x000000000000075f <+69>: lea rdi,\[rip+0xd5\] # print "password is valid"
0x0000000000000766 <+76>: call 0x5d0 <puts@plt>
0x000000000000076b <+81>: jmp 0x779 <main+95> # branch if correct
0x000000000000076d <+83>: lea rdi,\[rip+0xd9\] # print "password is incorrect"
0x0000000000000774 <+90>: call 0x5d0 <puts@plt>
0x0000000000000779 <+95>: mov eax,0x0 # return 0 for main
0x000000000000077e <+100>: mov rdx,QWORD PTR \[rbp-0x8\]
0x0000000000000782 <+104>: xor rdx,QWORD PTR fs:0x28
0x000000000000078b <+113>: je 0x792 <main+120>
0x000000000000078d <+115>: call 0x5e0 <\_\_stack\_chk\_fail@plt>
0x0000000000000792 <+120>: leave
0x0000000000000793 <+121>: ret
End of assembler dump.
(gdb)
I’ve marked the relevant assembly instructions with comments. (I got this idea from one of Chris Evans writeups). Let’s break down what this program is doing:
- read in an integer ("%d") from user with scanf
- compare user value with correct password
- if equal, print “password is valid”
- otherwise print “password is incorrect”
Since the correct password is hard coded in hex on line <+62>, I used gdb to print it in decimal (redacted below):
(gdb) b \*main+62
Breakpoint 1 at 0x758
(gdb) r
Starting program: /tmp/crackme2.bin
enter your password
LET ME IN
Breakpoint 1, 0x0000000008000758 in main ()
(gdb) p 0xXXXXX
$1 = XXXX
Run the program with this decimal value to win:
jason@lapras:/tmp$ ./crackme2.bin
enter your password
XXXXX
password is valid
crackme3
The final crackme was an interesting one, and involved looping. Again, I launched gdb to get a look under the hood:
jason@lapras:/tmp$ gdb -q crackme3.bin
Reading symbols from crackme3.bin...(no debugging symbols found)...done.
(gdb) disass main
Dump of assembler code for function main:
0x000000000000071a <+0>: push rbp
0x000000000000071b <+1>: mov rbp,rsp
0x000000000000071e <+4>: sub rsp,0x30
0x0000000000000722 <+8>: mov rax,QWORD PTR fs:0x28
0x000000000000072b <+17>: mov QWORD PTR \[rbp-0x8\],rax
0x000000000000072f <+21>: xor eax,eax
0x0000000000000731 <+23>: mov WORD PTR \[rbp-0x23\],0x7a61
0x0000000000000737 <+29>: mov BYTE PTR \[rbp-0x21\],0x74
0x000000000000073b <+33>: lea rdi,\[rip+0x112\] # print "enter your password"
0x0000000000000742 <+40>: call 0x5d0 <puts@plt>
0x0000000000000747 <+45>: lea rax,\[rbp-0x20\] # buffer for scanf to store our input
0x000000000000074b <+49>: mov rsi,rax
0x000000000000074e <+52>: lea rdi,\[rip+0x113\] # 0x868
0x0000000000000755 <+59>: mov eax,0x0
0x000000000000075a <+64>: call 0x5f0 <\_\_isoc99\_scanf@plt>
0x000000000000075f <+69>: mov DWORD PTR \[rbp-0x28\],0x0 # loop counter initialized to 0
0x0000000000000766 <+76>: jmp 0x797 <main+125>
0x0000000000000768 <+78>: mov eax,DWORD PTR \[rbp-0x28\]
0x000000000000076b <+81>: cdqe
0x000000000000076d <+83>: movzx edx,BYTE PTR \[rbp+rax\*1-0x20\]
0x0000000000000772 <+88>: mov eax,DWORD PTR \[rbp-0x28\]
0x0000000000000775 <+91>: cdqe
0x0000000000000777 <+93>: movzx eax,BYTE PTR \[rbp+rax\*1-0x23\]
0x000000000000077c <+98>: cmp dl,al # compare two characters
0x000000000000077e <+100>: je 0x793 <main+121> # continue if equal
0x0000000000000780 <+102>: lea rdi,\[rip+0xe4\]
0x0000000000000787 <+109>: call 0x5d0 <puts@plt> # print "password is incorrect"
0x000000000000078c <+114>: mov eax,0x0 # set 0 return value for main
0x0000000000000791 <+119>: jmp 0x7ae <main+148> # branch to exit code
0x0000000000000793 <+121>: add DWORD PTR \[rbp-0x28\],0x1 # increment loop counter by 1
0x0000000000000797 <+125>: cmp DWORD PTR \[rbp-0x28\],0x2 # check if loop count == 2
0x000000000000079b <+129>: jle 0x768 <main+78> # if not, branch to start of loop
0x000000000000079d <+131>: lea rdi,\[rip+0xdd\] # if it is, you win
0x00000000000007a4 <+138>: call 0x5d0 <puts@plt> # print "password is valid"
0x00000000000007a9 <+143>: mov eax,0x0
0x00000000000007ae <+148>: mov rcx,QWORD PTR \[rbp-0x8\]
0x00000000000007b2 <+152>: xor rcx,QWORD PTR fs:0x28
0x00000000000007bb <+161>: je 0x7c2 <main+168>
0x00000000000007bd <+163>: call 0x5e0 <\_\_stack\_chk\_fail@plt>
0x00000000000007c2 <+168>: leave
0x00000000000007c3 <+169>: ret
End of assembler dump.
The program is essentially an implementation of strncmp. It loops to compare the first three characters of our input with the first three characters of the correct password.
The comparison is made on line <+98>
0x000000000000077c <+98>: cmp dl,al
It compares the value in register dl with register al. These are the lower halves of registers edx and eax, respectively.
I set a breakpoint on this line, and printed out the values stored in these registers on each loop iteration. This allowed me to piece together the correct three letter password.
Run the program with the correct password to win:
jason@lapras:/tmp$ ./crackme3.bin
enter your password
XXX
password is valid
Conclusion
I had a lot of fun with this room recommend it to anyone starting out in reverse engineering!