在 GCC 中禁用堆栈保护不起作用
Disabling stack protection in GCC not working
我正在尝试使用此函数使用带有 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-address 覆盖保存的 EIP,但它在 RET 上崩溃了。我想出了用 HANDLE SIGSEGV ignore
等禁用 gdb 中的 SIGSEGV 但是 运行 在 GDB 之外,无论我在哪里设置 return 地址,我都会继续出现分段错误。
除了获得旧版本的 gcc 之外还有什么想法吗?当前使用 gcc 版本 6.1.1.
Linux遵循W^X原则:它将内存页标记为不可执行,除非它们是代码段的一部分。这超出了您编译的应用程序的范围,并且有充分的理由。 OS 承担了防御来自系统上执行的任何程序的缓冲区溢出攻击的责任;尤其是像您这样主动尝试执行缓冲区溢出攻击的程序。
您正试图通过 buf
在堆栈上写入代码并覆盖函数的 return 地址以跳转到新注入的代码中执行。当函数 returns 时,程序计数器设置为覆盖的 return 地址,现在指向堆栈内存。 SIGSEGV
当程序计数器由于对堆栈内存页面的执行权限被撤销而尝试执行下一条指令时抛出。
要从堆栈执行,必须禁用OS 堆栈保护。
幸运的是 Linux 提供了 execstack 供用户自行决定使用的这种情况。
有关更多一般信息,请参阅 this 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
以查看堆栈中的 return 地址 (RA)。
如果 RA 指向无效位置,GDB 将在 ret
指令上 SIGSEGV
:
Program received signal SIGSEGV, Segmentation fault.
0x00000000004005bc in foo () at bufferoverflow.c:27
您可以通过检查出错时的指令指针地址(在上面的示例中,它将是 0x00000000004005bc
)来检查它是否在 ret
指令上出错程序集(在 GDB 中使用 disassemble function_name
)。
如果 return 地址指向堆栈,它可能会抛出 SIGILL
非法指令,这意味着您的 return 地址可能没有正确对齐,或者您的注入的指令格式错误:
Program received signal SIGILL, Illegal instruction.
0x00007fffffffdc13 in ?? ()
同样,您可以使用 GDB 探索您的堆栈以了解原因。
GDB 可用于纠正缓冲区溢出的结构。
但是,一旦它按预期工作,请记住在 GDB 外部执行时内存地址可能会发生偏移,特别是如果您在没有调试标志 (-g
) 的情况下进行编译。您将不得不稍微调整一下 return 地址,以便在 'live' 环境中将其放置在您想要的位置。
旁白
一般来说,直接 运行 注入代码的缓冲区溢出攻击 通常 对于当今的堆栈保护机制是不可行的。通常需要存在额外的漏洞才能执行任意代码。
我终于弄明白了。 GCC 将我的指令和变量对齐到 16 字节边界。因此,它在序言中添加了汇编指令,即 lea ecx,[esp+0x4]
等,在函数尾声中添加了类似的指令,它稍微移动了堆栈指针并弄乱了堆栈的布局。我用 -mpreferred-stack-boundary=2
重新编译,修复了 GDB 中的问题。现在我只需要像您提到的那样使用那些 return 地址。谢谢!!
我正在尝试使用此函数使用带有 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
.
我可以让我的 nop-shellcode-address 覆盖保存的 EIP,但它在 RET 上崩溃了。我想出了用 HANDLE SIGSEGV ignore
等禁用 gdb 中的 SIGSEGV 但是 运行 在 GDB 之外,无论我在哪里设置 return 地址,我都会继续出现分段错误。
除了获得旧版本的 gcc 之外还有什么想法吗?当前使用 gcc 版本 6.1.1.
Linux遵循W^X原则:它将内存页标记为不可执行,除非它们是代码段的一部分。这超出了您编译的应用程序的范围,并且有充分的理由。 OS 承担了防御来自系统上执行的任何程序的缓冲区溢出攻击的责任;尤其是像您这样主动尝试执行缓冲区溢出攻击的程序。
您正试图通过 buf
在堆栈上写入代码并覆盖函数的 return 地址以跳转到新注入的代码中执行。当函数 returns 时,程序计数器设置为覆盖的 return 地址,现在指向堆栈内存。 SIGSEGV
当程序计数器由于对堆栈内存页面的执行权限被撤销而尝试执行下一条指令时抛出。
要从堆栈执行,必须禁用OS 堆栈保护。
幸运的是 Linux 提供了 execstack 供用户自行决定使用的这种情况。
有关更多一般信息,请参阅 this 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
以查看堆栈中的 return 地址 (RA)。
如果 RA 指向无效位置,GDB 将在 ret
指令上 SIGSEGV
:
Program received signal SIGSEGV, Segmentation fault.
0x00000000004005bc in foo () at bufferoverflow.c:27
您可以通过检查出错时的指令指针地址(在上面的示例中,它将是 0x00000000004005bc
)来检查它是否在 ret
指令上出错程序集(在 GDB 中使用 disassemble function_name
)。
如果 return 地址指向堆栈,它可能会抛出 SIGILL
非法指令,这意味着您的 return 地址可能没有正确对齐,或者您的注入的指令格式错误:
Program received signal SIGILL, Illegal instruction.
0x00007fffffffdc13 in ?? ()
同样,您可以使用 GDB 探索您的堆栈以了解原因。
GDB 可用于纠正缓冲区溢出的结构。
但是,一旦它按预期工作,请记住在 GDB 外部执行时内存地址可能会发生偏移,特别是如果您在没有调试标志 (-g
) 的情况下进行编译。您将不得不稍微调整一下 return 地址,以便在 'live' 环境中将其放置在您想要的位置。
旁白
一般来说,直接 运行 注入代码的缓冲区溢出攻击 通常 对于当今的堆栈保护机制是不可行的。通常需要存在额外的漏洞才能执行任意代码。
我终于弄明白了。 GCC 将我的指令和变量对齐到 16 字节边界。因此,它在序言中添加了汇编指令,即 lea ecx,[esp+0x4]
等,在函数尾声中添加了类似的指令,它稍微移动了堆栈指针并弄乱了堆栈的布局。我用 -mpreferred-stack-boundary=2
重新编译,修复了 GDB 中的问题。现在我只需要像您提到的那样使用那些 return 地址。谢谢!!