Stack Canaries

支持 HackTricks

StackGuard 和 StackShield

StackGuardEIP (Extended Instruction Pointer) 前插入一个特殊值,称为 canary,具体为 0x000aff0d(表示空值、换行符、EOF、回车),以防止缓冲区溢出。然而,像 recv()memcpy()read()bcopy() 这样的函数仍然容易受到攻击,而且它不保护 EBP (Base Pointer)

StackShield 比 StackGuard 采取了更复杂的方法,它维护一个全局返回栈,用于存储所有返回地址(EIPs)。这种设置确保任何溢出都不会造成伤害,因为它允许对存储的和实际的返回地址进行比较,以检测溢出事件。此外,StackShield 可以将返回地址与边界值进行比较,以检测 EIP 是否指向预期数据空间之外。然而,这种保护可以通过 Return-to-libc、ROP (Return-Oriented Programming) 或 ret2ret 等技术绕过,这表明 StackShield 也无法保护本地变量。

栈溢出保护器 (ProPolice) -fstack-protector:

这种机制在 EBP 前放置一个 canary,并重新组织本地变量,将缓冲区放置在更高的内存地址,以防止它们覆盖其他变量。它还安全地复制传递给栈上的参数,将这些副本用作参数。然而,它不保护少于 8 个元素的数组或用户结构中的缓冲区。

canary 是从 /dev/urandom 派生的随机数,或者默认值为 0xff0a0000。它存储在 TLS (Thread Local Storage) 中,允许跨线程共享内存空间具有线程特定的全局或静态变量。这些变量最初从父进程复制,子进程可以修改它们的数据而不影响父进程或同级进程。然而,如果在不创建新 canary 的情况下使用 fork(),所有进程(父进程和子进程)将共享相同的 canary,使其容易受到攻击。在 i386 架构上,canary 存储在 gs:0x14,在 x86_64 上存储在 fs:0x28

这种本地保护识别容易受攻击的带有缓冲区的函数,并在这些函数的开头注入代码以放置 canary,在结尾验证其完整性。

当 Web 服务器使用 fork() 时,可以通过逐字节猜测 canary 进行暴力攻击。然而,在 fork() 后使用 execve() 覆盖内存空间,从而消除攻击。vfork() 允许子进程执行而不复制,直到尝试写入时创建副本,提供了一种不同的进程创建和内存处理方法。

长度

x64 二进制文件中,canary cookie 是一个 0x8 字节的 qword。前七个字节是随机的,最后一个字节是一个 空字节

x86 二进制文件中,canary cookie 是一个 0x4 字节的 dword。前三个字节是随机的,最后一个字节是一个 空字节

两个 canary 的最低有效字节是空字节,因为它将是来自较低地址的栈中的第一个字节,因此读取字符串的函数将在读取它之前停止

绕过

泄露 canary,然后用其自身值覆盖它(例如缓冲区溢出)。

  • 如果 canary 在子进程中被 fork,可能可以逐字节进行 暴力破解

  • 如果二进制文件中存在一些有趣的 泄漏或任意读取漏洞,可能可以泄漏它:

  • 覆盖栈中存储的指针

栈易受栈溢出攻击的情况下,可能会 包含可被覆盖的字符串或函数地址,以便利用漏洞而无需到达栈 canary。查看:

  • 修改主 canary 和线程 canary

受 canary 保护的线程函数中的缓冲区 溢出 可用于 修改线程的主 canary。结果,该防护措施无效,因为检查使用了两个相同的 canary(尽管已修改)。

此外,受 canary 保护的线程函数中的缓冲区 溢出 可用于 修改存储在 TLS 中的主 canary。这是因为可能可以通过线程的栈中的 缓冲区溢出 达到存储 TLS 的内存位置。 结果,该防护措施无效,因为检查使用了两个相同的 canary(尽管已修改)。 此攻击在以下 writeup 中执行:http://7rocky.github.io/en/ctf/htb-challenges/pwn/robot-factory/#canaries-and-threads

还请查看 https://www.slideshare.net/codeblue_jp/master-canary-forging-by-yuki-koike-code-blue-2015 的演示,其中提到通常 TLSmmap 存储,当创建 线程 时也是由 mmap 生成的,根据这一点,可能允许如前述 writeup 中所示的溢出。

  • 修改 __stack_chk_fail 的 GOT 条目

如果二进制文件具有 Partial RELRO,则可以使用任意写入来修改 __stack_chk_fail 的 GOT 条目,使其成为一个不会阻止程序的虚拟函数,即使 canary 被修改。

此攻击在以下 writeup 中执行:https://7rocky.github.io/en/ctf/other/securinets-ctf/scrambler/

参考资料

支持 HackTricks

Last updated