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/sh
into memory - Get
NULL
into memory - Push
NULL
onto the stack - Push
happy
onto the stack - Push
happy[0]
onto the stack - Push value for
execve
onto 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 00
as the input, everything would go wrong since\x00
is 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 9
jmp 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.