Buffer Overflow (part II)

Buffer Overflow (part II)

0x0. Εισαγωγή

A buffer overflow (bof), or buffer overrun, is an anomaly whereby a program, while writing data to a buffer, overruns the buffer’s boundary and overwrites adjacent memory locations (Wikipedia).
Θα αναλύσουμε σε αυτό το άρθρο αυτή την πολύ παλιά ευπάθεια λογισμικού και θα δούμε αν αυτή εξακολουθεί να μετράει…

Από το πρώτο άρθρο του Aleph One (Elias Levy) το 1996, “Smashing The Stack For Fun And Profit“, έχουν περάσει πάνω από 25 χρόνια. Η απάντηση στην ερώτηση “Είναι αυτή η ευπάθεια ακόμα ενεργή;” είναι ένα μεγάλο: ΝΑΙ!
Αλλά πράγματι, σήμερα, με τα σύγχρονα περιβάλλοντα & γλώσσες ανάπτυξης λογισμικού (.Net, J2EE, RoR, κλπ) δεν είναι τόσο εύκολο να πραγματοποιηθεί μια τέτοια επίθεση, αλλά… οι εφαρμογές που δημιουργήθηκαν σε περισσότερο low level γλώσσες προγραμματισμού που είναι ευάλωτες στην buffer overflow (όπως η C ή η C++) εξακολουθούν να υπάρχουν, και είναι πολλές: Web servers, Λειτουργικά Συστήματα, DBMSs και γενικά, σχεδόν σε όλα όσα βάζουμε να “τρέχουν” οι “ασφαλείς” εφαρμογές μας…

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

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

Ας ξεκινήσουμε το ταξίδι μας με Linux…

0x1. Linux 64bit σε εφαρμογή 64bit

Ας δημιουργήσουμε τη δοκιμαστική μας εφαρμογή σε γλώσσα C για να ολοκληρώσουμε τη δοκιμή.
Ο πηγαίος κώδικας είναι ο εξής:

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

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

$ gcc -m64 -g -fno-stack-protector -z execstack -o demo demo.c

Ας εξηγήσουμε τις σημαίες:

  • -m64: δημιουργία ενός εκτελέσιμου αρχείου σε αρχιτεκτονική 64bit (για να είμαι ειλικρινής, θα μπορούσα να το παραλείψω στο συγκεκριμένο σύστημα, αφού χρησιμοποιείται ως προεπιλογή).
  • -g: για την παραγωγή ειδικών μεταδεδομένων για τον αποσφαλματωτή (debugger – που θα χρησιμοποιήσουμε αργότερα)
  • -fno-stack-protector: κατά την εκτέλεση του προγράμματος δεν θα πραγματοποιείται έλεγχος στοίβας μνήμης (no stack protection).
  • -z execstack: επιτρέπει την εκτέλεση κώδικα σε τμήμα στοίβας (στη μνήμη).
  • -o demo: όνομα του τελικού εκτελέσιμου αρχείου θα είναι demo.

Το συγκεκριμένο παράδειγμα έχει υλοποιηθεί στο KALI Linux 2022.4:

Επιπλέον, είναι πολύ σημαντικό να απενεργοποιήσετε την προστασία ASLR (Address Space Layout Randomization).
Για να το κάνετε αυτό, στο kali απλά εισάγετε την εντολή ως root:

$ sudo echo 0 > /proc/sys/kernel/randomize_va_space

Οποιαδήποτε στιγμή θέλετε να επαναφέρετε αυτόν τον έλεγχο ASLR, απλά πληκτρολογήστε αυτό:

$ sudo echo 2 > /proc/sys/kernel/randomize_va_space

  • 0: σημαίνει OFF
  • 2: σημαίνει ON

Και… btw, αν θέλετε να ελέγξετε, ποια είναι η τρέχουσα κατάσταση του ASLR, εισάγετε αυτό:

$ sudo cat /proc/sys/kernel/randomize_va_space

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

Όπως μπορείτε να δείτε, υπάρχουν δύο (τουλάχιστον!!) κύρια τρωτά σημεία στο πρόγραμμα: Είναι όπου χρησιμοποιείται η ευάλωτη συνάρτηση strcpy και ας το δούμε αυτό στην πράξη:

O παραπάνω είναι ένας σχετικά εύκολος ο τρόπος για να ελέγξουμε αν το πρόγραμμά μας είναι ευάλωτο σε μια επίθεση buffer overflow: Δίνουμε ένα πολύ μεγάλο αλφαριθμητικό και στη συνέχεια ελέγχουμε την απόκριση του προγράμματος. Αν λάβουμε ένα ‘segmentation fault’ τότε είναι πιθανό να έχουμε μια ευπάθεια bof (buffer overflow)…

0x1.0x1. Η προσέγγιση ROP

Σύμφωνα με τη Wikipedia: Return-oriented programming (ROP) is a computer security exploit technique that allows an attacker to execute code in the presence of security defenses such as executable space protection and code signing.
Ο κύριος στόχος μας εδώ είναι να εκμεταλλευτούμε την ευπάθεια ενός προγράμματος προκειμένου να ανακατευθύνουμε τη ροή του προγράμματος εκεί που μας αρέσει (και εκεί που μπορούμε, βεβαίως βεβαίως)…

Ας εξετάσουμε τη συμπεριφορά του προγράμματος χρησιμοποιώντας τον αποσφαλματωτή gdb.

 

Βάζουμε ένα σημείο διακοπής (break point) στη γραμμή 7, αμέσως μετά την strcpy, μέσα στη συνάρτηση checkProductKey.

Δείτε επίσης:   Buffer Overflow (part IV)

Εκτελούμε την εφαρμογή μέσα στον αποσφαλματωτή ( απλά πληκτρολογούμε dbg ./demo ) περνώντας ένα κανονικό αλφαριθμητικό, προκειμένου να ελέγξουμε πού βρίσκεται η διεύθυνση RET (περισσότερα γι’ αυτό παρακάτω). Έτσι θα έχουμε:

Ας συζητήσουμε μερικά πραγματάκια εδώ…

Η παραπάνω εικόνα χωρίζεται σε 2 κονσόλες:

  • Η αριστερή κονσόλα είναι ο disassembled κώδικας του προγράμματος που λαμβάνω μετά την τοποθέτηση του σημείου διακοπής στη γραμμή 7 (break 7) και την εισαγωγή disassemble /s main. Η παράμετρος “/s” δίνει εντολή στον αποσφαλματωτή να παράγει τον disassembled κώδικα μαζί με τον αντίστοιχο πηγαίο κώδικα C (thanks to the flag -g στη φάση της μεταγλώττισης).
  • Η δεξιά κονσόλα είναι η κονσόλα εκτέλεσης στην οποία δουλεύουμε δοκιμάζοντας το πρόγραμμα μας.
  • Σημειώστε επίσης το εξής σημαντικό: οι διευθύνσεις μνήμης (που ορίζονται με μπλε χρώμα) είναι ίδιες και στις δύο εικόνες. Αυτό είναι πολύ δύσκολο να συμβεί σε πραγματικές καταστάσεις, επειδή αυτές οι διευθύνσεις μπορεί να μην είναι πάντα οι ίδιες κάθε φορά που εκτελούμε το πρόγραμμα. Αυτό συμβαίνει εξαιτίας του ASLR, και γι’ αυτό το λόγο το απενεργοποιούμε, παραπάνω, αλλιώς μπορεί να καταλήξουμε να κυνηγάμε… φαντάσματα, πιστέψτε με! — burned-burned-burned!!

Όπως μπορείτε να δείτε στα ΠΡΑΣΙΝΑ ΚΟΥΤΙΑ τρέχουμε το πρόγραμμα στον αποσφαλματωτή περνώντας 10 “Α” ως όρισμα: run AAAAAAAAAA
Αυτό αποθηκεύεται στη στοίβα (stack – μην μου πείτε οτι δεν το ξέρετε – see part I) καθώς καλείται η συνάρτηση checkProductKey αφού περνάει ως όρισμα στη συνάρτηση.
Μπορούμε να δούμε τη στοίβα στη μνήμη εισάγοντας την εντολή x/24xg $rsp στον αποσφαλματωτή. Αυτή η εντολή δίνει εντολή στον αποσφαλματωτή να εμφανίσει σε δεκαεξαδική μορφή “x”, τις επόμενες 24 θέσεις μνήμης σε γιγαντιαία (“g”) μορφή 8-bytes.
Το $rsp είναι το γνωστό Stack Pointer (το παλιό ESP στα 32bit συστήματα – see again part I) όπου στην περίπτωσή μας δείχνει τις μεταβλητές που πέρασαν ως ορίσματα μέσα στη συνάρτησή μας.

Τα “AAAAAAAAAA” αναπαρίστανται εδώ σε δεκαεξαδική μορφή (“x”) από τον αριθμό “41” που είναι η ASCII δεκαεξαδική αναπαράσταση του γράμματος “A”.
Επίσης, σημειώστε ότι τα 10 “Α” αποθηκεύονται στη θέση μνήμης με αντίστροφη σειρά, καθώς πρόκειται για την αρχιτεκτονική little endian.
Έτσι, η πραγματική συμβολοσειρά που διατηρείται στη στοίβα (στη μνήμη) είναι η “41.41.41.41.41.41.41.41.41.41.00” (έβαλα το “.” για να είναι πιο ευανάγνωστο).
Σημειώστε ότι το “00” είναι ο μηδενικός χαρακτήρας που δηλώνει τον τερματισμό της συμβολοσειράς. Να θυμάστε αυτό το “00” επειδή παίζει σημαντικό ρόλο (ως εμπόδιο) στην ανάπτυξη exploits γενικότερα, και ειδικά στη δημιουργία shellcode (ή bytecode) (περισσότερα για αυτό στο part III). Το βασικό σημείο που πρέπει να γνωρίζετε εδώ είναι το εξής: Η ανάγνωση (ή μερικές φορές η εκτέλεση) μιας σειράς θέσεων μνήμης διακόπτεται όταν το σύστημα συναντήσει ένα byte 00.

Επικεντρωθείτε τώρα στα κόκκινα κουτάκια της εικόνας μας και θυμηθείτε πού βρισκόμαστε: Βρισκόμαστε μέσα στη συνάρτηση checkProductKey.
Όταν η συνάρτηση τελειώσει, η λειτουργικότητα του προγράμματος πρέπει να επιστρέψει στο σημείο που κλήθηκε. Για να είμαστε πιο συγκεκριμένοι: πρέπει να επιστρέψει στη διεύθυνση του καλούντα.
Προκειμένου το σύστημα να θυμάται αυτή τη διεύθυνση, την αποθηκεύει σε μια συγκεκριμένη θέση μνήμης μέσα στο buffer της τρέχουσας συνάρτησης. Ονομάζουμε αυτή τη διεύθυνση: διεύθυνση RET (RETurn). Είναι μια από τις πιο σημαντικές διευθύνσεις των επιθέσεων buffer overflow και θεωρείται το “ιερό δισκοπότηρο” κάθε εκμετάλλευσης μιας αδυναμίας buffer overflow.
Όπως μπορείτε να δείτε στο κόκκινο πλαίσιο στη δεξιά κονσόλα, η διεύθυνση RET αποθηκεύεται στο τέλος του buffer, μετά τη διεύθυνση του αλφαριθμητικού εισόδου μας “AAAAAAAAAA” και κάποιες άλλες διευθύνσεις (όπως ο Base Pointer, κάποιες μεταβλητές περιβάλλοντος κ.λπ. που είναι σημαντικές… αλλά όχι τόσο σημαντικές για την ώρα).

Όπως μπορείτε να φανταστείτε αν εισάγουμε ένα πολύ μεγάλο αλφαριθμητικό εισόδου, μεγαλύτερο από αυτό που έχει κρατήσει το πρόγραμμα μας (στην περίπτωσή μας είναι 12 – λόγω της char key[12]; στη γραμμή 5) τότε αυτό θα γράψουμε από πάνω από όλες τις διευθύνσεις που ακολουθούν αυτή τη μεταβλητή, συμπεριλαμβανομένης της διεύθυνσης RET. Αυτό είναι πολύ σημαντικό και πολύ δύσκολο!
και αυτός είναι ο λόγος:
Έστω οτι δώσουμε αυτό σαν input: “AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA”.
To buffer μας μπορεί να χωρέσει μόνο τους πρώτους 12 χαρακτήρες (ή bytes στην αρχιτεκτονική i386), συμπεριλαμβανομένου του χαρακτήρα τερματισμού, δηλαδή του “00”. Τι γίνεται λοιπόν με τους υπόλοιπους χαρακτήρες; Μα, θα αντικαταστήσουν τις γειτονικές διευθύνσεις μνήμης… συμπεριλαμβανομένης της διεύθυνσης που βρίσκεται το RET .
Έτσι, η διεύθυνση RET θα γεμίσει με “A”, άρα “AAAAAAAA” ή “4141414141414141”.
Σημειώστε ότι επίτηδες επέλεξα 8 bytes για να δηλώσω τη διεύθυνση σε συστήματα 64bit.
Γενικά, θα πρέπει να γνωρίζετε ότι στα 64bit συστήματα (σε αντίθεση με τα 32bit συστήματα που είδαμε σαν θεωρία στο Part I) έχουμε τις εξής διαφορές:

Δείτε επίσης:   Buffer Overflow (part I)

Οι καταχωρητές γενικού σκοπού έχουν επεκταθεί σε 64-bit. Έτσι έχουμε τώρα τους RAX, RBX, RCX, RDX, RSI και RDI.

  • Ο Instruction Pointer, ο Base Pointer και ο Stack Pointer έχουν επίσης επεκταθεί σε 64-bit ως RIP, RBP και RSP αντίστοιχα.
  • Έχουν παρασχεθεί οι πρόσθετοι καταχωρητές: R8 έως R15.
  • Οι pointers έχουν μήκος 8-bytes.
  • Τα push/pop στο STACK έχουν μήκος 8-bytes.
  • To μέγιστο μέγεθος μιας επιτρεπτής διεύθυνσης είναι: 0x00007FFFFFFFFFFFFFFF (περισσότερα σχετικά με αυτό θα πούμε λίγο αργότερα).

Δείτε τώρα τη διεύθυνση RET σε συνδυασμό με μια άλλη πολύ σημαντική διεύθυνση, η οποία είναι αποθηκευμένη στη μνήμη του επεξεργαστή: Ο Instruction Pointer ή $RIP. Αυτή η διεύθυνση έχει πάντα τη διεύθυνση της επόμενης εντολής που θα εκτελέσει το πρόγραμμα. Έτσι, όταν φτάσουμε στο τέλος της συνάρτησης μας, εκτελείται η εντολή $RIP = RET. Έτσι, η λειτουργικότητα του προγράμματος θα επιστρέψει στον καλούντα, αφού η διεύθυνση RET διατηρεί τη διεύθυνση του καλούντος, και για να είμαστε πιο συγκεκριμένοι διατηρεί τη διεύθυνση της επόμενης εντολής από αυτή που κλήθηκε τη συνάρτηση.

Έτσι, στο παραπάνω θεωρητικό μας παράδειγμα η διεύθυνση RET θα γεμίσει με “4141414141414141”, δηλαδή το πρόγραμμα, στο τέλος της συνάρτησης checkProductKey θα προσπαθήσει να μεταβεί στη διεύθυνση 0x4141414141414141.
Αλλά μια τέτοια διεύθυνση δεν υπάρχει στο πλαίσιο του προγράμματός μας, οπότε λαμβάνουμε το γνωστό σφάλμα: segmentation fault

Έχοντας κατά νου τις παραπάνω γνώσεις ας προσπαθήσουμε να εκμεταλλευτούμε το πρόγραμμά μας.
Εξετάζοντας τη δομή $rsp στο αρχικό μας παράδειγμα (στην παραπάνω εικόνα) μπορούμε να δούμε ότι χρειαζόμαστε 3 x 8 bytes προκειμένου να ικανοποιήσουμε τη διεύθυνση RET.
Ας το αποδείξουμε αυτό με το ακόλουθο παράδειγμα:

Όπως μπορείτε να δείτε, εισάγουμε 24 x “A” = “AAAAAAAAAAAAAAAAAAAAAAAA” και τα 8 “B” “BBBBBBBB” αντικαθιστούν τη διεύθυνση RET.

Το πρόγραμμα κατέρρευσε…
Αυτό που θα θέλαμε να κάνουμε στην πραγματικότητα είναι να αντικαταστήσουμε την $RIP με μια άκυρη διεύθυνση. Αλλά, στην πραγματικότητα δεν ελέγχουμε καθόλου την $RIP. Έχουμε έλεγχο μόνο στην RET όπως ήδη βλέπετε.
Για την ενημέρωσή σας πρέπει να το ξέρετε αυτό:
Το μέγιστο μέγεθος διεύθυνσης που μπορούμε να χειριστούμε στην αρχιτεκτονική 64bit είναι 0x00007FFFFFFFFFFFFFFF. Αυτό που κάναμε μέχρι εδώ, είναι ότι γράψαμε επάνω στην $RIP, μέσω της RET, τη μη κανονική διεύθυνση 0x4242424242424242, η οποία προκαλεί τον επεξεργαστή να εγείρει μια εξαίρεση.
[Αν έχετε μπερδευτεί λίγο με τις 64μπιτες διευθύνσεις, μπορείτε να διαβάσετε περισσότερα για αυτή την αρχιτεκτονική των 64bit
εδώ.]

Οπότε, ο στόχος ήταν να βρούμε το offset με το οποίο θα αντικαταστήσουμε την RET και κατά συνέπεια την $RIP με μια κανονική (υπαρκτή) διεύθυνση.
Για το λόγο αυτό χρησιμοποιώ ένα κυκλικό μοτίβο (ας πούμε “AAAAAABBBB” κλπ) και το δοκιμάζω μέχρι να καταλήξω στην απαραίτητη διεύθυνση (και ναι, ξέρω ότι υπάρχουν και άλλες δημοσιευμένοι μέθοδοι που υπολογίζουν διαφορετικά το απαιτούμενο offset, αλλά αυτή που παρουσίασα εδώ επίσης λειτουργεί… οπότε… ΟΚ! 😎 ).

Έτσι, θα πάω να αντικαταστήσω το “BBBBBBBBBB” με μια υπάρχουσα διεύθυνση του υπάρχοντος πηγαίου κώδικα (Code Segment) προκειμένου να παρακάμψω τους ελέγχους που γίνονται στον κώδικα για το σωστό κλειδί.
Αυτή η διεύθυνση είναι η ακόλουθη:

Σε αυτό το σημείο έχουν περάσει όλοι οι έλεγχοι σχετικά με το κλειδί προϊόντος, και αυτό είναι που ονομάζω ROP (βλ. τον τίτλο της παραγράφου).
Πρέπει να αντικαταστήσω το “B” με αυτή τη διεύθυνση: 0x0000555555555261

Αλλά πρέπει να περάσω την τιμή της ως bytes… και όχι ως συμβολοσειρά! Πώς να το κάνω αυτό;
Υπάρχουν διάφοροι τρόποι για να περάσετε αυτό το αλφαριθμητικό ως “bytes” στον αποσφαλματωτή:
Θα επιλέξω τον πιο γρήγορο… ρίξτε μια ματιά εδώ:

Ας εξετάσουμε λίγο την επίθεση “string” :
Εδώ χρησιμοποιώ λίγο την python v.2 (και στην v.3 λειτουργεί επίσης αν βάλω την εκτύπωση συμβολοσειράς σε παρενθέσεις: “(” και “)” ) :
python2 -c ‘print “1”*24 + “\x55\x55\x55\x55\x55\x52\x61″[::-1]’

Αντί να γράψω μια εξήγηση σε κείμενο εδώ θα δείξω την εικόνα του αποτελέσματος όταν το εκτελώ στη γραμμή εντολών:

Δείτε επίσης:   Πως να πιάσετε συχνότητα WiFI ακτίνας 2-5 Χιλιομέτρων ( + Amplified Aircrack )

Όπως μπορείτε να δείτε, περνάω τα παραπάνω αποτελέσματα, ως όρισμα γραμμής εντολών στο dbg χρησιμοποιώντας τον συμβολισμό run $( <SYSTEM_COMMAND> ).
Αυτός είναι ένας εύκολος τρόπος για να περάσετε τη συμβολοσειρά της γραμμής εντολών ως bytes σε ένα πρόγραμμα.
Το πρόγραμμα “σκάει” (με segmentation fault), αλλά στο τέλος, έκανα μια επιτυχημένη ανακατεύθυνση, όπως μπορείτε να δείτε το μήνυμα ‘Welcome’ (στο πράσινο πλαίσιο παραπάνω). Έτσι, παρακάμπτω τον μηχανισμό ελέγχου!!

Σημειώστε επίσης, τον τρόπο με τον οποίο περνάω τη διεύθυνση στόχου, με αντίστροφη σειρά: ο συμβολισμός [::-1] της python απλά έβαλε τη δεκαεξαδική συμβολοσειρά αντίστροφα.
Έτσι, το “55.55.55.55.55.52.61” θα μπει στα string arguments ως “61.52.55.55.55.55.55.55” (θυμηθείτε την αρχιτεκτονική little endian που ανέφερα παραπάνω).
Σημαντική σημείωση: το “555555555261” τοποθετείται στη μνήμη με αντίστροφη σειρά ανά ζεύγος: το “55.55.55.55.55.52.61” θα τοποθετηθεί στη μνήμη ως “61.52.55.55.55.55.55“.
Και γενικά, κάθε διεύθυνση μνήμης που θα διαβάσουμε, είναι μια σειρά από ζεύγη – ή μια σειρά από 1 byte (8bits, από το 0 έως το 255 – ή αλλιώς 2^8=256 πιθανές διαφορετικές τιμές).
“Historically, the byte was the number of bits used to encode a single character of text in a computer and for this reason it is the smallest addressable unit of memory in many computer architectures.” (wikipedia)
Το ίδιο ισχύει και για τα i386 64-bit Windows 10 αλλά και στο i386 64-bit Kali Linux.

Επιπλέον, ο συμβολισμός ‘\x’ υποδηλώνει ότι μιλάω στο πρόγραμμα όχι σε δεκαδικό αλλά σε δεκαεξαδικό σύστημα.
Το κάνω αυτό επειδή ο αποσφαλματωτής εμφανίζει τις διευθύνσεις μνήμης σε δεκαεξαδική σημειογραφία.
Αν αναρωτιέστε γιατί ο αποσφαλματωτής προτιμά τη δεκαεξαδική σημειογραφία, θα έλεγα το εξής:
Η δεκαδική αναπαράσταση της δεκαεξαδικής διεύθυνσης 555555555261 είναι αυτός ο αριθμός: 93824992236129. Λοιπόν, φαίνεται σαν έναν τηλεφωνικό αριθμό, ε;! 😆
Έτσι… δεν είναι τόσο εύκολο για τον άνθρωπο να αντιμετωπίσει τόσο μεγάλους αριθμούς μήκους. Η δεκαεξαδική σημειογραφία είναι πιο εύχρηστη και συνεπώς διαχειρίσιμη. Αυτός είναι ο λόγος για τον οποίο έχει υιοθετηθεί από (σχεδόν) όλα τα προγράμματα εντοπισμού σφαλμάτων στον κόσμο για την αναπαράσταση διευθύνσεων η 16δική αναπαράσταση.

Μέχρι στιγμής, λοιπόν,  έχω εκτελέσει, στην ουσία, ένα “goto” (θυμάστε στη BASIC την εντολή goto😉 ή ένα JUMP (assembly-wise) με τη χρήση του debugger.
Αυτό που χρειάζομαι τώρα είναι να κάνω το ίδιο στο τελικό εκτελέσιμο από τη γραμμή εντολών σε μια κονσόλα, και ΟΧΙ με τη χρήση του dbg.

Λοιπόν, η απάντηση φαίνεται αρκετά απλή: Απλά το τρέχω από τη γραμμή εντολών αυτό:

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

Σημειώστε ότι τα αποτελέσματα στην παραπάνω εικόνα δεν είναι πάντα τόσο προφανή, ειδικά όταν πειράζουμε τις διευθύνσεις μνήμης και τη στοίβα απευθείας. Αρκετές φορές αυτό που βλέπω στον αποσφαλματωτή δεν είναι ακριβώς το ίδιο με αυτό που βλέπω όταν εκτελώ το πρόγραμμα απευθείας από τη γραμμή εντολών και αυτό είναι πολύ συνηθισμένο όταν πρέπει να αναφερθώ σε διευθύνσεις στη στοίβα (και όχι στο τμήμα κώδικα όπως έκανα εδώ).
Τέτοιο παράδειγμα θα δούμε στο Μέρος ΙΙI αυτής της σειράς άρθρων, όταν θα δείξω πώς να βάλω ένα “shell” στη στοίβα και πώς να το εκτελέσω. Επιπλέον στο Μέρος ΙΙI θα δούμε τι είναι το bytecode, πώς θα δημιουργήσουμε ή θα βρούμε κάποιους έτοιμα byte-codes και πώς θα τα ελέγξουμε. Θα δούμε, οτι σε τέτοιες περιπτώσεις ότι τα αποτελέσματα μπορεί να μην είναι τόσο ντετερμινιστικά όσο θα περιμέναμε…

Happy Reversing!

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

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

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

0 0 votes
Article Rating
Subscribe
Notify of
1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments

Thank you

1
0
Would love your thoughts, please comment.x
()
x
Secured By miniOrange