在雪豹中创建缓冲区溢出

| 作为大学计算机安全课程的一部分,我即将学习缓冲区溢出以及如何将其用作漏洞利用。我正在尝试使用以下代码进行一些简单的缓冲区溢出:
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
    char buffer_one[4], buffer_two[16];

    strcpy(buffer_one, \"one\");
    strcpy(buffer_two, \"two\");

    strcpy(buffer_one, argv[1]);

    printf(\"buffer_two is at %p and contains \\\'%s\\\'\\n\", buffer_two, buffer_two);
    printf(\"buffer_one is at %p and contains \\\'%s\\\'\\n\", buffer_one, buffer_one);
}
如果我运行,我可以用空终止符覆盖buffer_one的内容
$./overflow 1234567890123456
 buffer_two is at 0x7fff5fbff8d0 and contains \'1234567890123456\'
 buffer_one is at 0x7fff5fbff8e0 and contains \'\'
但是,如果我发送的参数超过16个字符,程序将发送Abort陷阱。我以为这是Snow Leopard上的某种缓冲区保护(也许是ASLR?)。如果将buffer_two的大小设置为<16,则地址仍然相隔16位 我正在运行
gcc -o overflow overflow.c -fno-stack-protector
以删除堆栈保护 除了安装运行linux dist的VM以外,是否有其他解决方案?     
已邀请:
发生这种情况的关键是在内存中,
buffer_one
位于after4ѭ之后。这意味着当您溢出
buffer_one
时,您不会溢出到
buffer_two
中。取而代之的是,您溢出到用于保存其他内容的堆栈存储器中,例如保存的
ebp
指针,最重要的是返回地址。 而这正是您尝试缓冲区溢出攻击时想要发生的事情!当程序执行“ 8”时,从“ 9”开始的前四个字节进入分配给“ 3”的存储器。但是随后的12个内存开始溢出,用于其他目的,最终覆盖了返回地址。在没有看到机器代码的情况下,我无法确定到底哪个字节正溢出返回地址。但是我猜测SIGABRT时EIP的值为0x31323334或类似的值(\'1234 \'的十六进制表示)。关键是要意识到,通过能够覆盖返回地址,您可以控制EIP。当您控制EIP时,您也可以控制系统。 (有些过分夸张,但在大多数情况下相差不远)当您控制EIP时,您可以控制处理器下一步执行的指令(暂时不考虑OS /内核实际上位于两者之间的事实)。 现在,如果您确切找到哪个八个字节覆盖了返回地址,则可以将这些字节替换为缓冲区的地址(
0x00007fff5fbff8e0
),而不是返回到原始调用方(在这种情况下为libc),程序将开始执行您提供的指令(又称shellcode)。请注意,您必须在最重要的位置填写隐含的0,并将地址提供为实际的不可打印ASCII字符(
0x00 0x00 0x7f 0xff 0x5f
等),而不是实际的数字/字符
7ff5
等。如果使用x86-64体系结构,您还必须考虑到低字节序,并将其向后提供-
0xe0 0xf8 0xbf
等。使用反引号和使用简短的Python或Perl脚本的命令替换最容易实现提供这些不可打印的字符:
./overflow `python -c \'print \"AAAAAAAAAAAAAAAA\\xe0\\xf8\\xbf\\x5f\\xff\\x7f\"\'`
(A填充会使缓冲区溢出。)不幸的是,您将无法提供该地址所需的2个额外的additional16ѭ。其中一个NULL将在ѭ17之前为您放置,但是您必须对最后一个NULL感到幸运,并希望您覆盖的地址已经以ѭ18开头(实际上很有可能)。现在,当您使用正确数量的A执行此操作时,您可能仍会遇到分段错误甚至可能是非法指令,因为您现在将跳转至大写A并按实际执行它们机器指令(
0x41
=>
inc ecx
)。 最后,最后一部分是放入实际的shellcode。给定有限的缓冲区大小,很难提供仅12个字节左右的有用内容。由于您是在这种情况下编写代码的,因此最简单的方法可能是增大缓冲区的大小。如果不是这种选择,那么您可以A)还要使用
buffer_two
来增加16个字节,因为它在
buffer_one
之前出现,或者B)在环境变量中提供shellcode并跳转到该位置。 如果您希望自己编写实际的shellcode,则必须知道如何执行syscall以及什么调用约定以及如何使用它们,以及如何避免shellcode中的NULL字节。另一种选择是使用有效负载生成器,例如Metasploit附带的有效负载生成器,它将使它变得更加轻松(尽管您不会学到太多)。 从技术上讲,这是您唯一需要的部分,尤其是因为您对地址将是一个好主意。但是,很多时候(尤其是在不知道shellcode地址的情况下),会在shellcode的前面放置一个所谓的NOP底座,这样您就不必完全正确地获取地址。 NOP底座(“无操作”的缩写)只是几百至数千个NOP指令(0x90),您可以跳转到其中,直到执行继续到shellcode为止才起作用。 如果您跟踪了GDB中的所有内容,并且执行正确地跳到了shellcode,但是仍然遇到访问冲突,则可能是因为NX位已在堆栈页面上设置,这意味着处理器将拒绝从堆栈上执行数据说明。我不确定OSX是否包含
execstack
,但如果这样,您可以将其用于测试目的以禁用NX位(
execstack -s overflow
)。 抱歉,我不知道您是否想研究缓冲区溢出。您还可以查看其他指南,例如Aleph One的原型指南“粉碎堆栈以获取乐趣和利润”。 《 Shellcoder的手册》也是一本很好的书,可以查阅一下,我相信其他人也可以添加建议。 TL; DR:简而言之,您正在溢出缓冲区,并覆盖保存的指针并使用垃圾返回地址。     
如果您正在学习漏洞利用,那么您将需要真正地探索细节。 继续阅读机器代码!您可能能够找出如何使溢出问题越过Snow Leopard使用的任何检查方法。 这个问题可能比这还简单。没有规则,编译器必须以任何特定顺序将
buffer_one
buffer_two
放入堆栈,甚至根本不将它们放入堆栈。请注意,ѭ3actually实际上适合寄存器。 当然不是这种情况,但是我看到buffer_two被放置在buffer_one之前。这意味着将溢出写入
buffer_one
将永远不会写入
buffer_two
。我无法解释为什么它最终包含
\'\'
,但是ѭ31memory在内存中肯定要早于
f8e0
。     
x86堆栈上的数据是4字节对齐的。如果
buffer_two
长度不是4字节的倍数,则在
buffer_two
buffer_one
之间放置填充。将其更改为12或更小,并且它们应该间隔12个字节,依此类推。 [更新]我忽略了地址大小。您在64位系统上,堆栈是8字节对齐的。直到您的缓冲区大小更改至少8个字节,地址差才会改变。 这行是正确的:
strcpy(buffer_one, argv[1]);
输出看起来就像您将
argv[1]
复制到
buffer_two
。 在这种情况下,崩溃时您要复制多少? 17个字节? 18岁?如果大于24,则将以导致中止的方式破坏堆栈。 注意,“ 39”实际上是在复制17个字节,其中包括将终止符“ 3”截断的空终止符。     
您是否尝试在编译时禁用FORTIFY_SOURCE?
-D_FORTIFY_SOURCE=0
    

要回复问题请先登录注册