Basic Binary Disassembly

Completing a simple reversing challenge

Introduction

In this blog post, I shall be trying to complete the room `0x41haz` on TryHackMe, listed here. It is listed as a 'simple reversing challenge'; so we shall we be trying to understand what the supplied binary does by looking at its disassembled code.


First steps

The first steps would be to run the file command on the binary, which will provide us some information as to the file type and architecture. For example:

$ file 0x41haz.0x41haz
0x41haz.0x41haz: ELF 64-bit MSB *unknown arch 0x3e00* (SYSV)

We can also run checksec to check if there are any binary security controls in place like PIE or NX.

$ checksec 0x41haz.0x41haz
                                                   Checksec Results: ELF                                                   
┏━━━━━━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━┓
┃             ┃     ┃     ┃        ┃         ┃       ┃         ┃         ┃         ┃           ┃             ┃  Fortify   ┃
┃ File        ┃ NX  ┃ PIE ┃ Canary ┃  Relro  ┃ RPATH ┃ RUNPATH ┃ Symbols ┃ FORTIFY ┃ Fortified ┃ Fortifiable ┃   Score    ┃
┑━━━━━━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━┩
β”‚ 0x41haz.0x… β”‚ Yes β”‚ Yes β”‚   No   β”‚ Partial β”‚  No   β”‚   No    β”‚   No    β”‚   No    β”‚    No     β”‚     No      β”‚     0      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Note: This is using the pip version of checksec which is available here.

Both NX and PIE are enabled. NX enabled means that executable bit isn't set, that is, certain areas of the memory address space are non-executable. Thus meaning we cannot execute data (if we were to supply it, by overwriting the RIP, for example). PIE stands for Position Independent Executable. This is also a binary protection measure which ensures that the binary is loaded into random memory addresses every time the binary is executed (although certain parts of the binary may reside in a static parts of memory).

We can also conduct basic static analysis of the binary and look at the strings within it. For example:

$ strings -n 6 0x41haz.0x41haz
/lib64/ld-linux-x86-64.so.2
strlen
__cxa_finalize
__libc_start_main
libc.so.6
GLIBC_2.2.5
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
2@@25$gfH
[]A\A]A^A_
=======================
Hey , Can You Crackme ?
=======================
It's jus a simple binary 
Tell Me the Password :
Is it correct , I don't think so.
Well Done !!
GCC: (Debian 10.3.0-9) 10.3.0
.shstrtab
.interp
.note.gnu.build-id
.note.ABI-tag
.gnu.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rela.dyn
.rela.plt
.plt.got
.rodata
.eh_frame_hdr
.eh_frame
.init_array
.fini_array
.dynamic
.got.plt
.comment

We can see some pretty typical strings in the binary such as calls to the libc shared object etc. However, there are strings which ask for a password and provides different output based on the input. This tells us, most likely, that the binary will feature an if/else statement and will jump to different parts of memory depending on the input provided by the user.


The first protection

Recall from our file command the output:

0x41haz.0x41haz: ELF 64-bit MSB *unknown arch 0x3e00* (SYSV)

This is not typical of a normal binary especially this segment of the output MSB *unknown arch 0x3e00* (SYSV). The file here is treated directly as an executable. We need to edit the ELF header in the binary (specifically changing the sixth bit which defines endianness); in this case it is set to the Most Significant Bit, MSB (01 in hex) 1. We must change this from 01 to 02 to switch it to the Least Significant Bit (where the least-significant byte is put first). This is so that the file can be read properly. We can use a hex editor to do this, such as hexedit or hexeditor.

After changing the sixth bit, we can run the file command again on the modified binary.

$ file 0x41haz.0x41haz 
0x41haz.0x41haz: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=6c9f2e85b64d4f12b91136ffb8e4c038f1dc6dcd, for GNU/Linux 3.2.0, stripped

We now can see a lot more information about the binary. We are running an ELF 64-bit executable which has PIE enabled (which we verified using checksec). The binary is made for Linux systems and has been stripped. This means the symbol names (such as names of functions, like main) have been removed. This makes reverse engineering the binary harder, but still possible.


Detonation

We can now denote or run the binary to see how it works before we take a look at it in a program like ghidra or r2. This can be done like so:

$ ./0x41haz.0x41haz
=======================
Hey , Can You Crackme ?
=======================
It's jus a simple binary 

Tell Me the Password :

We get the output above, which we found when analysing the binary using strings. We can now type in a password, and see what the program returns. Of course, we don't know the password as of yet, so we should get an incorrect response.

=======================
Hey , Can You Crackme ?
=======================
It's jus a simple binary 

Tell Me the Password :
hello world
Is it correct , I don't think so.

Typing hello world gives us Is it correct, I don't think so. Clearly we guessed incorrectly. Now to take a closer look at it.


Using Radare2

We can use Radare2 r2 to disassemble the file. We do this with:

$ r2 -d 0x41haz
[0x7fa4c619a360]> aaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Finding and parsing C++ vtables (avrr)
[x] Skipping type matching analysis in debugger mode (aaft)
[x] Propagate noreturn information (aanr)
[x] Use -AA or aaaa to perform additional experimental analysis.

We can typeaaa to analyse all the code. The output tells us what that command does. Once we have analysed the file, we can run afl which analyses function lists (it lists all the functions within the program).

[0x00001080]> afl
0x00001080    1 43           entry0
0x00001030    1 6            sym.imp.puts
0x00001040    1 6            sym.imp.strlen
0x00001050    1 6            sym.imp.gets
0x00001060    1 6            sym.imp.exit
0x00001070    1 6            sym.imp.__cxa_finalize
0x00001165    8 219          main
0x00001160    5 133  -> 56   entry.init0
0x00001120    5 57   -> 50   entry.fini0
0x000010b0    4 41   -> 34   fcn.000010b0
0x00001000    3 23           fcn.00001000
[0x00001080]>

Here, we get a list of all the functions and their corresponding memory addresses. Typically, if it is a C program, we will first want to look at themain() function. We can do this in r2 via s main. That will change the prompt to the memory address of the main function.

[0x00001080]> s main
[0x00001165]> # prompt has been changed

We can now run pdb which will print the basic block assembly of the function.

[0x00001165]> pdb
            ; DATA XREF from entry0 @ 0x109d
β”Œ 219: int main (int argc, char **argv, char **envp);
β”‚           ; var char *s @ rbp-0x40
β”‚           ; var int64_t var_16h @ rbp-0x16
β”‚           ; var int64_t var_eh @ rbp-0xe
β”‚           ; var int64_t var_ah @ rbp-0xa
β”‚           ; var size_t var_8h @ rbp-0x8
β”‚           ; var int64_t var_4h @ rbp-0x4
β”‚           0x00001165      55             push rbp
β”‚           0x00001166      4889e5         mov rbp, rsp
β”‚           0x00001169      4883ec40       sub rsp, 0x40
β”‚           0x0000116d      48b832404032.  movabs rax, 0x6667243532404032 ; '2@@25$gf'
β”‚           0x00001177      488945ea       mov qword [var_16h], rax
β”‚           0x0000117b      c745f2735426.  mov dword [var_eh], 0x40265473 ; 'sT&@'
β”‚           0x00001182      66c745f64c00   mov word [var_ah], 0x4c     ; 'L'
β”‚           0x00001188      488d3d790e00.  lea rdi, str._nHey___Can_You_Crackme___n ; 0x2008 ; "=======================\nHey , Can You Crackme ?\n=======================" ; const char *s
β”‚           0x0000118f      e89cfeffff     call sym.imp.puts           ; int puts(const char *s)
β”‚           0x00001194      488d3db50e00.  lea rdi, str.Its_jus_a_simple_binary__n ; 0x2050 ; "It's jus a simple binary \n" ; const char *s
β”‚           0x0000119b      e890feffff     call sym.imp.puts           ; int puts(const char *s)
β”‚           0x000011a0      488d3dc40e00.  lea rdi, str.Tell_Me_the_Password_: ; 0x206b ; "Tell Me the Password :" ; const char *s
β”‚           0x000011a7      e884feffff     call sym.imp.puts           ; int puts(const char *s)
β”‚           0x000011ac      488d45c0       lea rax, [s]
β”‚           0x000011b0      4889c7         mov rdi, rax                ; char *s
β”‚           0x000011b3      b800000000     mov eax, 0
β”‚           0x000011b8      e893feffff     call sym.imp.gets           ; char *gets(char *s)
β”‚           0x000011bd      488d45c0       lea rax, [s]
β”‚           0x000011c1      4889c7         mov rdi, rax                ; const char *s
β”‚           0x000011c4      e877feffff     call sym.imp.strlen         ; size_t strlen(const char *s)
β”‚           0x000011c9      8945f8         mov dword [var_8h], eax
β”‚           0x000011cc      837df80d       cmp dword [var_8h], 0xd
β”‚       β”Œβ”€< 0x000011d0      7416           je 0x11e8

Going further

We can see there are a few lines which spark some curiosity.

0x0000116d      48b832404032.  movabs rax, 0x6667243532404032 ; '2@@25$gf'
0x0000117b      c745f2735426.  mov dword [var_eh], 0x40265473 ; 'sT&@'
0x00001182      66c745f64c00   mov word [var_ah], 0x4c     ; 'L'

These lines contain hex values which resemble a password. We can put them together and enter them into the binary.

=======================
Hey , Can You Crackme ?
=======================
It's jus a simple binary 

Tell Me the Password :
2@@25$gfsT&@L
Well Done !!

We get the correct answer and can submit the password as the flag, wrapped in THM{}. We can do that manually, or for fun use sed like so:

echo '2@@25$gfsT&@L' | sed "s/.*/THM{&}/"

Summary

This challenge was very easy, and did not require any binary exploitation. However it provided us with a step into using a disassembler like r2 and showcasing some of the commands used to analyse an executable.

Last updated