I'm currently writing a shellcode that exploit a target program that uses the puts
function. The program looks like this:
#include <stdio.h>
main() {
char buf[123];
puts(gets(buf));
}
What I want to do is overflow this buffer and invoke execve
with some arguments. I have a test program that's written in c/inline assembly that can invoke execve
with some arguments, then I use gdb
to get the shellcode from this program. From my understanding, the stack layout looks like this:
|-------buffer(+padding)---------|---------sfp---------|-------ret-------------|
By looking at part of the assembly code of the target program generated by gcc:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
addq $-128, %rsp
leaq -128(%rbp), %rax
movq %rax, %rdi
call gets
movq %rax, %rdi
call puts
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
I think buffer and padding take up 128 bytes, and sfp and return address each takes 8 bytes, so that's 144 bytes in total. Based on this, my nop sled ,payload and new return address(equals to the address of the buffer) combined(that is, my shellcode) should be 144 bytes too. For example, if my payload is 36 bytes, since the return address takes up 8 byte, my nop sled will be 100 bytes. But when I did it that way it didn't work. So I think maybe the way I understood the stack layout was wrong. Is it wrong?
Notice in my case the buffer address was known and the stack was set as executable by using execstack
and ASLR was turned off by using setarch
. So if the return address was overwritten by the address of the buffer, the code written in that buffer will run.
And I'm working on a x86 64bit machine.
If my understanding of the stack layout is right, I'll put on more information about my shellcode.
1) You are not exploiting the vulnerable code because it have puts()
function, you are exploiting it because it is using gets()
function, that is vulnerable to stack overflow here.
2) When you have a char buf[123]
, if you enter 122 chars and then one null terminator, stack is OK. But when you enter more than that, this is what happens:
Let's imagine it's buf[4], when you do gets()
input AAAA
EBP - 4 => will be AAAA
input AAAAAAAA (8 bytes)
EBP -4 => AAAA
EBP also => AAAA
if you enter 12x A
function return address will be 0x41414141
now you'll also overwrite function return address, so it will be AAAA 0x41414141 too! From there you need to point return address to your shellcode address in order to execute shellcode.
So when calling a function and for overflowing, the layout is:
Buffer for temporary storage
local variables
The saved EBP
Function return address
Function's arguments
Stack frame
so it's from below to top.
Actually for large variables it's better to use metasploit pattern_offset.rb
, it generates large string and when you find out EIP value, you can use patter_offset.rb
's output to detect EXACT paddings required to overwrite EIP in order to execute shellcode.
So actually to overwrite function return address, mostly you need [variable size] + 8. But it depends on local variables, size of them, order of them, etc. Also it depends on compiler, architecture, etc. Mostly it's done by trying and pattern_offset.rb etc.
When the routine begins, it has the return address on the stack. I'll assume the value of rsp = 0x428
just so we can look at real (but arbitrarily chosen) numbers:
0x428 return addr
Then we push rbp
0x428 return addr (8 bytes)
0x420 original rbp (8 bytes)
Then we allocate buffer, which is 123 bytes + padding = 0x80
0x428 return addr (8 bytes)
0x420 original rbp (8 bytes)
0x418 last eight bytes of buffer+padding
...
0x3a0 buffer (128 bytes)
So if you're expecting to smash the return address at 0x428, your response for gets()
will have to be 144 bytes long total, with the last 8 bytes being modified by your shellcode to point somewhere else (typically to the buffer area itself).
Assuming that sfp in your stack diagram means "saved frame pointer" (otherwise known as rbp
) then yes, your understanding is correct. If it's not working for you, then it's your shellcode that's probably faulty. (And that would probably be a separate question.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With