【发布时间】:2013-11-01 03:36:21
【问题描述】:
我目前正在编写一个利用使用puts 函数的目标程序的shellcode。程序如下所示:
#include <stdio.h>
main() {
char buf[123];
puts(gets(buf));
}
我想要做的是溢出这个缓冲区并使用一些参数调用execve。我有一个用 c/inline 程序集编写的测试程序,它可以使用一些参数调用 execve,然后我使用 gdb 从这个程序中获取 shellcode。据我了解,堆栈布局如下所示:
|-------buffer(+padding)---------|---------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 sled、payload 和新的返回地址(等于缓冲区的地址)组合起来(即我的 shellcode)也应该是 144 字节。例如,如果我的有效载荷是 36 个字节,由于返回地址占用 8 个字节,我的 nop sled 将是 100 个字节。但是当我这样做时,它不起作用。所以我想也许我理解堆栈布局的方式是错误的。错了吗?
请注意,在我的情况下,缓冲区地址是已知的,并且使用 execstack 将堆栈设置为可执行文件,并使用 setarch 关闭了 ASLR。因此,如果返回地址被缓冲区地址覆盖,写入该缓冲区的代码就会运行。
我正在使用 x86 64 位机器。
如果我对堆栈布局的理解是正确的,我将提供有关我的 shellcode 的更多信息。
【问题讨论】:
-
如果您知道要跳转到的确切地址,我认为您真的不需要 nop 雪橇。不过不应该受到伤害,而且您需要某种填充来到达并覆盖返回地址。也可能是nops。
-
@user2357112 是的。无论哪种方式,它都应该工作。
-
这听起来更像是一个“发现大脑放屁”的错误,而不是“我的理解有缺陷,我需要学习一些东西”的错误。我认为您对堆栈布局的理解没有任何明显错误。
-
@user2357112 那是实际代码。它可以用 gcc 编译。我认为这个程序应该是坏的......作为一个例子
-
运行你的shellcode会产生段错误?您使用的是哪个版本的 Linux 内核?在后来的内核(和 GCC)中塞进了一堆相当漂亮的安全特性,这些特性阻碍了大多数简单的 shellcode 尝试。