Καλώς ορίσατε στο dotNETZone.gr - Σύνδεση | Εγγραφή | Βοήθεια
σε

 

Αρχική σελίδα Ιστολόγια Συζητήσεις Εκθέσεις Φωτογραφιών Αρχειοθήκες

Pointer Overflow Attacks

Îåêßíçóå áðü ôï ìÝëïò Thiseas. Τελευταία δημοσίευση από το μέλος Thiseas στις 28-08-2007, 00:09. Υπάρχουν 0 απαντήσεις.
Ταξινόμηση Δημοσιεύσεων: Προηγούμενο Επόμενο
  •  28-08-2007, 00:09 34525

    Pointer Overflow Attacks

    Pointer Overflow Attacks

    Τα διάφορα buffer oveflows είναι αρκετά "της μόδας" τελευταία και αυτό για πολλούς λόγους:
    1. Πολλά προγράμματα είναι γραμμένα απρόσεκτα και πρόχειρα αφήνοντας "τρύπες".
    2. Πολλοί προγραμματιστές απλά δεν γνωρίζουν πως να προστατέψουν τα προγράμματα τους.
    3. Παλιές versions των compilers δεν έχουν μεθόδους προστασίας από επιθέσεις ή χρησιοποιούν μη ασφαλείς functions (π.χ. strcpy).


    Ο σκοπός του post αυτού είναι να παρουσιάσω ένα πολύ απλό πρόγραμμα το οποίο είναι ευάλωτο (vulnerable) σε επίθέσεις Pointer Overflow.
    Πρώτα θα παρουσιάσω το πρόγραμμα και μετά θα το τροποποιήσω έτσι ώστε να υλοποιήσω μια τέτοια επίθεση.
    Φυσικά όλα αυτά που θα παρουσιαστούν είναι ΕΝΤΕΛΩΣ εκπαιδευτικά και ο λόγος που τα δίνω είναι για περαιτέρω μελέτη και προβληματισμό για την αποφυγή τέτοιων προβλημάτων στα προγράμματα σας.

    Την βασική ιδέα του προγράμματος που σας παρουσιαζω την πήρα από το βιβλίο "THE ART OF COMPUTER VIRUS RESEARCH AND DEFENSE" του Peter Szor.

    Tο πρόγραμμα το έχω δοκιμάσει σε Windows XP στους compilers: Dev C++ 4.9.9.2 και Microsoft Visual C++ 2005.
    Θα ήθελα να παρουσιάσω τα παραδείγματα όπως τα έκανα στο Visual Studio 2005 και αυτό γιατί θέλω επίσης να δείξω κάποιες πολύ "καλές" δυνατότητες του πακέτου.

    Θα πρέπει να δημιουργείσετε ένα C++ project για WIN32 console application.

    Για να περάσει compile το πρόγραμμα στην MSC θα πρέπει να δηλώσετε στα options του compilation οτι το πρόγραμμα δεν χρησιμοποιεί "precompiled headers".
    Αυτό μπορείτε να το κάνετε πηγαίνοντας στο:
    Project | <Program Name> Properties... | Configuration Properties | C/C++ | Precopiled Headers = "Not Using Precompiled Headers"

    Βεβαια για να μπορέσετε να κατανοήσετε τα αποτελέσματα πρέπει να κάνετε compile σε debug mode.
    Συγκεκριμένα (για τους λάτρεις της command line - και όχι μόνο) οι flags που θα πρέπει να έχετε είναι:
    /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /Gm /EHsc /MDd /Fo"Debug\\" /Fd"Debug\vc80.pdb" /W3 /nologo /c /Wp64 /ZI /TP /errorReport:prompt

    Έστω λοιπόν οτι έχουμε το παρακάτω μικρό και αθώο πρόγραμμα:

    001 #include "stdio.h"
    002 #include "string.h"
    003 #include "stdlib.h"
    004
    005 int CallBack(char *szTemp) // Μια απλή function που δέχεται ένα string και το τυπώνει στην οθόνη.
    006 {
    007 printf("CallBack(%s)\n", szTemp);
    008 return 0;
    009 }
    010
    011 int main(int argc, char **argv)
    012 {
    013 static char bufferMusic; // Ορίζω ένα string 8 θέσεων.
    014 static int (*funcptr)(char *szTemp); // Ορίζω έναν pointer σε function που δέχεται σαν παραμετρο ενα pointer σε χαρακτήρα/ες (δηλ. ενα string).
    015
    016 funcptr = CallBack; // Θέτω τον pointer να δείχνει την function που όρισα στην αρχή.
    017 strcpy(buffer, "hi!"); // Δίνω στην μεταβλητή Buffer μια τιμή...
    018
    019 (*funcptr)("param"); // Καλώ την function με τη χρήση του pointer που δείχνει σε αυτήν.
    020
    021 printf("End of the program.\n"); // Ωχ!!! τι κάνω εδώ? ξέχασα... ;)
    022
    023 return 0; // Επιστρεφω στο λειτουργικό (στην μεταβλητη συστήματος errorlevel) την τιμή 0. Δηλ. Τέλος Προγράμματος.
    024 }


    Αν αυτό το τρέξουμε θα πάρουμε τα παρακάτω:
    CallBack(param)
    End of the program.


    Τίποτα το ιδιαίτερο μέχρι στιγμής.

    Παρατηρώ λίγο τον κώδικα...
    χμ...
    Κατ' αρχάς υπάρχει η strcpy... χμ, κακό αυτό... Ωπα! Εδω έχουμε vulnerable Κώδικα!!
    Σκέφτομαι....
    Αν κάνω buffer overflow στην μεταβλητή buffer τι θα γίνει?
    Δηλαδή αν γράψω την γραμμή 017 ως εξής:

    017 strcpy(buffer, "1234567_102030");

    Ξερετε τι θα γίνει?
    H Buffer είναι 8 χαρακτηρες.
    Άρα θα κρατήσει τους πρώτους 8 χαρακτηρες στην buffer και οι υπόλοιποι '102030' θα... "περισέψουν"!!
    Αλλά επειδή στον κόσμο του προγραμματισμού δεν περισεύει τίποτα, αυτοί οι υπόλοιποι χαρακτήρες θα θεωρήσει το πρόγραμμα οτι ανήκουν στην επομενη εντολη.
    Ποιά ειναι η επόμενη εντολή?
    Προσέχτε μην πείτε την γραμμή 018 ή την 019!!
    Κάνω overwrite το buffer!
    Που ορίζεται το buffer? Στην γραμμή 013 (είδες? είναι και γρουσούζικη!)
    Άρα το "περίσσεμα" ποια γραμμή θα καλύψει?... μα την 014.
    Ποια είναι η 014... η δήλωση του Function Pointer!!!!
    Voila!!
    Μετά λοιπόν την εκτέλεση της αλλαγμένης γραμμής 017 θα επιρεαστεί η μνήμη που έχει κρατηθεί από τις γραμμές 013 και 014, οπότε ο function pointer δεν θα δείχνει πια την fuction callback.
    Τι θα δείχνει?
    Μα το περίσευμα μου... την εντολή που υπάρχει στην διεύθυνση 102030...
    χμ.... οκ οχι ακριβώς 102030 γιατι το πρόγραμμα δεν δουλεύει με τον σειριακό τρόπο που σκεφτόμαστε εμείς.
    Στην πράξη το πρόγραμμα θα εκτελέσει οτι βρίσκεται στη διεύθυνση χ30 χ20 χ10, δηλαδή το '102030' ανάποδα (ανα 2) και φυσικά δεκαεξαδικό!!
    Χοντρικά, σκεφτείτε οτι μετα την εκτέλεση του strcpy(buffer,  "1234567_102030"); η μνημη μου θα γίνει έτσι:
    MNHMH
    STACK  TIMH
    -------- ---------
    buffer 1234567_
    funPtr 102030

    Οπότε η γραμμή
    019 (*funcptr)("param");

    Θα πάει να εκτελέσει οτι είναι στην διεύθυνση funPtr που τώρα πια είναι το 102030!!

    Τώρα θα κάνουμε ένα κόλπο.
    θα κάνουμε buffer overflow και θα οδηγήσουμε το πρόγραμμα εκεί που θέλουμε εμείς.
    Ας πουμε να το οδηγήσουμε κατευθείαν να εκτελέσει την γραμμή 021 χωρίς να καλέσει ποτέ την function callback.

    Δηλαδή να κάνουμε buffer overflow και να δώσουμε σαν "περίσευμα" την διεύθυνση της εντολής στην γραμμή 021.
    Αν το πετύχουμε αυτό, θα πρέπει μόλις εκτελέσουμε το πρόγραμμα να δούμε:

    End of the program.

    και όχι το παρακάτω που βλέπαμε πριν:
    CallBack(param)
    End of the program.


    Χμ.... πως θα το κάνουμε αυτό?
    Το Visual Studio μας προσφέρει ένα όμορφο τρόπο...
    Λοιπόν.... σηκώνουμε μανίκια γιατί θα λερώσουμε λίγο τα χέρια μας...
    Δεν αλλάζουμε τίποτα!! στο πρόγραμμα μας ακόμα!!
    Βάζουμε ένα break point στην γραμμη 021.
    Πατάμε f6 για build και μετά
    Πατάμε f5 για να ξεκινήσει το debug.
    Μόλις τοποθετηθουμε στην γραμμή 021, πατάμε CTRL+ALT+D ή από το μενού (Debug|Windows|Disassembly).
    Θα ανοίξει ένα window με τον dissasmbled κώδικα που μόλις γράψαμε και τον cursor τοποθετημένο στο κρίσιμο σημείο:
        ....
        ....
        19:     (*funcptr)("param");
    004112B5 68 68 46 41 00   push        offset string "param" (414668h)
    004112BA FF 15 28 61 41 00 call        dword ptr [funcptr (416128h)]
    004112C0 83 C4 04         add         esp,4
        20:
        21:     printf("End of the program.\n");
    004112C3 68 4C 46 41 00   push        offset string "End of the program.\n" (41464Ch)
    004112C8 FF 15 38 72 41 00 call        dword ptr [__imp__printf (417238h)]
    004112CE 83 C4 04         add         esp,4
        22:                                     
        23:     return 0;
        ....

    Εδώ φαίνεται καθαρά η διεύθυνση της γραμμής 21, Είναι η 004112C3 (φυσικά σε 16δική μορφή)

    Πώς πρέπει να την περάσουμε όμως εμείς για δουλέψει?
    θυμάστε το κόλπο?
    Ανα δυο αντίστροφα και δεκαεξαδικα.
    Δηλαδή:
    C31241 ή καλύτερα σαν C string: "\xc3\x12\x41"

    Τις 16δικές τιμές "68 4C 46 41 00" που είναι δίπλα στην διεύθνση ίσως εσείς δεν τις βλέπετε.
    Είναι τα λεγόμενα code bytes του Assembly κώδικα (χρήσιμο συνήθως σε αυτούς που φτιάχνουν Xploits).
    Αν θέτε (πορτοκαλάδα θέτε! - από πορτοκάλια? [άσχετο] ) να τα δείτε κάντε Right-Click και τσεκάρετε το "Show Code Bytes".

    Οπότε το πρόγραμμα μας γίνεται:

    001 #include "stdio.h"
    002 #include "string.h"
    003 #include "stdlib.h"
    004
    005 int CallBack(char *szTemp) // Μια απλή function που δέχεται ένα string και το τυπώνει στην οθόνη.
    006 {
    007 printf("CallBack(%s)\n", szTemp);
    008 return 0;
    009 }
    010
    011 int main(int argc, char **argv)
    012 {
    013 static char bufferMusic; // Ορίζω ένα string 8 θέσεων.
    014 static int (*funcptr)(char *szTemp); // Ορίζω έναν pointer σε function που δέχεται σαν παραμετρο ενα pointer σε χαρακτήρα/ες (δηλ. ενα string).
    015
    016 funcptr = CallBack; // Θέτω τον pointer να δείχνει την function που όρισα στην αρχή.
    017 strcpy(buffer, "1234567_\xc3\x12\x41"); // buffer overflow: goto at printf("End of the program.\n");
    018
    019 (*funcptr)("param"); // Καλώ την function με τη χρήση του pointer που δείχνει σε αυτήν.
    020
    021 printf("End of the program.\n"); // Ωχ!!! τι κάνω εδώ? ξέχασα... ;)
    022
    023 return 0; // Επιστρεφω στο λειτουργικό (στην μεταβλητη συστήματος errorlevel) την τιμή 0. Δηλ. Τέλος Προγράμματος.
    024 }



    Όλο το πρόβλημα δημιουργληθηκε απο την strcpy.
    Η Micro$oft προτείνει την strcpy_s, αν και νομίζω οτι και η strncpy είναι αρκετή, απλά θέλει ακόμα μια παράμετρο....


    Happy Coding Guys...

    Nothing to declare...
Προβολή Τροφοδοσίας RSS με μορφή XML
Με χρήση του Community Server (Commercial Edition), από την Telligent Systems