Stack Smashing
Last updated
Last updated
What you need to do:
Find the address of the buffer on the stack
Find the address of the EIP on the stack, that was pushed for the RET instruction
Subtract #1 from #2. That’s how much padding space you have
Fill the padding space with random padding
After the padding space, overwrite the EIP which was saved on the stack. It should point to the NOP slide in #6. Put it in the middle of the NOP slide due to differences in the stack address space (view section “Stack address changes”)
After the overwritten EIP, put a NOP slide. You can be generous with this, like 128 bytes for example. But making it ridiculously large may break the program.
After the NOP slide, put your shellcode
Note: You can also put the shellcode inside the buffer, but the above method is better, since then you won't be limited by the buffer size.
You need to know how many bytes you need to overflow to get to the saved return pointer. You can use the cyclic generator from pwntools to easily identify it.
You can generate the overflow like this (in ascii in this case, but you can also leave them as bytes for exploitation):
Alternatively, you can use the pwntools command-line program:
Use the generated overflow to cause a segfault in the application. Then look at dmesg:
You can see that the instruction pointer was 61616b62, which corresponds to the ascii text 'aakb'. Also, it's in little endian, so it corresponds to 'bkaa' in the cyclic generator's output.
Echo out the overflow ascii and find out where the 'bkaa' string occurs in the cyclic output. That's the place where you need to put the address you want to overflow the saved return pointer with. The amount of bytes up until that string is the amount of bytes you need to get to the return pointer on the stack from the initial overflow location.
Here’s the script I used to generate the input file for the phoenix stack5 challenge:
Remember to cat the input in like this:
Note: In hindsight, it would be better and easier to do this with pwntools.
Note: This is not a real issue when you get to more advanced exploitation methods, because you don't ever provide absolute stack addresses anyways due to ASLR.
When you use a debugger like GDB or radare2, then that debugger will put environment variables on the stack. This will make the address space move around, and when running the program normally, the absolute addresses you found in your debugger will no longer be entirely correct.
Also, argv will be smaller if you call the program with the full path instead of a relative path (‘./program’ is smaller than ‘/path/to/program’).
Additionally, the current working directory is also on the stack, so different working directories will result in different addresses.
This is one reason why NOP slides are important. Note that the addresses relative to the base pointer should still stay the same.
So keep in mind - use a decent-sized nopslide and put the EIP/RIP into the middle of that nopslide. That way the address changes won’t mess up your exploit so easily. However, if you add too many, then the process will crash (probably because the stack runs out of space?) When I added 700 bytes of NOPs it was fine, but when I added 7000, then that was trouble.
I made a test file which shows differences in an address on the stack for different execution environments (This was run on the Exploit Education Phoenix machine. The GDB is modded there. Probably that’s why radare2 is better in this case)
Also, I didn’t try to simply attach the debugger. Maybe then the address wouldn’t change?
Execution type | Path to executable | Address |
Normal | ./catch-exec-test | 0x7fffffffe4b0 |
Normal | /home/user/stack/stack5/catch-exec-test | 0x7fffffffe470 |
Radare2 | ./catch-exec-test | 0x7fffffffe490 |
Radare2 | /home/user/stack/stack5/catch-exec-test | 0x7fffffffe460 |
Gdb with environment variables | ./catch-exec-test 0x7fffffffe460 | 0x7fffffffe460 |
Gdb without 2 environment variables unset | ./catch-exec-test 0x7fffffffe480 | 0x7fffffffe480 |
Gdb with "catch exe" (another subprocess started using execve with no environment variables) | ./catch-exec-test | 0x7fffffffed60 |
Conclusion: On the Phoenix machine, Radare2 is the most accurate when you specify the full path. It was only off by 0x10.
These lines should give the same environment variables as during normal execution. Run these inside gdb.
Note: This didn’t work with the modded GDB of Phoenix
Radare2 has the configuration clearenv=true
in the rr2 file, but it didn’t result in the same address for me as running normally. So, it doesn’t seem to work.
This script may help you:
It will keep the locations the same between running the program with GDB and running it normally. Didn’t work with radare2 as of july 2020. Doesn’t work with the modded GDB of Phoenix. Doesn’t work with ASLR enabled.
If your shellcode is on the stack, and your shellcode also pushes stuff onto the stack, then if the ESP is located where your shellcode is at, then your shellcode will corrupt itself by pushing data into its own code.
This is why you shouldn’t put your shellcode too close to the EIP, instead, you should have a NOP slide between them. Because that’s also where the stack pointer is. Also, remember that the stack grows downward, so putting the shellcode after the overwritten EIP (up higher) is a good idea.
LiveOverflow video discussing this: