This is by far the most difficult ELF32 binary I’ve analysed, primarily because the symbols are stripped, making commands like diassemble main in gdb impossible.
The share library funcitons in libc.so referenced in this program are shwon as ds:0xnnnnnnn, which involves some work to guess who is who.
The original article does not show the correct password (however, works with some alternatives) in the first place, which will be worked out in this write-up.
I’ve learned a lot when trying to analyse this binary, therefore, I wanted to note down the parts I think is important for future reference and help other people.
If you would like to give the binary a try, feel free to download it from the original webiste above.
In this write-up, I will give more words on analysing the logic of the binary pertaining to revealing the password.
Let’s get started!
(BIG THANKS TO THE AUTHOR OF THE ORIGINAL ARTICLE! BTW)
Since the program has anit-debugger built-in, we need to preload a fake ptrace() function from our custom library to get around it, the steps are well-documented there, which won’t repeat there.
One thing I’d like to add though, is if we need to load it up in gdb for dynamic analysis, we can use the following command to set the LD_PRELOAD environment variable.
gdb-peda$ set environment LD_PRELOAD=./fake.so
Above assumes gdb-peda is used, and the custom fake.so share library is in the same directory as the hackme binary.
Reversing the logic
As explained in the original write-up, the code that is pertaining to the password authentication can be narrowed down to the following snippet, which instead, will be annotated thoroughly in this post,
;set eax to 0 to prepare for 80485c1 80485bc: 31 c0 xor eax,eax
;dlopen@plt is used as a base addr for jmp, dlopen@plt+0x1f1 = 80485c1 80485be: eb 01 jmp 80485c1 <dlopen@plt+0x1f1>
80485c0: 40 inc eax
;user input stored at [ebp+eax*1-0x7c], increment eax to loop thru the string until the end ('\0') 80485c1: 80 7c 05 84 00 cmp BYTE PTR [ebp+eax*1-0x7c],0x0
80485c6: 75 f8 jne 80485c0 <dlopen@plt+0x1f0>
;after the end of user input is reached, proceed to the next 80485c8: 31 db xor ebx,ebx ;password should be 19 chars long 80485ca: 83 f8 13 cmp eax,0x13 80485cd: 0f 94 c3 sete bl;sete set if equal
;esi=10 80485d0: be 0a 00 00 00 mov esi,0xa
; random() returns a random number (random#), which makes the remainder of random#/0x13 random. ; it then picks the (remainder+1)th char in user input, and the 1-byte hex code positioned the same in the predefined sequence. ; remainder decides the # of times eax will be applied with the loop (add + multiply itself) ; user input will be xor'ed with the least 1-byte of the product of above step, to see if the result equals to predefined hex code in the same position. ; above process is repeated 10 times, since the position picked in the user input is random, we need to make sure every char in user input satisfy above xor'ed check. 80485d5: e8 b6 fd ff ff call 8048390 <random@plt> 80485da: b9 13 00 00 00 mov ecx,0x13
;take the remainder as an index, store the [remainder+1]th char from user input in edi, ;and from the same poistion from predefined hex sequence at base addr 0x804869c, store it in cl 80485e4: 8a 8a 9c 86 04 08 mov cl,BYTE PTR [edx+0x804869c] 80485ea: 0f b6 7c 15 84 movzx edi,BYTE PTR [ebp+edx*1-0x7c] 80485ef: 42 inc edx
;address of printf() is moved into eax at 804861f 8048638: ff d0 call eax 804863a: 83 c4 10 add esp,0x10 804863d: 8d 65 f4 lea esp,[ebp-0xc] 8048640: 5b pop ebx 8048641: 5e pop esi 8048642: 5f pop edi 8048643: 5d pop ebp 8048644: c3 ret
Reversing the password
The logic is clear now, let’s write a python script to do the reverse to reveal the password.