SQL Injection (SQLi) — Notlarım
SQL Injection (SQLi) — Notlarım

SQLi , uygulamanın veritabanına attığı SQL sorgusuna kullanıcı girdisi üzerinden müdahale edebilmen demek. Sonuç: Normalde göremeyeceğin veriyi görürsün, hatta bazı durumlarda veri değiştirme/silme ve daha ileri compromise mümkün.
Etkisi (impact)
Yetkisiz veri erişimi: şifreler, kart bilgileri, kişisel veriler
Veri bütünlüğü bozulur: update/delete ile kalıcı hasar
İş sürekliliği riski: DoS’e kadar gidebilir
Kurumsal tokat: itibar kaybı + regülasyon cezaları + uzun süre fark edilmeyen backdoor ihtimali
2) SQLi nasıl tespit edilir? (manual test mantığı)
Her giriş noktasında sistematik denersin. Mantık: “Ben bunu değiştirince uygulamanın cevabı tutarlı şekilde değişiyor mu?”
Klasik test seti
' (tek tırnak) → SQL hatası / anomali var mı?
“base value” ve “different value” üreten SQL syntax → response farkı var mı?
Boolean denemeler: OR 1=1 vs OR 1=2 → sayfa/sonuç farkı?
Time-based payload → yanıt süresi farkı?
OAST payload → dışarıya DNS/HTTP etkileşimi var mı? (Burp Collaborator
Pratik bakış: Hata , içerik farkı , süre farkı , out-of-band sinyal = 4 ana “sensör”.
3) SQLi sorgunun neresinde olur?
Çoğu kişi WHERE’e takılı kalır ama SQLi her yerde çıkabilir:
WHERE (SELECT’in klasiği)
UPDATE (set edilen değerlerde / WHERE’de)
INSERT (insert edilen değerlerde)
SELECT içinde table/column name
ORDER BY içinde
Bu ayrım önemli çünkü her konumda aynı teknik çalışmaz.
4) SQLi örnek “senaryoları” (hangi saldırı tipi neye yarar?)
Hidden data retrieval : normalde gizli olan kayıtları görünür yapmak
Logic subversion : login bypass gibi, uygulama mantığını kırmak
UNION attack : başka tablolardan veri çekmek
Blind SQLi : sonuç/hatayı göremesen bile veri sızdırmak
5) Hidden data retrieval mantığı (WHERE + comment)
Uygulama şunu yapıyor gibi düşün:
category='Gifts' AND released=1 → sadece yayınlananlar
Sen girdiyle sorgunun kalanını “yorum satırı” yapınca released=1 devre dışı kalabiliyor. Bu sınıfın özeti: filtreyi etkisizleştir → daha fazla kayıt dönsün.
OR 1=1 gibi şeyler bazen masum görünür ama aynı input başka sorguya (UPDATE/DELETE) taşınırsa, “kazara kıyamet” çıkabilir.
6) Login bypass (uygulama mantığını sabote etme)
Login sorgusu:
username='wiener' AND password='bluecheese'
Sen password kısmını devre dışı bırakacak şekilde WHERE’i kırarsan, parolasız giriş mümkün olur. Bu sınıfın özeti: AND password kontrolünü etkisizleştir .
7) UNION Attacks (görünür sonuç varsa en tatlısı)
Uygulama response içinde SQL sonucunu gösteriyorsa UNION ile başka SELECT ekleyebilirsin.
UNION için 2 şart
Kolon sayısı aynı olmalı
Kolon tipleri uyumlu olmalı (string ↔ string vs)
(A) Kolon sayısını bulma
İki yöntem:
Yöntem 1: ORDER BY index
ORDER BY 1 , ORDER BY 2 … hata verene kadar artır Hata aldığın yer “limitin aşıldığı” yerdir.
Yöntem 2: UNION SELECT NULL,…
UNION SELECT NULL
UNION SELECT NULL,NULL
UNION SELECT NULL,NULL,NULL … Hata kesildiği anda kolon sayısını yakalamış olursun.
DB notu:
Oracle’da SELECT ... FROM dual gerekir (dual diye built-in tablo var)
MySQL’de -- yorumunun çalışması için çoğu zaman sonra boşluk gerekir; # da comment olabilir.
(B) String taşıyabilen kolonu bulma
Kolonlara sırayla 'a' koyup hangisi patlamıyor bakarsın. Patlamıyorsa o kolon text uyumlu .
İlginç veriyi çekme
Kolon sayısı + text kolon(lar) bulundu mu:
kullanıcı adı/şifre gibi hedef alanları uygun kolonlara map edersin.
Tek kolonda çok değer (concat)
Bazen sayfa sadece 1 kolon basar. O zaman username ~ password gibi birleştirirsin. Concat operatörü DB’ye göre değişir (Oracle: || , bazıları farklı).
8) Database’i “tanıma” (enum mantığı)
SQL standart gibi dursa da DB’ler farklı:
string concat
comment
stacked queries
error mesaj formatları
DB tipi & versiyon
MySQL / MSSQL: @@version
PostgreSQL: version()
Oracle: v$version
Bu sorgulardan hangisi çalışıyor → DB tipi hakkında sinyal verir.
Tablo/kolon listeleme
Çoğu DB: information_schema.tables ve information_schema.columns
Oracle: all_tables ve all_tab_columns
Bu bölümün özeti: Önce şema bilgisi → sonra doğru tablo/kolon adlarıyla veri çekme.
9) SQLi “farklı context’lerde”
SQLi sadece query string değil:
JSON body
XML body
cookie
header
Bazı WAF filtreleri keyword arar; input farklı formatta olunca encoding/escaping ile bypass denenebilir. Örnek: XML entity ile SELECT’in bir harfini encode etmek gibi.
10) Blind SQL Injection (sonuç yoksa “dedektiflik” başlar)
Blind SQLi : response içinde ne query sonucu var, ne de DB hatası.
Burada 3 ana yaklaşım var:
10.1 Conditional responses (boolean-based)
Sayfada “Welcome back” gibi fark edilebilir bir davranış varsa:
Koşul doğruysa mesaj var
Yanlışsa mesaj yok Buradan tek tek karakter çıkarırsın (SUBSTRING/SUBSTR ile).
Özet: True/False → UI farkı → bit bit veri .
10.2 Error-based (conditional error veya verbose errors)
İki alt tip:
A) Conditional error ile çıkarım Koşul doğruysa hata üret (divide-by-zero gibi), yanlışsa üretme. Response farkı = koşulun cevabı.
B) Verbose error ile veri sızdırma Bazı DB’ler hatayı fazla konuşur. CAST ile “string’i int’e çevir” deyip hatada o stringi görebilirsin. Özet: Hata mesajı = data taşıyıcısı .
10.3 Time-based
Uygulama hata yakalıyorsa ve response farkı yoksa:
Koşul doğruysa sleep/delay
yanlışsa normal dön MSSQL örneği: WAITFOR DELAY mantığı.
Özet: Süre farkı = True/False .
10.4 Out-of-band (OAST)
Asenkron query / response farkı yok / time da zor → en güçlü seçenek. DB’den senin kontrolündeki domaine DNS/HTTP isteği attırırsın (Burp Collaborator). Hatta bazı senaryolarda veriyi subdomain içine gömüp direkt exfil yaparsın.
Özet: Uygulama sessizse, DB’yi konuştur: dışarı trafik .
11) Second-order SQLi (stored SQLi)
First-order: Input gelir → aynı request’te sorguya girer → patlar. Second-order: Input güvenli kaydedilir gibi görünür → sonra başka bir yerde tekrar kullanılırken SQL’e gömülür → patlar.
Özet: “Bunu daha önce DB’ye güvenli yazdım” rehaveti = klasik dev hatası.
12) SQLi nasıl önlenir? (gerçek çözüm)
Altın kural: Parameterized queries / Prepared statements
String birleştirme ile query kurma = risk. Prepared statement ile input data olarak gider, query yapısını bozamaz.
Parametrizasyonun yetmediği yerler
Tablo adı / kolon adı / ORDER BY gibi “query yapısı” parçaları parametrelenemez. Burada yaklaşım:
Whitelist (izinli değer listesi)
veya bambaşka bir mantıkla feature’ı tasarlamak
Ek not (çok kritik)
“Bazı input trusted ya” diye concatenation’a geri dönme. Bug’ların çoğu bu “case-by-case güven” saflığından çıkıyor.
Flashcard gibi son tekrar
UNION çalışması için şartlar? → kolon sayısı aynı + tip uyumu
Blind SQLi’de 3 sinyal? → content farkı / error farkı / time farkı / (bonus: OAST)
DB enum niye önemli? → DB tipi + tablo/kolon adları = hedef veriye giden yol
En sağlam önlem? → prepared statements
SQL Injection Checklist
Temel Tanım & Etki
- ☐ SQLi = Kullanıcı girdisiyle SQL sorgusunun yapısını bozabilme
- ☐ Yetkisiz veri okuma (password, PII, kart bilgisi)
- ☐ Veri değiştirme / silme (UPDATE, DELETE)
- ☐ Auth bypass / privilege escalation
- ☐ Uzun vadeli compromise, DoS ihtimali
SQLi Tespit (Manual Test)
Her input için aşağıdakiler denenir:
- ☐ ' → hata / anomali var mı?
- ☐ Base value vs farklı value → response farkı?
- ☐ Boolean test:
- ☐ OR 1=1
- ☐ OR 1=2
- ☐ Time-based payload → response süresi farkı?
- ☐ OAST payload → DNS / HTTP etkileşimi?
Sinyaller: Content farkı | Error farkı | Time farkı | Out-of-band trafik
SQLi Nerelerde Olur?
- ☐ SELECT → WHERE
- ☐ UPDATE → SET / WHERE
- ☐ INSERT → VALUES
- ☐ SELECT → table adı / column adı
- ☐ ORDER BY
Klasik SQLi Senaryoları
- ☐ Hidden data retrieval (filtre bypass)
- ☐ Login bypass (password check öldürme)
- ☐ UNION ile başka tablodan veri çekme
- ☐ Blind SQLi (sonuç görünmüyorsa)
Database Enum (Tanıma)
DB Tipi & Versiyon
- ☐ MySQL / MSSQL → @@version
- ☐ PostgreSQL → version()
- ☐ Oracle → v$version
Tablo & Kolonlar
- ☐ information_schema.tables
- ☐ information_schema.columns
- ☐ Oracle için:
- ☐ all_tables
- ☐ all_tab_columns
UNION Attacks (Visible SQLi)
Ön Koşullar
- ☐ Kolon sayısı aynı
- ☐ Kolon tipleri uyumlu
Kolon Sayısı Bulma
- ☐ ORDER BY 1,2,3…
- ☐ UNION SELECT NULL,…
String Kolon Bulma
- ☐ 'a' → hata vermeyen kolon(lar)
Veri Çekme
- ☐ username , password , vs.
- ☐ Tek kolon varsa → concat kullan
DB farkları:
- ☐ Oracle → FROM dual
- ☐ MySQL → -- (boşluk şart) / #
Blind SQL Injection
Boolean-Based
- ☐ True/False → sayfa davranışı farkı
- ☐ SUBSTRING / SUBSTR ile karakter karakter çıkarım
Error-Based
- ☐ Conditional error (CASE + divide by zero)
- ☐ Verbose error (CAST ile data’yı hataya taşıma)
Time-Based
- ☐ Koşul true → delay
- ☐ Süre farkından true/false çıkarımı
Out-of-Band (OAST)
- ☐ DNS / HTTP isteği tetikleniyor mu?
- ☐ Veriyi subdomain içine gömerek exfil mümkün mü?
Second-Order SQLi
- ☐ Input önce DB’ye güvenli gibi yazılıyor
- ☐ Sonra başka request’te query’ye girip patlıyor
Farklı Context’ler
- ☐ Query string
- ☐ POST body
- ☐ JSON
- ☐ XML (encoding ile bypass)
- ☐ Cookie / Header
Önleme (Savunma Tarafı)
- ☐ Prepared / Parameterized Queries
- ☐ String concatenation YOK
- ☐ Table / column / ORDER BY için:
- ☐ Whitelist
- ☐ Alternatif iş mantığı
- ☐ “Trusted input” varsayımı YAPMA
Son Kontrol Soruları
- ☐ UNION neden çalışmıyor? → kolon sayısı / tip uyumu
- ☐ Blind SQLi’de veri nasıl çıkar? → boolean / error / time / OAST
- ☐ En sağlam önlem ne? → Prepared statements







