Stack Canaries
StackGuard 和 StackShield
StackGuard 在 EIP (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
:
-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 的演示,其中提到通常 TLS 由 mmap
存储,当创建 线程 的 栈 时也是由 mmap
生成的,根据这一点,可能允许如前述 writeup 中所示的溢出。
修改
__stack_chk_fail
的 GOT 条目
如果二进制文件具有 Partial RELRO,则可以使用任意写入来修改 __stack_chk_fail
的 GOT 条目,使其成为一个不会阻止程序的虚拟函数,即使 canary 被修改。
此攻击在以下 writeup 中执行:https://7rocky.github.io/en/ctf/other/securinets-ctf/scrambler/
参考资料
Last updated