77 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Stack Canaries
{{#include ../../../banners/hacktricks-training.md}}
## **StackGuard 和 StackShield**
**StackGuard****EIP (扩展指令指针)** 之前插入一个特殊值,称为 **canary**,具体为 `0x000aff0d`表示空值、换行符、EOF、回车以防止缓冲区溢出。然而`recv()``memcpy()``read()``bcopy()` 这样的函数仍然存在漏洞,并且它不保护 **EBP (基指针)**
**StackShield** 采用比 StackGuard 更复杂的方法,通过维护一个 **全局返回栈**,存储所有返回地址 (**EIPs**)。这种设置确保任何溢出不会造成伤害因为它允许比较存储的和实际的返回地址以检测溢出发生。此外StackShield 可以检查返回地址与边界值,以检测 **EIP** 是否指向预期数据空间之外。然而,这种保护可以通过 Return-to-libc、ROP面向返回的编程或 ret2ret 等技术绕过,这表明 StackShield 也不保护局部变量。
## **Stack Smash Protector (ProPolice) `-fstack-protector`:**
该机制在 **EBP** 之前放置一个 **canary**,并重新组织局部变量以将缓冲区放置在更高的内存地址,防止它们覆盖其他变量。它还安全地复制传递到局部变量上方的堆栈参数,并使用这些副本作为参数。然而,它不保护少于 8 个元素的数组或用户结构中的缓冲区。
**canary** 是从 `/dev/urandom` 派生的随机数或默认值 `0xff0a0000`。它存储在 **TLS (线程局部存储)** 中,允许跨线程共享内存空间具有线程特定的全局或静态变量。这些变量最初从父进程复制,子进程可以在不影响父进程或兄弟进程的情况下更改其数据。然而,如果 **`fork()` 在不创建新 canary 的情况下使用,所有进程(父进程和子进程)共享相同的 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。**前三个字节是随机的**,最后一个字节是 **空字节**
> [!CAUTION]
> 两个 canary 的最低有效字节是空字节,因为它将是来自较低地址的堆栈中的第一个,因此 **读取字符串的函数将在读取之前停止**。
## 绕过
**泄露 canary** 然后用其自身的值覆盖它(例如,缓冲区溢出)。
- 如果 **canary 在子进程中被 fork**,可能可以 **逐字节暴力破解** 它:
{{#ref}}
bf-forked-stack-canaries.md
{{#endref}}
- 如果二进制文件中存在一些有趣的 **泄露或任意读取漏洞**,可能可以泄露它:
{{#ref}}
print-stack-canary.md
{{#endref}}
- **覆盖堆栈存储的指针**
易受堆栈溢出影响的堆栈可能 **包含可以被覆盖的字符串或函数的地址**,以利用该漏洞而无需到达堆栈 canary。检查
{{#ref}}
../../stack-overflow/pointer-redirecting.md
{{#endref}}
- **修改主 canary 和线程 canary**
在受 canary 保护的线程函数中 **缓冲区溢出** 可以用来 **修改线程的主 canary**。因此缓解措施是无效的因为检查是使用两个相同的尽管被修改过的canary。
此外,在受 canary 保护的线程函数中 **缓冲区溢出** 可以用来 **修改存储在 TLS 中的主 canary**。这是因为,可能通过线程的 **堆栈中的 bof** 到达存储 TLS 的内存位置因此canary。\
因此缓解措施是无效的因为检查是使用两个相同的尽管被修改过的canary。\
此攻击在以下写作中进行: [http://7rocky.github.io/en/ctf/htb-challenges/pwn/robot-factory/#canaries-and-threads](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](https://www.slideshare.net/codeblue_jp/master-canary-forging-by-yuki-koike-code-blue-2015) 的演示,其中提到通常 **TLS** 是通过 **`mmap`** 存储的,当 **线程****堆栈** 被创建时,它也是通过 `mmap` 生成的,这可能允许如前所述的溢出。
- **修改 `__stack_chk_fail` 的 GOT 条目**
如果二进制文件具有部分 RELRO则可以使用任意写入来修改 **`__stack_chk_fail` 的 GOT 条目**,使其成为一个不会在 canary 被修改时阻止程序的虚拟函数。
此攻击在以下写作中进行: [https://7rocky.github.io/en/ctf/other/securinets-ctf/scrambler/](https://7rocky.github.io/en/ctf/other/securinets-ctf/scrambler/)
## 参考
- [https://guyinatuxedo.github.io/7.1-mitigation_canary/index.html](https://guyinatuxedo.github.io/7.1-mitigation_canary/index.html)
- [http://7rocky.github.io/en/ctf/htb-challenges/pwn/robot-factory/#canaries-and-threads](http://7rocky.github.io/en/ctf/htb-challenges/pwn/robot-factory/#canaries-and-threads)
- [https://7rocky.github.io/en/ctf/other/securinets-ctf/scrambler/](https://7rocky.github.io/en/ctf/other/securinets-ctf/scrambler/)
{{#include ../../../banners/hacktricks-training.md}}