Block Magnates

The New Crypto Publication on The Block

Follow publication

Software Reverse Engineering: Diffusing Phase 1

--

Baby, I hate little secrets you keep from me x_x

The article continues the previous one which talked about the initial approach of reverse engineering Windows version of bomb binary using Windbg. This part will diffuse phase_1 of bomb binary on Linux platform using GNU Gdb.

This writeup is a part of the article series —

NOTE: Do use the Linux version of bomb binary (an ELF binary) which can be downloaded here and verify the file-type before proceeding further to confirm we are using the same binary.

Disassemble main for Linux binary

Disassembly for main() is quite simple in ELF version as compared to Windows one.

NOTE: The function calling conventions for x64 binaries on Linux differs from what we saw for Windows platform. First 6 integer parameters for a function call are passed in registers (in order)— RDI, RSI, RDX, RCX, R8 and R9. Remaining parameters (beyond first 6) are pushed onto the process stack in reverse order (i.e. starting from right to left). You can find more information about calling conventions in System V ABI.

disass main

I assume read_line() (which is called just before phase_1) does what the name suggests, i.e. reads a line of input from the user and passes that as a parameter to phase_1 (const char *) (see instruction @ 0x00005555555554a5 <main+92>: mov %rax,%rdi), but no one is stopping us from confirming our hypothesis ;). For now, let’s stick to the code flow inside phase_1().

Rescue Mission: Phase_1

Setting up a breakpoint at <main+95> (0x00005555555554a8), we step into (si) the phase_1() and disassemble it to have an overview of what this function does.

(gdb) b *main+95
(gdb) r
(gdb) disass main
Dump of assembler code for function main:
0x0000555555555449 <+0>: endbr64
...
0x00005555555554a0 <+87>: call 0x555555555c56 <read_line>
0x00005555555554a5 <+92>: mov %rax,%rdi
=> 0x00005555555554a8 <+95>: call 0x5555555555a7 <phase_1>
0x00005555555554ad <+100>: call 0x555555555d9e <phase_defused>
...
(gdb) si
disass phase_1

Looking at the disassembly—

  • <phase_1+15>: We see a call to strings_not_equal() which as the name suggests, probably compares 2 strings. This function takes 2 parameters, first parameter is the input string provided by the user and second parameter is the string residing at location 0x555555557150 , which falls inside DATA segment of process address space (Umm… a hardcoded string?). Therefore, function prototype for this function turns out to be —

int strings_not_equal (const char *, const char *);

  • The next 2 instructions <phase_1+20 to 22>, the function tests (bitwise AND without storing any results) the value returned from strings_not_equal() (i.e. stored inside eax). If the return value is not zero, the bomb explodes by calling explode_bomb() , otherwise the phase is diffused.

So, we need to make sure strings_not_equal() returns a zero integer. Below is the reversed C code for phase_1()

reversed phase_1 ()

Trying out that DATA string

Since, we see a possible string comparison by the name of strings_not_equal(), why not try out the hardcoded string (I am just a renegade hockey mom.) as password to the first phase.

testing Phase_1 input string

Phase 1 was quite straight forward which relied on validating user input by hardcoded string comparison (remember this string from strings.exe output in previous article). Also, when we send a SIGINT signal to this program (Ctrl+c represented by ^C symbol), the signal handler registered in bomb_initialize() issues those puts()/sleep() calls as discussed in previous article.

NOTE: Hardcoded strings used by the program can be found in .rodata section of ELF binary. We can use readelf (readelf -p .rodata ./bomb) to interpret strings from any section as shown below in contrast to using strings ./bomb utility (as discussed in previous article) which tries to interpret strings from the entire binary.

Dumping strings from .rodata section of ELF binary

Mama, I got some trust issues !

Since the symbol table is present as debugging information in the ELF binary (due to binary NOT being stripped), it becomes easy to assume that strings_not_equal() checks for the equality of strings. Still, there may be reasons for not trusting the debugging information supplied with the program. Some of them might be —

  • The algorithm applied inside this function might differ from what one can interpret by its name.
  • Or perhaps, the target software application is trojanized or having any other malicious attribute (such as hooks, trampolines etc.) hidden inside function body.

NOTE: Having debugging symbols in malware should be considered as a sign of deception and NOT a mistake by the malware author !

I encourage you to try reversing strings_not_equal() before moving further.

disass strings_not_equal
  • <strings_not_equal + 0 to 32>: Both strings are checked for their length and if they are not equal, function returns 1 to the caller (see cmp instruction @ strings_not_equal+37).

Memory access to data structures is performed using r/mX form which is mostly of the form — [base + index*scale + displacement]. Here, compiler decides to perform array access using the same form without displacement which can be seen on <strings_not_equal+ 54 & 64) in AT&T syntax as cmp %dl, 0x0(%rbp,%rax,1) , i.e. displ(base, index, scale). Can we spot a loop construct implemented by compiler in this function ?

  • If both strings are equal in length, then a loop iterates over both strings comparing each character of both input (pointed to by RBX and each character getting stored into RDX) and hardcoded string (pointed to by RBP) (from <strings_not_equal + 54 to 70>). If all characters are same, it returns a 0 to the caller.

Below is the pseudocode for this function —

Pseudocode for strings_not_equal

Epilogue

Diffusing phase 1 didn’t involve much effort as it used simple hardcoded string comparison technique which in case of passwords may be vulnerable brute-forcing DATA strings from read-only sections of binary or may easily be spotted by basic binary analysis techniques. This technique to validate a user is/should NOT be used in the world of software engineering. Let’s continue diffusing phase_2.

Cheers,
Abhinav Thakur
(a.k.a
compilepeace)

Connect on Linkedin

--

--

No responses yet