Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

writing shellcode: why my shellcode won't work?

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.

like image 729
Gnijuohz Avatar asked Oct 23 '13 02:10

Gnijuohz


2 Answers

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.

like image 107
72DFBF5B A0DF5BE9 Avatar answered Nov 07 '22 23:11

72DFBF5B A0DF5BE9


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.)

like image 25
Edward Avatar answered Nov 07 '22 23:11

Edward