Buffer Overflow (part IV)

Buffer Overflow (part IV)

Ήρθε η ώρα να προσπαθήσουμε να εκτελέσουμε buffer overflow (με ανακατεύθυνση) σε ένα λειτουργικό σύστημα  Windows 11. Για να δούμε πόσο εύκολο είναι να το κάνεις αυτό… 😎
Θα ακολουθήσουμε την ίδια μέθοδο όπως στο Part ΙΙ.
Ας ξεκινήσουμε με τον πηγαίο κώδικα του μικρού μας προγράμματος επίδειξης, του bufferoverflow.c:

Για όσους θέλουν να αντιγράψουν τον κώδικα, για να γράψουν τα δικά τους παραδείγματα – tests, Ορίστε:

#include <iostream>
#include <string>
#pragma warning(disable : 4996) //_CRT_SECURE_NO_WARNINGS
#pragma runtime_checks("", off)
#pragma optimize("", off)
using namespace std;

bool checkProductKey(char *userKey) {
    char key[12];
    strcpy(key, userKey);
    bool n = (strcmp(userKey, "123-456") == 0);
    return n;
}

int main(int argc, char* argv[]) {
    char key[255];
    if (argc != 2) {
        cout << "Enter product key >";
        cin >> key;
    }
    else
        strcpy(key, argv[1]);

    bool iAllow = checkProductKey(key);

    if (!iAllow) {
        cout << "Wrong key!\n";
        return -1;
    }

    cout << "Welcome to the DEMO SA Application.\n";
    cout << "(c) 2023 all rights reserved.\n";
    return 0;
}

Δεν θα μπω στον κόπο να εξηγήσω τι κάνει το παραπάνω πρόγραμμα μιας και ο κώδικας του είναι πολύ προφανής. Πρόκειται για πρόγραμμα σε C,  που τρέχει από τη γραμμή εντολών των Windows Powershell .
Παρακάτω βλέπετε την εκτέλεση του προγράμματος με διάφορες παραμέτρους :

Ένα από τα πιο σημαντικά σημεία για να δουλέψετε το bof  είναι να επιλέξετε τις σωστές παραμέτρους compiler & linker.

Χρησιμοποιώ ένα console project σε C++ στο Visual Studio 2022 και αλλάζω ορισμένες προεπιλεγμένες παραμέτρους όπως υποδεικνύουν οι παρακάτω εικόνες.
Σημειώστε ότι ορισμένες παραμέτρους τις έχω ήδη ρυθμίσει από τον πηγαίο κώδικα, χρησιμοποιώντας οδηγίες pragma, αλλά για λόγους σαφήνειας θα επαναλάβω όλη τη διαδικασία εδώ:

1. Απενεργοποιήστε το επίπεδο προειδοποίησης (όχι απολύτως απαραίτητο αλλά χρήσιμο για αυτό το μικρό πρόγραμμα για να αποφύγετε κάποια ενοχλητικά μηνύματα)
2. Απενεργοποιήστε τους ελέγχους κύκλου ζωής ανάπτυξης ασφαλούς κώδικα – SDL=Security Development Lifecycle.

Δεν θέλω ο Optimizer να αλλάξει τίποτα στον κώδικά μου… (και στις δύο παραπάνω περιπτώσεις)

1. Ορίζω τα runtime checks στο default
2. Απενεργοποιώ κάθε security check

1. Απενεργοποιώ το ASLR (Address Space Layout Randomization)

2. Απενεργοποιώ το DEP (Data Execution Prevention), για να μπορώ να εκτελέσω κώδικα στο τμήμα μνήμης του STACK. Αυτό είναι απαραίτητο μόνο εάν θα ήθελα να εκτελέσω έναν shell στo Stack, αλλά δεν είναι απαραίτητο εάν εκτελέσω ένα Buffer Overflow με και απλώς αντικαταστήσω τη διεύθυνση RET για να ανακατευθύνω το πρόγραμμά μου σε διαφορετική θέση στον ίδιο κώδικα (τμήμα) – όπως έκανα στο μέρος II σε ένα BOX Linux.

Για να συνοψίσω όλες τις επιλογές που έθεσα για τη μεταγλώττιση του προγράμματός μου:
/JMC /permissive- /ifcOutput "Debug\" /GS- /analyze- /W0 /Zc:wchar_t /ZI /Gm- /Od /sdl /Fd"Debug\vc143.pdb" /Zc:inline /fp:precise /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /Gd /Oy- /MDd /FC /Fa"Debug\" /EHsc /nologo /Fo"Debug\" /Fp"Debug\BufferOverflow.pch" /diagnostics:column

Ήρθε λοιπόν η ώρα να ξεκινήσω τις δοκιμές μου και να ελέγξω τη συμπεριφορά του προγράμματος. Θα κάνω δύο τύπους δοκιμών:

  1. Στην πρώτη μου δοκιμή θα περάσω μια μεγάλη συμβολοσειρά ως παράμετρο, μεγαλύτερη από αυτή που ορίζεται στη γραμμή 9 (του πηγαίου κώδικα).
  2. Στο δεύτερο τεστ θα περάσω μια σύντομη συμβολοσειρά ως παράμετρο, προκειμένου να ελέγξω την κανονική λειτουργία του προγράμματος.
Δείτε επίσης:   Ασφαλής χρήση του Διαδικτύου: Συμβουλές για παιδιά

Επιπλέον θα βάλω κάποια breakpoints και θα ελέγξω την κατάσταση της μνήμης και κάποιες σημαντικές σημαίες (flags).

Για την 1η δοκιμή μου (την υπερχείλιση) έβαλα αυτό το όρισμα γραμμής εντολών στο πρόγραμμα εντοπισμού σφαλμάτων:

Η γνωστή συμβολοσειρά (που χρησιμοποιείται σε δοκιμές bof) ενός μοτίβου 4 bytes (το μέγεθος μιας διεύθυνσης μνήμης σε συστήματα 32 bit): AAAABBBBCCCCDDDDEEEE.

Για το δεύτερο τεστ μου (το κανονικό!) εισάγω απλώς αυτό:

Μόλις 10 “Α”.

Ας δούμε τώρα τι θα συμβεί όταν εκτελέσω τις δοκιμές μου, μελετώντας τις παρακάτω εικόνες:

Έβαλα ένα σημείο διακοπής (breakpoint) στη γραμμή 24, εκεί που καλείται η συνάρτηση checkProductKey.
Επιπλέον, έχω ενεργοποιήσει τρία σημαντικά Windows για να έχω όσο το δυνατόν περισσότερες πληροφορίες.
Σημαντική σημείωση : Για να δείτε και να επιλέξετε τις παρακάτω επιλογές, το πρόγραμμα πρέπει να εκτελείται και να διακοπεί κάποιο breakpoint – διαφορετικά αυτές οι επιλογές δεν είναι ορατές).

  1. Το Disassembly window (επιλέγοντας: Debug | Windows | Disassebly).
  2. Το Memory window (επιλέγοντας: Debug | Windows | Memory | Memory 1)
  3. Το Registers window (επιλέγοντας: Debug | Windows | Registers)

Ενώ η εκτέλεση έχει σταματήσει στο συγκεκριμένο σημείο διακοπής (στη γραμμή 24) επιλέγω το παράθυρο Disassembly:

[Κύρια εικόνα] : Όπως μπορούμε να δούμε την κλήση checkProductKey που βρίσκεται στη διεύθυνση μνήμης: 0x00414215 (θυμηθείτε: οι διευθύνσεις δίνονται σε δεκαεξαδική μορφή).

Συμβουλή: βλέπετε το textbox πάνω από το disassembly window – εκεί που λέει “Address: main(int, char * *)”: Εδώ μπορούμε να εισάγουμε για να δούμε τον κώδικα της κύριας συνάρτησης  ή μπορούμε να δώσουμε οποιαδήποτε άλλη function, όπως η checkProductKey.
Το ίδιο ισχύει και για το παράθυρο μνήμης (Memory window), εγώ το χρησιμοποιώ για να εισάγω διευθύνσεις μνήμης.

Συνεχίζω την εκτέλεση μέχρι το τελευταίο breakpoint, μέσα στη συνάρτηση checkProductKey , ακριβώς πριν η συνάρτηση επιστρέψει στον καλούντα της (στη γραμμή 12) παρακάτω:

Παραπάνω, βλέπω την κλασική δομή Stack.
Εάν ελέγξετε το παράθυρο Registers θα δείτε επίσης ότι ο Base Pointer βρίσκεται στη διεύθυνση 0x0019FD8C όπως φαίνεται στην εικόνα.
Αμέσως μετά τον Base Pointer βρίσκεται η Διεύθυνση RET. Αυτός είναι ο στόχος μου!

Συνεχίζοντας την εκτέλεση λαμβάνω ένα σφάλμα παραβίασης πρόσβασης :

Αν υποθέσουμε ότι διαβάσατε τα Parts IΙ και IIΙ, είναι πλέον εύκολο να καταλάβετε γιατί:
Όταν τελειώσει ο έλεγχος της συνάρτησης ProductKey, ο EIP λαμβάνει τη διεύθυνση RET (που έχει γεμίσει με “E” – 45 το οποίο είναι ο 16δικός κωδικός ascii του “E”) και προσπαθεί να επιστρέψει σε αυτήν τη διεύθυνση, την 0x45.45.45.45. Ωστόσο, αυτή η διεύθυνση δεν υπάρχει, εξ ου και το μήνυμα λάθους “Access violation reading location 0x45454545”.

Δείτε επίσης:   Ασφαλής χρήση των συστημάτων ιστοσελίδων κοινωνικής δικτύωσης

Για λόγους σαφήνειας, επιτρέψτε μου να σας υπενθυμίσω ξανά κάποια πράγματα που συζητήσαμε στο Μέρος ΙΙ:
Το παρακάτω είναι μια σχηματική αναπαράσταση αυτού που βλέπουμε στη Στοίβα μας ενώ εκτελείται  η συνάρτηση checkProductKey : 

Εισάγοντας μια πολύ μεγάλη συμβολοσειρά που περνάει ως παράμετρος η συνάρτησή μας, υπερκαλύπτει και αντικαθιστά και τις διευθύνσεις: EBP και RET!

Κοιτάξτε τώρα, πώς φαίνεται η ίδια δομή κατά την εκτέλεση της 2ης δοκιμής μας: Η κανονική: “AAAAAAAAAA”

Εδώ, τα πράγματα είναι πιο ξεκάθαρα: Αφού τελειώσει η συνάρτηση, θα επιστρέψει στη διεύθυνση καλούντος της, όπως υποδεικνύουν οι διευθύνσεις RET είναι: 0x00414221 (θυμηθείτε ότι ακολουθείται το μοντέλο little endian  – διαβάζουμε από τη μνήμη με αντίστροφη σειρά).
Μπορείτε να διασταυρώσετε με το [Main Image] για να ελέγξετε την ακριβή διεύθυνση (το 0x00414221 ) που θα πάει η λειτουργικότητα του προγράμματός μας.
Αυτό σημαίνει επίσης (όπως είπαμε ήδη στα τρία προηγούμενα Parts των άρθρων μας), ότι ο EIP θα είναι 0x00414221.
Ή, με άλλα λόγια
EIP = RET Address (at lines 12 & 13 above) είναι αυτό που ονομάζουμε Function Epilogue.

Μέχρι στιγμής όλα καλά: Γνωρίζουμε την ακριβή συμβολοσειρά που θα μπορεί να αντικαταστήσει τη διεύθυνση RET. Αυτή είναι η “AAAABBBBCCCCDDDDEEEE”
Τώρα αυτό που πρέπει να κάνουμε είναι να αντικαταστήσουμε το EEEE με μια υπάρχουσα διεύθυνση για να ανακατευθύνουμε το πρόγραμμα για να παρακάμψουμε τον έλεγχο ProductKey.
Μπορούμε να βρούμε αυτή τη διεύθυνση διαβάζοντας τον disassembly κώδικα: Βρισκόταν μερικές διευθύνσεις κάτω (δεν φαίνεται στην εικόνα) της [Κύριας Εικόνας]. Εκεί που εμφανίζουμε τα μηνύματα καλωσορίσματος.
Είναι ακριβώς εδώ:

Αυτή είναι η διεύθυνση που ψάχνουμε: 0x41424D !!
Είμαστε έτοιμοι,… σχεδόν!!
Όπως γνωρίζουμε, βρισκόμαστε σε λειτουργία εντοπισμού σφαλμάτων (debug mode). Η πραγματική διεύθυνση του προγράμματος όταν εκτελείται σε release mode  είναι συνήθως μια ελαφρώς διαφορετική διεύθυνση…

Αυτό που χρειαζόμαστε στην πραγματικότητα είναι την διεύθυνση σε release mode. Για αυτόν τον λόγο δημιουργούμε το τελικό εκτελέσιμο αρχείο σε release mode:

Προσοχή στην παγίδα: Όταν αλλάζετε το πρόγραμμα από Debug σε Release mode, όλες οι παράμετροι compiler & linker επαναφέρονται στις προεπιλεγμένες τους τιμές { 👿 👿  }, επομένως πρέπει να τις επαναφέρετε ακολουθώντας τις ρυθμίσεις που έκανα στην αρχή του άρθρου.

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

Αλλά πώς στο καλό, μπορώ να βρώ αυτήν τη διεύθυνση τώρα, με δεδομένο ότι δεν μπορώ πια να ψάξω σε debug mode, μέσω του Visual Studio;

Χμ… εδώ χρειάζεται λίγο Reversing ακόμα… και για αυτό το λόγο θα χρησιμοποιήσω ένα παλιό εργαλείο που ονομάζεται: Olly Debugger v.1.1 . Φυσικά μπορείτε να χρησιμοποιήσετε όσες άλλες επιλογές θέλετε, πολλές από αυτές χρησιμοποιούνται για τον εντοπισμό σφαλμάτων / αποσυναρμολόγηση ενός εκτελέσιμου αρχείου, όπως x32dbg , IDA κ.λπ…
Εκτελώντας το τελικό εκτελέσιμο αρχείο στο Olly, μπορώ εύκολα να εντοπίσω τη διεύθυνση της συνάρτησης checkProductKey :

Δείτε επίσης:   Συμβουλές: Πώς να βοηθήσετε κάποιον που ο λογαριασμός του έχει παραβιαστεί;

Όπως μπορείτε να δείτε παραπάνω, έχω αναδημιουργήσει το ίδιο περιβάλλον που είχα στη λειτουργία εντοπισμού σφαλμάτων του Visual Studio.
Όπως μπορείτε επίσης να δείτε (στο κάτω δεξιό μέρος – στο τμήμα της μνήμης), η διεύθυνση υπερχείλισης είναι λίγο… μεταγενέστερη σε σχέση με τη λειτουργία εντοπισμού σφαλμάτων. Το EBP συμπληρώνεται με 66.66.66.66 (“f”) ενώ εισάγω τη συμβολοσειρά “aaaaabbbbccddddeeeeeffff”. Αυτό σημαίνει ότι η διεύθυνση RET είναι αμέσως μετά το EBP, δηλαδή εδώ: aaaaabbbbccddddeeeeffffXXXX

Το ερώτημα τώρα είναι: Πού είναι η διεύθυνση που εμφανίζουμε τα μηνύματα καλωσορίσματος;
Αυτή είναι η διεύθυνση που πρέπει να βάλουμε στη θέση του XXXX παραπάνω για να αναγκάσουμε το πρόγραμμα να παρακάμψει όλους τους ελέγχους ProductKey.
Λοιπόν, δεν είναι τόσο δύσκολο να το βρείτε χρησιμοποιώντας τον Olly:

Ο μαγικός αριθμός είναι: 0x0040138B

Τώρα, ας δημιουργήσουμε το exploit μας σε ένα αρχείο χρησιμοποιώντας έναν Επεξεργαστή HEX. Αυτό είναι το εύκολο μέρος:

Θυμηθείτε, εισάγουμε το aaaaabbbbccccdddeeeeeffff 8B134000 . Θυμηθείτε τη διεύθυνση με αντίστροφη σειρά ανά byte: 8B.13.40.00 είναι η διεύθυνση διεύθυνση στόχος μας: 00.40.13.8B
και αποθηκεύω το αρχείο με το όνομα args στο δίσκο.
Λοιπόν… Ας το δοκιμάσουμε τώρα, για να δούμε τι έχουμε καταφέρει μέχρι τώρα:

Bingo! Καταφέραμε μια επιτυχημένη παράκαμψη των μέτρων ασφαλείας του προγράμματος με ανακατεύθυνση υπερχείλισης buffer!

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

Επίλογος

Αυτό είναι το τέλος της στοιχειώδους σειράς άρθρων μου σχετικά με τον τρόπο εκτέλεσης υπερχείλισης buffer και δοκιμής των αποτελεσμάτων και αποφυγής ορισμένων παγίδων.
Αυτό που θα ήθελα να δείξω στην πραγματικότητα, ΔΕΝ είναι μόνο πώς να κάνω το πραγματικό bof. Πιστεύω ότι αυτό που μετράει δεν είναι μόνο ο προορισμός αλλά το ίδιο το ταξίδι.
Η εμπειρία, τα εργαλεία, οι συμβουλές, τα κόλπα και οι παγίδες που αντιμετωπίζετε κατά τη διάρκεια του ταξιδιού, είναι ο πραγματικός στόχος.
Και αυτό δεν ισχύει μόνο για ένα ταπεινό Buffer Overflow… 😉

Happy Reversing and may the force be with you…

 

 

Με πολύτιμη εμπειρία στον κόσμο της κυβερνοασφάλειας συμβάλλει ενεργά στην κοινότητά μας με αναλύσεις ευπαθειών και οδηγούς.

Με τη συνεισφορά του, προσφέρει στην κοινότητα μας ένα πλούσιο φάσμα γνώσεων και δεξιοτήτων που ενισχύουν την ανάπτυξη και την ασφάλεια του ψηφιακού μας κόσμου.

Security enthusiast | Technology & Information Security Manager
CISA, CEH, CEH Item Writer, ISO27001 LA, DPO Executive

0 0 votes
Article Rating
Subscribe
Notify of
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x
Secured By miniOrange