2016-09-28 55 views
3

我想要使用此功能采用了经典的溢出与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以及使用sudo echo 0 > /proc/sys/kernel/randomize_va_space删除ASLR。

我可以让我的nop-shellcode地址覆盖保存的EIP,但它在RET上崩溃。我想通过HANDLE SIGSEGV ignore等来禁用gdb中的SIGSEGV,但是在GDB外部运行时,无论我设置返回地址的位置如何,我都会继续收到Segmentation Fault。

除了获得旧版本的gcc以外的任何想法?目前使用gcc版本6.1.1。

回答

4

Linux遵循W^X原则:它将内存页面标记为非可执行文件,除非它们是代码部分的一部分。这超出了你编译的应用程序的范围,并有很好的理由。操作系统承担这一责任,以防止来自系统上执行的任何程序的缓冲区溢出攻击;尤其是那些积极尝试执行缓冲区溢出攻击的程序,就像你的程序一样。

您试图通过buf在堆栈上编写代码,并覆盖该函数的返回地址以将执行跳转到新注入的代码中。当函数返回时,程序计数器被设置为重写的返回地址,该地址现在指向堆栈内存。当程序计数器由于堆栈内存页面的撤销执行权限而尝试执行下一条指令时,将抛出SIGSEGV

要从堆栈执行,必须禁用OS堆栈保护。

幸运的是,Linux提供了execstack这种情况下,用户可以自行决定使用。

请参阅this Unix & Linux Stack Exchange发布更多一般信息。


调试插入的数据

如果您收到广发行SIGSEGV,它可能意味着在你的缓冲区溢出尝试一些错误。为了避免在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中使用printx/x来探索堆栈的外观。例如,您可以检查$rbp+8以查看堆栈上的返回地址(RA)。

GDB会SIGSEGVret指令,如果RA指向无效的位置:

Program received signal SIGSEGV, Segmentation fault. 
0x00000000004005bc in foo() at bufferoverflow.c:27 

您可以检查,看它是否故障的ret指令通过在故障时检查指令指针地址(在上面的例子中,它将是0x00000000004005bc)针对程序的程序集(在GDB中使用disassemble function_name)。

如果返回地址堆栈中的点背,它可能会抛出一个SIGILL的非法指令,这意味着你的寄信人地址可能不正确对齐,或者您的注入指令格式有误:

Program received signal SIGILL, Illegal instruction. 
0x00007fffffffdc13 in ??() 

再次,您可以使用GDB来探索您的堆栈以了解其原因。

GDB对于获得正确的缓冲区溢出结构很有用。 但是,一旦按预期工作,请记住,在GDB之外执行时可能会移动内存地址,特别是如果您编译时没有调试标志(-g)。你将不得不随身带回地址,以便在'现场'环境中获得它想要的位置。


旁白

一般情况下,直接运行注入的代码缓冲区溢出攻击是一般不能与今天的堆栈保护机制是可行的。通常情况下,需要额外的漏洞才能执行任意代码。

+2

当然,这个问题的关键是准确地揭示OP所打算的那种攻击。此功能的广泛使用使得此类攻击比以前更加可行。 –

+0

我使用execstack和gcc'-z execstack'并使用'sudo execstack -s vuln',并验证GNU堆栈被设置为RWE(读,写执行)'readelf -l vuln',我仍然得到类似的错误。关于我在做什么错的想法?我注意到我需要溢出缓冲区的数量超过了108个字节,而是116个字节。 – nrabbit

+0

@nrabbit我现在没有时间去做,但会在我的机器上做实验,然后回到你身边。 – sdsmith

1

我终于明白了这个问题。 GCC将我的指令和变量对齐到16字节的边界。因此,它在序言部分添加了汇编指令,即lea ecx,[esp+0x4]等,并且在函数结尾处类似地将堆栈指针移动了一些,并且与堆栈的布局有关。我重新编译了-mpreferred-stack-boundary=2,这个问题在GDB中解决了。现在我只需要像你提到的那样使用返回地址。谢谢!!