security - test - Comment fonctionne l'injection SQL de la BD XKCD "Bobby Tables"?




sql injection user id (8)

Juste en regardant:

(Source: https://xkcd.com/327/ )

Que fait ce SQL:

Robert'); DROP TABLE STUDENTS; --

Je sais que les deux sont des commentaires, mais le mot DROP n'est-il pas aussi commenté puisqu'il fait partie de la même ligne?

https://code.i-harness.com


TL; DR

-- The application accepts input, in this case 'Nancy', without attempting to
-- sanitize the input, such as by escaping special characters
school=> INSERT INTO students VALUES ('Nancy');
INSERT 0 1

-- SQL injection occurs when input into a database command is manipulated to
-- cause the database server to execute arbitrary SQL
school=> INSERT INTO students VALUES ('Robert'); DROP TABLE students; --');
INSERT 0 1
DROP TABLE

-- The student records are now gone - it could have been even worse!
school=> SELECT * FROM students;
ERROR:  relation "students" does not exist
LINE 1: SELECT * FROM students;
                      ^

Cela laisse tomber la table des étudiants. Pour clarifier ce qui se passe, essayons ceci avec une simple table contenant seulement le champ name et ajoutons une seule ligne (testée avec PostgreSQL 9.1.2):

school=> CREATE TABLE students (name TEXT PRIMARY KEY);
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "students_pkey" for table "students"
CREATE TABLE
school=> INSERT INTO students VALUES ('John');
INSERT 0 1

Supposons que l'application utilise le code SQL suivant pour insérer des données dans la table:

INSERT INTO students VALUES ('foobar');

Remplacez foobar par le nom réel de l'étudiant. Une opération d'insertion normale ressemblerait à ceci:

--                            Input:   Nancy
school=> INSERT INTO students VALUES ('Nancy');
INSERT 0 1

Lorsque nous interrogeons la table, nous obtenons ceci:

school=> SELECT * FROM students;
 name
-------
 John
 Nancy
(2 rows)

Que se passe-t-il lorsque nous insérons le nom de Little Bobby Tables dans la table?

--                            Input:   Robert'); DROP TABLE students; --
school=> INSERT INTO students VALUES ('Robert'); DROP TABLE students; --');
INSERT 0 1
DROP TABLE

L'injection SQL est ici le résultat du nom de l'étudiant terminant l'instruction et incluant une commande DROP TABLE séparée; les deux tirets à la fin de l'entrée sont destinés à commenter tout code restant qui provoquerait sinon une erreur. La dernière ligne de la sortie confirme que le serveur de base de données a supprimé la table.

Il est important de noter que pendant l'opération INSERT , l'application ne vérifie pas l'entrée des caractères spéciaux, et permet donc de saisir une entrée arbitraire dans la commande SQL. Cela signifie qu'un utilisateur malveillant peut insérer, dans un champ normalement destiné à l'entrée de l'utilisateur, des symboles spéciaux tels que des guillemets ainsi que du code SQL arbitraire pour que le système de base de données l'exécute, d'où SQL "injection" .

Le résultat?

school=> SELECT * FROM students;
ERROR:  relation "students" does not exist
LINE 1: SELECT * FROM students;
                      ^

L'injection SQL est l'équivalent de la base de données d'une vulnérabilité d' exécution de code arbitraire à distance dans un système d'exploitation ou une application. L'impact potentiel d'une attaque par injection SQL réussie ne peut pas être sous-estimé - selon le système de base de données et la configuration de l'application, il peut être utilisé par un attaquant pour provoquer une perte de données (comme dans le cas présent) code arbitraire sur la machine hôte elle-même.

Comme indiqué par la bande dessinée XKCD, une protection contre les attaques par injection SQL consiste à désinfecter les entrées de la base de données, par exemple en échappant aux caractères spéciaux, afin qu'elles ne puissent pas modifier la commande SQL sous-jacente et ne puissent pas exécuter de code SQL arbitraire. Si vous utilisez des requêtes paramétrées, par exemple en utilisant SqlParameter dans ADO.NET, l'entrée sera automatiquement nettoyée pour vous.


Comme tout le monde l'a déjà souligné, le '); ferme la déclaration originale, puis une deuxième déclaration suit. La plupart des frameworks, y compris des langages comme PHP, ont maintenant des paramètres de sécurité par défaut qui ne permettent pas plusieurs instructions dans une chaîne SQL. En PHP, par exemple, vous pouvez seulement exécuter plusieurs instructions dans une chaîne SQL en utilisant la fonction mysqli_multi_query .

Vous pouvez cependant manipuler une instruction SQL existante via l'injection SQL sans devoir ajouter une seconde instruction. Disons que vous avez un système de connexion qui vérifie un nom d'utilisateur et un mot de passe avec cette simple sélection:

$query="SELECT * FROM users WHERE username='" . $_REQUEST['user'] . "' and (password='".$_REQUEST['pass']."')";
$result=mysql_query($query);

Si vous fournissez peter comme nom d'utilisateur et secret comme mot de passe, la chaîne SQL résultante ressemblera à ceci:

SELECT * FROM users WHERE username='peter' and (password='secret')

Tout va bien. Imaginez maintenant que vous fournissez cette chaîne comme mot de passe:

' OR '1'='1

Alors la chaîne SQL résultante serait ceci:

SELECT * FROM users WHERE username='peter' and (password='' OR '1'='1')

Cela vous permettrait de vous connecter à n'importe quel compte sans connaître le mot de passe. Vous n'avez donc pas besoin de pouvoir utiliser deux instructions pour utiliser l'injection SQL, bien que vous puissiez faire des choses plus destructrices si vous êtes capable de fournir plusieurs instructions.


Disons que le nom a été utilisé dans une variable, $Name . Vous exécutez ensuite cette requête:

INSERT INTO Students VALUES ( '$Name' )

Ce que vous obtenez est:

INSERT INTO Students VALUES ( 'Robert' );  DROP TABLE STUDENTS; --' )

Le -- seulement les commentaires le reste de la ligne.


Dites que vous avez naïvement écrit une méthode de création d'étudiant comme ceci:

void createStudent(String name) {
    database.execute("INSERT INTO students (name) VALUES ('" + name + "')");
}

Et quelqu'un entre le nom Robert'); DROP TABLE STUDENTS; -- Robert'); DROP TABLE STUDENTS; --

Ce qui est exécuté sur la base de données est cette requête:

INSERT INTO students (name) VALUES ('Robert'); DROP TABLE STUDENTS --')

Le point-virgule termine la commande d'insertion et en démarre une autre; le - commente le reste de la ligne. La commande DROP TABLE est exécutée ...

C'est pourquoi les paramètres de liaison sont une bonne chose.


Le '); termine la requête, il ne démarre pas un commentaire. Ensuite, il supprime la table des étudiants et commente le reste de la requête qui devait être exécutée.


Le caractère ' dans SQL est utilisé pour les constantes de chaîne. Dans ce cas, il est utilisé pour terminer la chaîne de caractères et non pour commenter.


Une seule citation est le début et la fin d'une chaîne. Un point-virgule est la fin d'une déclaration. Donc, s'ils faisaient une sélection comme ceci:

Select *
From Students
Where (Name = '<NameGetsInsertedHere>')

Le SQL deviendrait:

Select *
From Students
Where (Name = 'Robert'); DROP TABLE STUDENTS; --')
--             ^-------------------------------^

Sur certains systèmes, le select serait exécuté en premier suivi de l'instruction drop ! Le message est le suivant: NE PAS INTRODUIRE DES VALEURS DANS VOTRE SQL. Au lieu d'utiliser des paramètres!


Voici comment cela fonctionne: Supposons que l'administrateur recherche des enregistrements d'étudiant

Robert'); DROP TABLE STUDENTS; --

Étant donné que le compte d'administrateur a des privilèges élevés, la suppression de la table de ce compte est possible.

Le code pour récupérer le nom d'utilisateur de la requête est

Maintenant, la requête serait quelque chose comme ceci (pour rechercher la table des étudiants)

String query="Select * from student where username='"+student_name+"'";

statement.executeQuery(query); //Rest of the code follows

La requête résultante devient

Select * from student where username='Robert'); DROP TABLE STUDENTS; --

Puisque l'entrée de l'utilisateur n'est pas hygiénique, la requête ci-dessus est manipulée en 2 parties

Select * from student where username='Robert'); 

DROP TABLE STUDENTS; --

Le double tiret (-) commentera simplement la partie restante de la requête.

Ceci est dangereux car il peut annuler l'authentification par mot de passe, le cas échéant

Le premier fera la recherche normale.

Le second laissera tomber l'étudiant de table si le compte a des privilèges suffisants (Généralement le compte d'admin d'école exécutera une telle question et aura les privilèges mentionnés ci-dessus).





sql-injection