Executing Shellcode
This section will demonstrate hijacking the execution flow of a program 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 -zexecstackThe compilation flag
-fno-stack-protectorprevents the compiler from placing stack canaries (integers that detect whether or not a stack-based buffer overflow has occured). The flag-zexecstackdisables 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