Executing Shellcode

This section will demonstrate hijacking the execution flow of a program to execute shellcode.

Live Overflow - Using a buffer overflow to execute shellcode

Getting Started

We will be using the following code for the rest of this section:

Note: Compile with gcc -o bof3 bof3.c -fno-stack-protector -zexecstack

The compilation flag -fno-stack-protector prevents the compiler from placing stack canaries (integers that detect whether or not a stack-based buffer overflow has occured). The flag -zexecstack disables the NX-bit of the binary which lets us execute shellcode placed on the stack.

You may notice that we no longer have a clear goal like modifying a variable or jumping to a win function.

How can we leverage this vulnerability to create a much more dangerous result like arbitrary code execution?

Debugging with GDB

As our exploits and binaries get more and more complicated, it is important that we have a way to analyze how they work behind the scenes. For this, we will be using a debugger - more specifically the GNU Debugger or GDB. If you are unfamiliar with debuggers, you should read the short introduction and the GDB section here.

Note: Any GDB examples you see run are with the pwndbg gdb extension.

To run our program in gdb, we run the command gdb -q ./bof3. The -q flag stands for “quiet” and just removes the license information that is printed when you launch gdb.

Here is a table of common GDB commands that you will see throughout this section:

Command

Description

r / run

run the program

r < exploit

run the program with a file called “exploit” as input

c / continue

continue execution of the program

s / step

execute one line and break; go into function calls

n / next

execute one line and break; do not go into function calls

break *address

stop execution at a specific address

Let’s try running our program in GDB:

That doesn’t help us much, but we can build on that by setting a breakpoint at the main function. A breakpoint makes your program stop whenever a certain point in the program is reached. To set a breakpoint, you use the format break *address. The break command also takes function names if your binary hasn’t been stripped of its symbols.

Our breakpoint is now set at main’s address which we can verify by using the objdump command:

When we run the program, the program’s execution will be stopped when it reaches main’s address.

Don’t worry too much about pwndbg’s output. It will all make sense in later sections. For now, you should at least be somewhat familiar with registers.

Controlling EIP

If you are not familiar with registers, please visit and reread the Understanding x86 Instructions section.

NOP Sled

Pwntools Exploit

Last updated