编写shellcode:为什么我的shellcode不起作用?

时间:2013-10-23 02:45:01

标签: shellcode

我目前正在编写一个shellcode,它利用了使用puts函数的目标程序。该计划如下:

#include <stdio.h>
main() {
    char buf[123];
    puts(gets(buf));
}

我想要做的是溢出此缓冲区并使用一些参数调用execve。我有一个用c / inline程序集编写的测试程序可以用一些参数调用execve,然后我用gdb从这个程序中获取shellcode。根据我的理解,堆栈布局如下所示:

  

| -------缓冲液(+填充)--------- | --------- --------- SFP | ---- --- RET ------------- |

通过查看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       

我认为缓冲区和填充占用 128 字节,sfp和返回地址各占 8 字节,因此 144 字节总数。基于此,我的nop雪橇,有效负载和新的返回地址(等于缓冲区的地址)组合(即我的shellcode)也应该是 144 字节。例如,如果我的有效载荷是36个字节,因为返回地址占用8个字节,我的nop sled将是100个字节。但是当我这样做的时候它没有用。所以我想也许我理解堆栈布局的方式是错误的。这是错的吗?

请注意,在我的情况下,缓冲区地址已知,并且使用execstack将堆栈设置为可执行文件,并使用setarch关闭ASLR。因此,如果返回地址被缓冲区的地址覆盖,那么写入该缓冲区的代码就会运行。

我正在使用x86 64位计算机。

如果我对堆栈布局的理解是正确的,我会提供有关我的shellcode的更多信息。

2 个答案:

答案 0 :(得分:1)

1)您没有利用易受攻击的代码,因为它具有puts()函数,您正在利用它,因为它正在使用gets()函数,这很容易受到堆栈溢出的影响。

2)当你有一个char buf[123]时,如果输入122个字符然后输入一个空终止符,则堆栈就可以了。但是当你输入更多时,就会发生这种情况:

让我们想象它是buf [4],当你做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

现在你也会覆盖函数返回地址,所以它也将是AAAA 0x41414141! 从那里你需要将返回地址指向你的shellcode地址,以便执行shellcode。

因此,在调用函数和溢出时,布局为:

Buffer for temporary storage
local variables
The saved EBP
Function return address
Function's arguments
Stack frame

所以从下到上。 实际上对于大变量,最好使用metasploit pattern_offset.rb,它会生成大字符串,当您找到EIP值时,可以使用patter_offset.rb的输出来检测覆盖EIP所需的EXACT填充以便执行的shellcode。

所以实际上要覆盖函数返回地址,大多数情况下你需要[变量大小] + 8.但它取决于局部变量,它们的大小,它们的顺序等等。它还取决于编译器,体系结构等。主要是它的通过尝试和pattern_offset.rb等完成。

答案 1 :(得分:1)

当例程开始时,它在堆栈上有返回地址。我将假设rsp = 0x428的值,以便我们可以查看真实(但任意选择)的数字:

0x428 return addr

然后我们推rbp

0x428 return addr (8 bytes)
0x420 original rbp (8 bytes)

然后我们分配缓冲区,即123字节+填充= 0x80

0x428 return addr (8 bytes)
0x420 original rbp (8 bytes)
0x418 last eight bytes of buffer+padding 
  ...
0x3a0 buffer (128 bytes)

因此,如果您希望在0x428处粉碎返回地址,则gets()的响应总计必须为144个字节,最后8个字节由shellcode修改为指向其他位置(通常到缓冲区本身)。

假设堆栈图中的 sfp 表示“已保存的帧指针”(也称为rbp),那么是,您的理解是正确的。如果是不适合你,那么你的shellcode可能有问题。 (这可能是一个单独的问题。)