堆栈保护和堆栈溢出保护 - 哨兵、内存。

8

我有几个关于Stack Guard和SSP保护的问题。第一个问题是关于Stack Guard及其三种canaries的,如果我没记错的话——终止符、随机数和随机XOR。

  1. 我想知道如何在x86 Linux系统上禁用Stack Guard?我在某个地方读到,可以使用这个命令,在使用gcc编译时-disable-stackguard-randomization,就像启用的命令一样-enable-stackguard-randomization,但两者都不起作用。如果需要,我的gcc版本是4.8.2。

  2. 关于Stack guard的下一个问题,当我能够启用/禁用它时,如何设置我想要使用哪种类型的canaries?我所了解的是,默认情况下使用终止符canaries,对于随机canaries,我必须使用-enable-stackguard-randomization进行编译,但是随机XOR呢?(或者使用null 0x00000000)

  3. 现在关于SSP(ProPolice),我知道,对于随机canary,我必须使用fstack-protector-all进行编译,但是对于终止符,与Stack Guard中的默认设置相同吗?

  4. 最后一个问题,我在哪里可以找到内存中的随机canary?例如,我有这个场景-编译的C程序,如gcc -g example.c -o example -fstack-protector-all,因此具有随机canaries。假设我能够在每次执行后获取canary的地址。所以我期望Canary = 0x1ae3f900

    从不同的论文中,我得到了一些信息,即canary位于.bss段中。因此,我使用readelf获取了.bss段的地址:readelf -a ./example | grep bss。它是080456c9。在gdb中,我设置了一些断点,以获取canary的地址,但是当我检查.bss地址x/20x 0x080456c9时,我只看到0x00000000地址,但是canary不在那里。此外,我检查了__stack_chk_fail是否存在,但结果相同,我无法在那里看到它。我从PLT/GOT中获取了stack_chk_fail的地址。


还可以参考OSS-Security邮件列表上的Qualys Security Advisory - The Stack Clash。它展示了一些很棒的技巧,并且对保护页非常不利。令人惊讶的是,它能够使如此多的操作系统崩溃。 - jww
1
您可以通过使用gcc -fno-stack-protectorgcc -fstack-protector=strong来覆盖默认设置。关于何时以及如何使用GCC的堆栈保护功能,请参见When and how to use GCC's stack protection feature?和https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html#index-fstack-protector。此外,还可以参考[Why does this memory address %fs:0x28 ( fs[0x28] ) have a random value?](//stackoverflow.com/q/10325713),该文章指出正常的堆栈保护会随机化堆栈cookie。 - Peter Cordes
更正一下,启用该选项的命令是 -fstack-protector-strong,而不是 = - Peter Cordes
1个回答

2

栈破坏保护(SSP)是对StackGuard的改进。SSP最初在gcc 4.1中实现。

我想知道如何在x86 Linux系统上禁用Stack Guard?

使用-fno-stack-protector来禁用用户空间SSP。

--disable-stackguard-randomization--enable-stackguard-randomization 是glibc源代码的构建选项。

当我能够启用/禁用它时,如何设置我想要使用哪种类型的canaries?

据我所知,在gcc中无法进行配置。自 glibc 2.10以来,栈canary是在名为_dl_setup_stack_chk_guard的函数中生成的。以下是其代码的部分内容:

  if (dl_random == NULL)
    {
      ret.bytes[sizeof (ret) - 1] = 255;
      ret.bytes[sizeof (ret) - 2] = '\n';
    }
  else
    {
      memcpy (ret.bytes, dl_random, sizeof (ret));
      ret.num &= ~(uintptr_t) 0xff;
    }

“dl_random”保存了辅助向量条目“AT_RANDOM”的地址,它是由内核在创建进程时初始化的一个16字节的随机值。如果你正在运行一个不初始化“AT_RANDOM”的内核或模拟器,则检查“dl_random == NULL”将为真,并且使用的canary是以255和“\n”分别初始化的终止符值,其第一和第二最高字节。所有其他字节都为零。通常,“AT_RANDOM”由内核初始化,因此将复制“AT_RANDOM”的最后7个有效字节。canary的最后一个字节设置为零。

因此,如果您想使用特定的方法生成canary,可以更改此代码并构建自己的glibc。

作为另一种替代方法,在@PeterCordes的评论中建议在“main”函数顶部将canary值写入内存位置“%%fs:0x28”(请参见下面的代码),并在从“main”返回之前恢复运行时生成的canary。

关于SSP(ProPolice),我知道,对于随机金丝雀,我必须使用“fstack-protector-all”进行编译,但是终结者呢?与默认情况下的Stack Guard一样吗?
所有类型的-fstack-protector选项都使用SSP。这些不会影响如何生成金丝雀。
最后一个问题,如果你们中有人能告诉我在哪里可以找到内存中的随机金丝雀,我将不胜感激。
金丝雀是在进程启动时动态生成的;您不能使用readelf获取金丝雀。根据这篇文章,当为i386编译时,您可以使用以下代码获取金丝雀:
int read_canary()
{
  int val = 0;
  __asm__("movl %%gs:0x14, %0;"
          : "=r"(val)
          :
          :);
  return val;
}

"并且对于 x86_64:"
long read_canary()
{
  long val = 0;
  __asm__("movq %%fs:0x28, %0;"
          : "=r"(val)
          :
          :);
  return val;
}

1
您可以更改此代码并构建自己的glibc。或者在main顶部写入%% fs: 0x28,并在返回之前恢复原始canary值,以防main本身或任何CRT启动函数使用堆栈保护。或者调用exit而不是让main返回,这样您就不需要恢复旧的堆栈canary。我不知道异常是否会通过main取消缠绕,并导致堆栈保护器检查失败,但可能不会,因为任何顶级异常捕获可能会中止而不是返回。 - Peter Cordes
1
如果你在main函数中调用exit而不是返回,那么你就不需要恢复旧的栈canary。main函数的栈cookie检查代码将不会被执行(它的返回地址也不会被使用)。因此,调用exit可以简化main函数。 - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接