我正在尝试使用这个函数使用strcpy的经典溢出来重新创建堆栈缓冲区溢出:
#include <stdio.h>
#include <string.h>
void main(int argc, char **argv) {
char buf[100];
strcpy(buf,argv[1]);
printf("Done!n");
}
我已经尝试编译所有不同的标志,以删除堆栈保护gcc -o vuln vuln.c -fno-stack-protector -g -z execstack
以及删除ASLR与sudo echo 0 > /proc/sys/kernel/randomize_va_space
。
我可以得到我的nop-shell代码地址覆盖保存的EIP,但它在RET上崩溃。我发现在gdb中禁用SIGSEGV与HANDLE SIGSEGV ignore
等,但是在gdb之外运行,我继续得到分段故障,无论我设置返回地址到哪里。
除了获得旧版本的gcc之外,还有什么想法吗?
Linux遵循W^X原则:它将内存页标记为不可执行的,除非它们是代码段的一部分。这超出了已编译应用程序的范围,这是有充分理由的。操作系统承担这个责任来防御来自系统上执行的任何程序的缓冲区溢出攻击;尤其是那些主动尝试执行缓冲区溢出攻击的程序,比如你的。
您正在尝试通过buf
在堆栈上编写代码并覆盖函数的返回地址以跳转到新注入的代码中执行。当函数返回时,程序计数器被设置为覆盖的返回地址,该地址现在指向堆栈内存。当程序计数器由于栈内存页的执行权限被撤销而试图执行下一条指令时,会抛出SIGSEGV
。
如果要从堆栈执行,必须关闭操作系统的堆栈保护。
幸运的是,Linux为这种情况提供了execstack,由用户自行决定使用。
查看这个Unix &Linux Stack Exchange post .
调试注入数据
如果你在gdb中得到SIGSEGV
,这可能意味着在你的缓冲区溢出尝试中有一些错误。为了避免混淆main
末尾的清理,我建议创建一个从main调用的函数来处理缓冲区溢出:
#include <stdio.h>
#include <string.h>
char injection_code[] = ""; // buffer overrun data here
void foo () {
char buf[100];
// memcpy used to copy the full injection string, including any nested 0s
memcpy(buf, injection_code, 108); // +8 here to handle 64-bit system RAs
}
int main (int argc, char** argv) {
foo();
printf("Done!n");
return 0;
}
使用GDB进行调试。我建议在foo
的末尾设置一个断点,并检查寄存器是否与info registers
相符。您可能对指令指针最感兴趣,您可以使用info registers rip
(64位)或info registers eip
(32位)来检查。
您可以通过在GDB中使用print
或x/x
来探索堆栈的样子。例如,您可以检查$rbp+8
以查看堆栈上的返回地址(RA)。
GDB将SIGSEGV
上的ret
指令,如果RA指向一个无效的位置:
Program received signal SIGSEGV, Segmentation fault.
0x00000000004005bc in foo () at bufferoverflow.c:27
您可以根据程序的汇编(在GDB中使用disassemble function_name
)检查发生故障时的指令指针地址(在上面的示例中,它将是0x00000000004005bc
)来检查ret
指令是否出错。
如果返回地址指向堆栈的后面,它可能会抛出一个非法指令的SIGILL
,这意味着你的返回地址可能没有正确对齐,或者你注入的指令是畸形的:
Program received signal SIGILL, Illegal instruction.
0x00007fffffffdc13 in ?? ()
同样,您可以使用GDB来探索您的堆栈以了解原因。
GDB对于获得正确的缓冲区溢出结构很有用。但是,一旦它按预期工作,请记住,在GDB外部执行时,内存地址可能会被移动,特别是在没有调试标志(-g
)的情况下进行编译时。你需要稍微改动一下返回地址,以便在"实时"环境中得到你想要的地址。
除了
的
一般来说,直接运行注入代码的缓冲区溢出攻击通常在当前的堆栈保护机制下是不可行的。通常需要存在额外的漏洞才能执行任意代码。
我终于弄明白了这个问题。GCC将我的指令和变量对齐到16字节边界。因此,它在序言中添加了汇编指令,例如lea ecx,[esp+0x4]
等,在函数尾声中也添加了类似的指令,它将堆栈指针移动了一点,并且混淆了堆栈的布局。我用-mpreferred-stack-boundary=2
重新编译,修复了GDB中的问题。现在我只需要按你说的处理那些回信地址。谢谢! !