buffer_one
为什么会发生这种情况的关键是位于buffer_two
内存中的事实。这意味着当你溢出时buffer_one
,你并没有溢出到buffer_two
. 相反,您会溢出到用于保存其他内容的堆栈内存中,例如保存的ebp
指针,最重要的是返回地址。
这正是您在尝试使用缓冲区溢出漏洞时想要发生的事情!当程序执行strcpy(buffer_one, argv[1]);
前四个字节从argv[1]
go 到分配给buffer_one
. 但是接下来的 12 开始溢出用于其他事情的内存,最终覆盖了返回地址。在没有看到机器代码的情况下,我无法确定究竟是哪些字节溢出了返回地址。但我猜测 SIGABRT 时 EIP 的值是 0x31323334 或类似的值(“1234”的十六进制表示)。关键是要意识到,通过覆盖返回地址,您可以控制 EIP。当你控制 EIP 时, 你就控制了系统. (有点夸张,但在大多数情况下并不遥远)当您控制 EIP 时,您可以控制处理器接下来将执行哪些指令(暂时不考虑操作系统/内核实际上介于两者之间的事实)。
现在,如果您确切地发现哪八个字节覆盖了返回地址,您可以将这些字节替换为缓冲区的地址0x00007fff5fbff8e0
(又名 shellcode)。请注意,您必须在最重要的位置填写隐含的 0,并将地址提供为实际不可打印的 ASCII 字符(0x00 0x00 0x7f 0xff 0x5f
等等),而不是实际的数字/字符7ff5
等。如果使用 x86-64 架构,您还必须考虑 little-endianness 并将其向后提供 -0xe0 0xf8 0xbf
等等。提供这些不可打印字符最容易使用反引号和使用简短 Python 或 Perl 脚本的命令替换来完成:
./overflow `python -c 'print "AAAAAAAAAAAAAAAA\xe0\xf8\xbf\x5f\xff\x7f"'`
(A 正在填充以溢出缓冲区。)不幸的是,您将无法提供\x00
该地址所需的 2 个附加值。这些 NULL 中的一个将由 为您放置strcpy
,但您必须对最后一个 NULL 感到幸运,并希望您正在覆盖的地址已经开始0x00
(实际上很有可能)。现在,当您使用正确数量的 A 执行此操作时,您可能仍然会遇到分段错误甚至可能是非法指令,因为您现在将跳转到大写的 A 并将它们作为实际机器指令 ( 0x41
=> inc ecx
) 执行。
最后最后一部分是放入实际的 shellcode。鉴于您有限的缓冲区大小,很难仅在 12 个字节左右提供任何有用的东西。由于您是在这种情况下编写代码,因此最简单的方法可能是使缓冲区更大。如果这不是一个选项,那么您可以 A) 也可以使用buffer_two
16 个字节,因为它之前buffer_one
或 B) 在环境变量中提供 shellcode 并跳转到那个。
如果您想自己编写实际的 shellcode,您必须知道如何执行系统调用、调用约定是什么以及如何使用它们,以及如何避免 shellcode 中出现 NULL 字节。另一种选择是使用一个有效载荷生成器,例如 Metasploit 中包含的那个,这将使它更容易(尽管你不会学到那么多)。
从技术上讲,这些是您唯一需要的部分,尤其是因为您对地址将是什么有了很好的了解。然而,很多时候(尤其是在不知道 shellcode 地址的情况下)一个所谓的 NOP sled 会放在 shellcode 前面,这样您就不必完全正确地获取地址。NOP sled(No Operation 的缩写)只是成百上千的 NOP 指令(0x90),您可以跳到中间,然后在继续执行到 shellcode 之前无效。
如果您在 GDB 中跟踪所有内容并且执行正确地跳转到了 shellcode,但您仍然遇到访问冲突,这可能是因为在堆栈页面上设置了 NX 位,这意味着处理器将拒绝将堆栈中的数据作为指令执行。我不确定是否execstack
包含在 OSX 中,但如果包含,您可以将其用于测试目的以禁用 NX 位 ( execstack -s overflow
)。
我为文字墙道歉,但我不确定你想研究缓冲区溢出多远。您还可以查看其他指南,例如 Aleph One 的原型指南“Smashing the Stack for Fun and Profit”。Shellcoder's Handbook也是一本值得一看的好书,我相信其他人可以添加建议。
TL;DR:简而言之,您正在溢出缓冲区并用垃圾覆盖保存的指针和返回地址。