Creating ShellCode On Narnia
The Shellcoders Handbook was the foundation of most of the write up. It’s based largely on the contents of Chapter 3 - Spawining a Shell. I’ve adapted it to work on the OverTheWire server used for the narnia challenges - mostly making sure the various protections are disabled. These protections are disabled since we want to rely on very specific locations in order to set the shellcode up correctly. This particular bit of shell code just calls execve with /bin/sh. When combined with a vulnerable program that has elevated privileges it’ll spawn a shell with that access level - in the case of the narnia challenges, the next level. There already exists a lot of pre-generated shellcode out there (http://shell-storm.org/shellcode/), however part of the fun is to understand the fundamentals and try doing it myself.
Fair warning, this is my first attempt at creating some shellcode.
To get started, need a simple C program to launch a shell using execve. Since this being done on the OverTheWire servers the only writable location is /tmp/, so everything will take place in a folder that was created there.
|
|
Once that source is saved, it’s time to compile it. To disable all of the things that would get in the away when creating some shellcode, it needs to be compiled with with static set in GCC so there’s no dynamic linking, no stack protector, and no position-independent-executable (pie). Additionally, to make things easy, let’s make sure we compile it as a 32bit binary (-m32).
|
|
If all went well and no compilation errors, try running it to make sure it actually spawns a shell:
|
|
Now the fun part, dumping out all the assembly (with objdump) and identifying what actually matters for creating the shellcode in as few bytes as possible.
First the main function:
|
|
Now the execv function, which is the ultimate target and focus for creating the shellcode:
|
|
These should prepare the registers with the values of happy[0], happy and the NULL of the system call we’d like to make (0xb, aka execve).
|
|
This line moves 0xb into $eax to handle the execve call.
|
|
Verifying by taking a peek at $ebx, first get the address stored in $ebx:
|
|
Then dump the contents of that address:
|
|
Next, to figure out what the call does. Ideally this is the way to get to the syscall - which in theory works it way to an int 0x80 call (aka, a transition to the kernel):
|
|
Stepping into this function reveals the following, which isn’t quite what was expected:
|
|
However, we know that this must eventually get to the right place as we end up in the Kernel calls. But it’s still unclear what 0x80eb9f0 refers to. So objdump to the rescuse to help sort that out a bit more.
Using the following command we can get more info:
|
|
Using 40010708 and converting it from little-endian to big-endian, we get: 08070140
And looking back at the overall dump with 08070140:
|
|
Now that all of the important pieces have been located with their appropriate byte code. It’s time to work on extracting the very minimum we care about and coming up with a game plan of how to implement the shellcode. First lets find the string for /bin/shell and where it gets stored into memory.
The overall goal is the following:
- Get
/bin/shinto memory - Get
NULLinto memory - Push
NULLonto the stack - Push
happyonto the stack - Push
happy[0]onto the stack - Push value for
execveonto the stack - Make the
syscall
First up is the string /bin/sh, by looking over main
|
|
Next up is the NULL to be stored in adjacent memory:
|
|
Now to push a NULL onto the stack so we can populate the register with it:
|
|
And next up the address to the array “happy” on to the stack:
|
|
And finally the address to string onto the stack:
|
|
And now breaking down execve, first the NULL:
|
|
Next the address for the array (happy):
|
|
And once again (happy[0]):
|
|
And finally the magic value of 0xb for the execve syscall:
|
|
Once the stack is prepared we call into the magic function that ultimately gets us to <_dl_sysinfo_int80>:
|
|
Which is just:
|
|
There are two things we need to keep in mind when preparing the shellcode.
-
Be clever in removing the nullbytes/null terminators (
\x0) from the assembly code. If we were to just use the the assemblyb8 0b 00 00 00as the input, everything would go wrong since\x00is a null terminator, therefore something clever needs to be done. -
Need to figure out a way to do relative addressing, since it is not possible to safety rely on any address of the string. The trick is using a jump, followed by a call backwards, forming a bit of sandwich of code. The sandwich method roughly has this outline:
1 2 3 4 5 6 7 8 9jmp short GotoCall # The jmp to a label shellcode: pop $esi ... <shellcode goodness> ... GotoCall: call shellcode # call backwards to the shellcode db '/bin/sh/' # define byte of the string
The pop $esi is important here, since it’ll put the address of '/bin/sh' into $esi. This happens since the return address from the call will be the address of the next instruction after the call, and it was pushed into the stack when doing call. From there we can use relative addressing to $esi, no need to worry about specific memory addresses.
Now to put it all together:
|
|
Note, the order of populating our registers isn’t in the same order as our disassembly from our C program. The important thing here is making sure the correct registers are filled with the correct values. I.e. The disassembly showed that the value of 0xb was stored in $eax, so we accomplish this with $al. The address of the string needed to be in $ebx. $ecx and $ebx get the junk data.
Compile with:
|
|
Link with elf-i386 (since targeting 32bit):
|
|
Now use objdump to check out waht the final assembly code looks like and start verifying that it’s as expected, and most importantly making sure there’s no \x00 bytes:
|
|
Since it all looks good, time to convert it to some shellcode, using a bit CommandlineFu for extracting the shell code easily:
|
|
Create a test app, using a ton of ():
|
|
Compile and disable security with a ton of options:
|
|
Run it and verify it works. If all went well, the shelltry should spawn a shell. Going forward it should be possible to use this shellcode for all of the narnia challenges and we no longer have to rely on something created by someone else.