我目前正在编写一个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的更多信息。
答案 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可能有问题。 (这可能是一个单独的问题。)