BROP - Blind Return Oriented Programming
Last updated
Last updated
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Ο στόχος αυτής της επίθεσης είναι να μπορέσουμε να καταχραστούμε ένα ROP μέσω μιας υπερχείλισης buffer χωρίς καμία πληροφορία για το ευάλωτο δυαδικό αρχείο. Αυτή η επίθεση βασίζεται στο εξής σενάριο:
Μια ευπάθεια στο stack και γνώση του πώς να την ενεργοποιήσουμε.
Μια εφαρμογή server που επανεκκινείται μετά από μια κατάρρευση.
Μπορείτε να βρείτε περισσότερες πληροφορίες για αυτές τις διαδικασίες εδώ (BF Forked & Threaded Stack Canaries) και εδώ (BF Addresses in the Stack).
Αυτό το gadget επιτρέπει βασικά να επιβεβαιωθεί ότι κάτι ενδιαφέρον εκτελέστηκε από το gadget ROP επειδή η εκτέλεση δεν κατέρρευσε. Συνήθως, αυτό το gadget θα είναι κάτι που σταματά την εκτέλεση και είναι τοποθετημένο στο τέλος της αλυσίδας ROP όταν αναζητάμε gadgets ROP για να επιβεβαιώσουμε ότι εκτελέστηκε ένα συγκεκριμένο gadget ROP.
Αυτή η τεχνική χρησιμοποιεί το ret2csu gadget. Και αυτό συμβαίνει επειδή αν αποκτήσετε πρόσβαση σε αυτό το gadget στη μέση κάποιων εντολών, αποκτάτε gadgets για να ελέγξετε rsi
και rdi
:
Αυτά θα είναι τα gadgets:
pop rsi; pop r15; ret
pop rdi; ret
Παρατηρήστε πώς με αυτά τα gadgets είναι δυνατό να ελέγξετε 2 παραμέτρους μιας συνάρτησης που θα καλέσετε.
Επίσης, παρατηρήστε ότι το gadget ret2csu έχει μια πολύ μοναδική υπογραφή επειδή θα popάρει 6 καταχωρητές από το stack. Έτσι, στέλνοντας μια αλυσίδα όπως:
'A' * offset + canary + rbp + ADDR + 0xdead * 6 + STOP
Αν το STOP εκτελείται, αυτό σημαίνει βασικά ότι χρησιμοποιήθηκε μια διεύθυνση που popάρει 6 καταχωρητές από το stack. Ή ότι η διεύθυνση που χρησιμοποιήθηκε ήταν επίσης μια διεύθυνση STOP.
Για να αφαιρεθεί αυτή η τελευταία επιλογή, εκτελείται μια νέα αλυσίδα όπως η παρακάτω και δεν πρέπει να εκτελεί το gadget STOP για να επιβεβαιώσει ότι το προηγούμενο popάρισε 6 καταχωρητές:
'A' * offset + canary + rbp + ADDR
Γνωρίζοντας τη διεύθυνση του gadget ret2csu, είναι δυνατό να συμπεράνουμε τη διεύθυνση των gadgets για να ελέγξουμε rsi
και rdi
.
Ο πίνακας PLT μπορεί να αναζητηθεί από 0x400000 ή από τη διαρρεύσουσα διεύθυνση RIP από το stack (αν χρησιμοποιείται PIE). Οι καταχωρήσεις του πίνακα είναι χωρισμένες κατά 16B (0x10B), και όταν καλείται μια συνάρτηση, ο server δεν καταρρέει ακόμη και αν οι παράμετροι δεν είναι σωστές. Επίσης, η έλεγχος της διεύθυνσης μιας καταχώρησης στο PLT + 6B επίσης δεν καταρρέει καθώς είναι ο πρώτος κώδικας που εκτελείται.
Επομένως, είναι δυνατό να βρείτε τον πίνακα PLT ελέγχοντας τις εξής συμπεριφορές:
'A' * offset + canary + rbp + ADDR + STOP
-> καμία κατάρρευση
'A' * offset + canary + rbp + (ADDR + 0x6) + STOP
-> καμία κατάρρευση
'A' * offset + canary + rbp + (ADDR + 0x10) + STOP
-> καμία κατάρρευση
Η συνάρτηση strcmp
ρυθμίζει τον καταχωρητή rdx
στο μήκος της συμβολοσειράς που συγκρίνεται. Σημειώστε ότι rdx
είναι η τρίτη παράμετρος και πρέπει να είναι μεγαλύτερη από 0 προκειμένου να χρησιμοποιήσουμε αργότερα το write
για να διαρρεύσουμε το πρόγραμμα.
Είναι δυνατό να βρείτε την τοποθεσία της strcmp
στον PLT με βάση τη συμπεριφορά της χρησιμοποιώντας το γεγονός ότι μπορούμε τώρα να ελέγξουμε τις 2 πρώτες παραμέτρους των συναρτήσεων:
strcmp(<μη αναγνώσιμη διεύθυνση>, <μη αναγνώσιμη διεύθυνση>) -> κατάρρευση
strcmp(<μη αναγνώσιμη διεύθυνση>, <αναγνώσιμη διεύθυνση>) -> κατάρρευση
strcmp(<αναγνώσιμη διεύθυνση>, <μη αναγνώσιμη διεύθυνση>) -> κατάρρευση
strcmp(<αναγνώσιμη διεύθυνση>, <αναγνώσιμη διεύθυνση>) -> καμία κατάρρευση
Είναι δυνατό να ελέγξετε αυτό καλώντας κάθε καταχώρηση του πίνακα PLT ή χρησιμοποιώντας τη αργή διαδρομή PLT που βασικά συνίσταται στο να καλείτε μια καταχώρηση στον πίνακα PLT + 0xb (η οποία καλεί το dlresolve
) ακολουθούμενη στο stack από τον αριθμό καταχώρησης που θέλετε να ελέγξετε (ξεκινώντας από το μηδέν) για να σαρώσετε όλες τις καταχωρήσεις PLT από την πρώτη:
strcmp(<μη αναγνώσιμη διεύθυνση>, <αναγνώσιμη διεύθυνση>) -> κατάρρευση
b'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0x300) + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP
-> Θα καταρρεύσει
strcmp(<αναγνώσιμη διεύθυνση>, <μη αναγνώσιμη διεύθυνση>) -> κατάρρευση
b'A' * offset + canary + rbp + (BROP + 0x9) + p64(0x300) + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP
strcmp(<αναγνώσιμη διεύθυνση>, <αναγνώσιμη διεύθυνση>) -> καμία κατάρρευση
b'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP
Θυμηθείτε ότι:
BROP + 0x7 δείχνει στο pop RSI; pop R15; ret;
BROP + 0x9 δείχνει στο pop RDI; ret;
PLT + 0xb δείχνει σε μια κλήση στο dl_resolve.
Αφού βρείτε το strcmp
, είναι δυνατό να ρυθμίσετε το rdx
σε μια τιμή μεγαλύτερη από 0.
Σημειώστε ότι συνήθως το rdx
θα φιλοξενεί ήδη μια τιμή μεγαλύτερη από 0, οπότε αυτό το βήμα μπορεί να μην είναι απαραίτητο.
Τέλος, χρειάζεται ένα gadget που να εξάγει δεδομένα προκειμένου να εξάγει το δυαδικό αρχείο. Και σε αυτό το σημείο είναι δυνατό να ελέγξουμε 2 παραμέτρους και να ρυθμίσουμε το rdx
μεγαλύτερο από 0.
Υπάρχουν 3 κοινές συναρτήσεις που θα μπορούσαν να καταχραστούν για αυτό:
puts(data)
dprintf(fd, data)
write(fd, data, len(data)
Ωστόσο, το αρχικό έγγραφο αναφέρει μόνο τη συνάρτηση write
, οπότε ας μιλήσουμε γι' αυτήν:
Το τρέχον πρόβλημα είναι ότι δεν γνωρίζουμε πού βρίσκεται η συνάρτηση write μέσα στον PLT και δεν γνωρίζουμε έναν αριθμό fd για να στείλουμε τα δεδομένα στη σύνδεσή μας.
Ωστόσο, γνωρίζουμε πού είναι ο πίνακας PLT και είναι δυνατό να βρούμε τη write με βάση τη συμπεριφορά της. Και μπορούμε να δημιουργήσουμε πολλές συνδέσεις με τον server και να χρησιμοποιήσουμε έναν υψηλό FD ελπίζοντας ότι θα ταιριάζει με κάποιες από τις συνδέσεις μας.
Υπογραφές συμπεριφοράς για να βρείτε αυτές τις συναρτήσεις:
'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0) + p64(0) + (PLT + 0xb) + p64(ENTRY) + STOP
-> Αν εκτυπώνονται δεδομένα, τότε βρέθηκε η puts
'A' * offset + canary + rbp + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP
-> Αν εκτυπώνονται δεδομένα, τότε βρέθηκε η dprintf
'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + (RIP + 0x1) + p64(0x0) + (PLT + 0xb ) + p64(STRCMP ENTRY) + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP
-> Αν εκτυπώνονται δεδομένα, τότε βρέθηκε η write
Original paper: https://www.scs.stanford.edu/brop/bittau-brop.pdf
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)