Bu çalışmada, SQL injection’ın (SQLi) ne olduğuna bakacağız. Bazı yaygın örnekleri PortSwigger‘ın LAB’larını kullanarak deneyeceğiz.
SQL Injection Zafiyeti Nedir?
SQL injection (SQLi), bir uygulamanın veritabanı ile iletişimi esnasında birtakım teknikleri kullanarak sorgulara müdahale etme işlemidir. Son kullanıcının görmemesi gereken, bilmemesi gereken ya da erişmemesi gereken bilgilere erişme durumudur.
SQL Injection Zafiyetinin Etkileri Nedir?
Eğer başarılı bir SQL Injection saldırısı gerçekleştirilirse veritabınındaki bilgiler silinebilir, değiştirilebilir yada görüntülenen bilgiler illegal bir şekilde yayılabilir, satılabilir! Erişilen bilgiler parolalar, kredi kartı bilgileri veya kişisel kullanıcı bilgileri gibi hassas veriler olabilir. Kalıcı bağlantılar olarak adlandırdığımız backdoor yani arka kapılar bırakılabilir. Bu da uzun vade de bilgi ya da verinin sürekli çalınmasına neden olur.
SQL Injection Örnekleri
Yaygın olarak kullanılan bazı teknikler;
- Gizli Verileri Çekme: Ek sorgular göndererek daha fazlasına erişmek.
- Uygulama Mantığını Alt Üst Etme: Uygulama mantığına müdahale edecek sorgular göndermek.
- UNION Saldırıları: Farklı veritabanı tablolarından veri çekmek.
- Kullanılan Veritabanını İnceleme: Veritabanı versiyonu ve bu versiyon üzerindeki zafiyetleri araştırmak.
- Blind SQL Injection: Gönderilen SQL sorgularında tablolar ya da kolonlar direkt olarak görüntülenmez, kör atış dediğimiz saldırı tekniğidir. Bu saldırı türünde veritabanında olan sütunları direkt olarak tarayıcıda görüntüleyemeyiz.
WHERE ile Gizli Verileri Çekme
SQL Injection zafiyetini anlamak için SQL sorgularının nasıl yazıldığını anlamalıyız. Bir veritabanında nasıl sorgu yapılır, tablolar nasıl çekilir vb. sorgulama işlemlerine hakim olmak gerekir.
SQL sorgusu yaparken SELECT, DELETE, UPDATE ve INSERT gibi değerler kullanılır. İsimlerden anlayacağımız gibi SELECT bir tablodaki değeri çekmeye, DELETE belirtilen tablo verisini silmeye, UPDATE değiştirmeye INSERT ise yeni bir veri yüklemeye olanak tanır. Bunun yanı sıra WHERE değeri ile de tablodaki hangi sütundan veri çekilecek ise o belirtilir ve sorgular istenilen veriye/bilgiye göre şekillendirilir.
Eğer uygulama üzerinde bir açık var ise WHERE ile istediğiniz değeri çekebilirsiniz.
Elbette uygulamayı öncelikle inceliyoruz. Bazı kategorilerin olduğunu ve uygulamadaki ürünlerin bu kategorileri yerleştirildiğini görüyoruz. Bir uygulamada SQLi zafiyetinin olup olmadığını anlamanın en hızlı yolu tek tırnak ( ‘ ) işaretini kullanmaktır. URL yazan “category=” ifadesinden sonra tek tırnak işareti ile test ediyoruz.
Burada arka planda veritabanına aşağıdaki gibi bir sorgu gidecektir.
SELECT * FROM products WHERE category = ''' AND released = 1
Artk uygulamada bir zafiyet olduğunun farkındayız. Burp Suite ile araya girerek istediğimiz sorguları gönderebiliriz. İstersek URL üzerinden de istek gönderebiliriz. category’den sonra ‘+OR+1=1– değerini yazıyoruz ve aşağıdaki ekran görüntüsünü alabiliyoruz. Yeni ekran görüntümüzde değer çekebildiğimizi görüyoruz. Bu aşamadan sonra daha da ileri gidilip tablo isimleri, bu tablolardaki değerler rahatlıkla çekilebilir.
SQL Injection İle Login Bypass Etme
Eğer uygulamada SQLi zafiyeti buluyor ise kullanıcı adı ya da parolalar atlatılabilir. Yine bu işlemi tarayıcı üzerinden deneyebileceğimiz gibi Burp Suite ile de denebiliriz.
Uygulama üzerindeki login ekranındaki textboxlara “or”, “=”, “1” vb değerler ile deneme yaparak veritabanında yaptığımız sorguya eşleşme bulabilirsek içeri girebiliriz. Öncesinde SQLi zafiyeti var mı kontrol etmek için yine tek tırnak ( ‘ ) işaretinden faydalanacağız.
Kullanıcı adına tek tırnak parolaya ise admin yazarak bir hata ekranı aldık ve bu hata ekranı sunucu hatası olarak döndüğü için SQLi zafiyetinin olduğunu söyleyebiliriz. Gerçek kullanıcı adını bulmak için deneme yanılma yöntemi ile sorgular göndermeliyiz.
Birkaç denemeden sonra administrator’– değeri ile giriş yapabildiğimizi görüyoruz.
Eğer uygulama üzerinde bir açık var ise WHERE ile istediğiniz değeri çekebilirsiniz.
UNION Attack ile Sütun Sayısı Döndürme
Veritabanları tablolardan, tablolarda bildiğiniz üzere sütun ve satırlardan oluşmaktadır. Excel tabloları gibi düşünebiliriz. Biraz T-SQL dediğimiz sorgu yöntemlerini bilmeliyiz. Uygulamada gezerken edindiğimiz kategori bilgilerinden yola çıkarak bu kategorilerde kaç tane sütun var bunu tespit edebiliriz. Şu ana kadar yaptığımız tüm başlıklardaki sorguları aslında #SQLMAP aracı ile de yapabiliriz fakat bu örneklerde tamamı ile tarayıcı ve Burp Suite uygulaması odaklı gitmekteyiz. Çeşitlilik iyidir 🙂
Ekrana gelen çıktıların “category” değişkenine göre geldiğini tarayıcıda görebiliyoruz. Yine tek tırnak ( ‘ ) yöntemi ile SQLi olup olmadığını tespit edebiliriz. Daha sonra UNION ile sorgular göndermek gerekecek. Eğer Burp Suite ekranında 200 OK değerini alabiliyorsak bu başarılı bir sorgu olduğu anlamına gelmektedir.
2 farklı yöntem ile sütun sayısını bulacağız bunlardan birisi NULL değerini kullanarak diğeri ise ORDER BY değerini kullanarak gerçekleşecek.
Gifs kategorisindeki ürünleri listelemesi için butona basıyoruz ve Burp Suite üzerinde trafiği sağ click ile Repeater’a gönderiyoruz.
Gifts değişkeninden sonra NULL ifadesini sıra ile deniyoruz. 1 tane yazınca hata aldık. 2 defa yazınca hata aldık ama 3. defa yazınca 200 OK döndü. Buradan çıkaracağımız sonuç ise tablolunun 3 sütunlu olduğudur.
Diğer yöntem ise sütunları sıralama sorgusudur. Bu sorguyu da ORDER BY ile gerçekleştirilir. Bu yöntem de bize tabloda kaç sütun olduğu bilgisini verecektir. Çünkü olmayan bir sütun içerisi sıralanamaz.
Bu işlemlerin aynısını tarayıcı yardımı ile de gerçekleştirebiliriz. URL’e ORDER BY 1 yazarsak 4. de hata alırız.
UNION Attack ile Metin İçeren Sütun Bulma
Bu atak türünde sütun içerisindeki metni çekebilmemiz için öncesinde kaç sütun olduğunu bilmemiz gerekir. Biz bir önceki başlıkta bu işlemi gerçekleştirdik. Bu nedenle direkt sorguya geçebiliriz.
Giden sorguda 1. sütuna ‘bilisimcikiz’ yazdık ve hata aldık. Aynı sorguyu 2. ve 3. sütunlar için de denedik. Sadece 2. sütunda 200 OK aldık. Lab ortamında sütuna girmemiz için Portswigger ‘nRDQjJ’ değerini vermişti.
‘nRDQjJ’ değerini sorguda göndererek labı tamamladık. Aynı işlemi direkt olarak URL’den de yapabilirdik.
UNION Attack ile Tablodan Veri Çekme
Bu labda daha önce kullandığımız teknikleri birleştireceğiz. Labda bize “users” tablo ismi ve “username” “password” sütunları bilgisi verilmiş. Bu bilgileri SQLi açığı olan bir uygulamadan SQLMAP kullanarak rahatlıkla çekebilirdik. Şu an bizde hazır verilmiş durumdadır. Önce kaç sütun var onu belirleyeceğiz daha sonra bu sütunlardaki verilerin türlerini belirleyeceğiz. Daha önce çalışmadığımız bir uygulama olsaydı tek tırnak ( ‘ ) ile SQLi olup olmadığını anlayabilirdik.
Daha önce yaptığımız gibi ORDER BY ile sıralama yapıyoruz ve 1., 2. sütunları sıralarken yani 200 OK dönerken 3. sütun için hata dönüyor. Bu da bize 2 sütunlu bir tablo ile karşı karşıya olduğumuzu gösteriyor. Daha sonra bu sütunların türlerini öğreniyoruz.
'+UNION+select+NULL,NULL-- // bu yukarıda yaptımız ORDER BY işleminin NULL ile yapılmış hali. Çıktısı yine 200 OK şeklindedir.
'+UNION+select+'bilisimcikiz',NULL-- // Burada sütun türünün text olup olmadığını test ediyoruz ve 200 OK alıyoruz. Tarayıcıdaki görüntüsü de aşağıdaki gibidir.
'+UNION+select+NULL,'bilisimcikiz'-- // Burada sütun türünün text olup olmadığını test ediyoruz ve 200 OK alıyoruz. Burp Suite Repetardeki ekran görüntüsü aşağıdaki gibidir.
Bize labda “users” tablosu ve “username”, “password” sütunları olduğu belirtilmişti. Bu bilgiler ile yeniden sorgu göndereceğiz ve elde edeceğimiz “administrator” kullanıcısı ve parolası ile sisteme giriş yaparak labı tamamlayacağız.
'+UNION+select+username,password+from+users-- // username ve password'ü tırnak içerisinde yazmadık çünkü text olarak ekrana basmasını değil de sütun ismi olarak algılayıp ekrana basmasını istiyoruz.
Elde ettiğimiz kullanıcı bilgileri ile sisteme giriş yapıyoruz.
UNION Attack ile Bir Sütuna Birden Fazla Veri Çekme
Uygulama benzer bir uygulama; öncesinde sütun sayısını, daha sonra sütun türlerini bulmamız gerekir. Daha sonra veritabanı versiyonunu SQL injection cheat sheet yöntemleri ile öğrenmiştik. Yine bu yöntemler ile ekrana tek sütunda birden fazla sütun bilgisi çekeceğiz.
Sütun sayısı bulma işlemini tarayıcıda URL üzerinden NULL ya da ORDER BY ile de gerçekleştirebilirdik.
Yine sütun türünü bulma işlemlerini de tarayıcıdan URL’e sorguyu yazarak bulabilirdik. Tüm bu istekler tarayıcıda çıktı olarak görünmektedir. Bizim aldığımız ekran görüntüleri genel itibari ile Burp Suite Repeater tabındaki görüntülerdir.
Text olan sütunu öğrendikten sonra bu sütunda “username” ve “password” sütunlarını birlikte yazdırabiliriz. Şimdi veritabanı versiyonunu öğreneceğiz. Bunun için SELECT’den sonra her veritabanı için özel olan çağırma işlemlerini deneyeceğiz.
Text olan ikinci sütun olduğu için orada versiyon bilgisini istedik ve 200 OK aldık. Tarayıcı ekran görüntüsü de aşağıdaki gibidir.
SQL injection cheat sheet adresinde PostgreSQL veritabanı için 'foo'||'bar'
şeklinde bir birleştirme verilmiş. Bunu kendi isteğimize uyarlıyoruz.
'+UNION+select+NULL,username||'*'||password+from+users-- // || İki tane pipe işaretinin arasına yıldız ( * ) işaretini metin gibi algılasın ve kullanıcı adı ile parolayı ayırsın diye yaptık.
Elde ettiğimiz administrator kullanıcısı parolası ile giriş yapıyor ve labı tamamlıyoruz.
SQLi Açığı Bulunan Oracle Veritabanından Versiyon Öğrenme
Aslına bakarsanız bir önceki örnekte versiyon nasıl çekilir bilgi sahibi olmuştuk. Bu lab ile öğretilmek istenilen Oracle veritabanlarının davranış biçimleridir. Bir önceki örnekte PostgreSQL bir veritabanının versiyonunu öğrenmiştik. Farklılıklar bize saha da çıktıları yorumlama kabiliyeti sunacaktır.
Uygulamayı açtığımızda üst kısımda bize almamız gereken çıktıyı söylüyor. Zafiyet yine category parametresinde bulunuyor. Tek tırnak ( ‘ ) işareti ile anlayabiliyoruz. Diğer uygulamalarda olduğu gibi önce sütun sayısını daha sonra da sütun türlerini bulmalıyız. 2 sütun olduğunu öngörüyoruz. Tarayıcıda öyle görünüyor ve yine tarayıcıdaki verinin text olması 2 sütununda text olduğunu düşünmemize neden oluyor ama yine de emin olmalıyız. Hızlıca sorgularımızı Burp Suite aracı ile gönderiyoruz. Bu sefer ORDER BY ile tespit gerçekleştiriyoruz.
3. sütunu alfabetik sıralamasını beklediğimizde sunucu hatası aldık. Bu bilgi ile 2 sütundan oluştuğunu kesinleştirmiş olduk.
1. ve 2. sütuna text yazarak sorgu göndermemize rağmen sunucu hatası aldık. Eğer veritabanı PostgreSQL olsaydı bize diğer örneklerde olduğu gibi çıktı dönecekti ama dönmedi. Bu durum veritabanının Oracle olma olasılığını yükseltti. Şimdi Oracle veri tabanlarında SELECT işlemi nasıl yapılıyor araştırmalıyız.
Adresteki bilgileri incelerken herkes tarafından görüntülenebilecek tablo isimleri olduğunu görüyoruz. DUAL için sorgu gönderiyoruz.
DUAL tablosunu çağırarak 1. ve 2. sütuna text yazdığımızda 200 OK çıktısını alıyoruz. Bu durum veritabanının Oracle olduğunu doğruluyor. Şimdi sütunlardan birinde versiyon çağırılabilir. Daha önceki örneklerde kullandığımız SQL injection cheat sheet adresinden Oracle veritabanında versiyon nasıl çağrılıyor öğreniyor ve uyguluyoruz.
'+UNION+SELECT+banner,+'bilisimcikiz'+FROM+v$version--
MySQL ve Microsoft Veritabanı ve Versiyon Bilgisini Çekme
Uygulamayı ziyaret ettiğimizde diğer örneklerde olduğu gibi benzer bir uygulama görüyoruz. Sırası ile önce sütun sayısını daha sonra sütun türünü belirliyoruz ve text olan sütunlarda versiyon bilgisini görüntülüyoruz. Yine ORDER BY ile hızlıca sütun sayısını öğreniyoruz.
Sorguyu gönderiyoruz ama daha 1. sütunu istememize rağmen hata alıyoruz. Her tablonun mutlaka ki bir sütun ve satırı bulunur. Bu nedenle sondaki iki orta çizgi yerine diyez (#) ekliyoruz. Bu sorgunun geri kalanını yorumla demenin farklı bir yoludur.
2 tane sütun olduğunu tespit ediyoruz. Şimdi text olanını bulmalıyız. Tarayıcıda baktığımız her ikisi de textmiş gibi görüyor fakat kesin olmalıdır. Burada yine UNION SELECT NULL, bilisimcikiz diye denemeler gerçekleştireceğiz.
'+UNION+select+'bilisimcikiz',+NULL+#
Labda bize MySQL ve Microsoft veritabanları olduğunu söylüyor. Fakat bu bilgiyi bilmediğimizi düşündüğümüzde üzerinde çalıştığımız veritabanı türünü bulmak için bu adresteki SQL injection cheat sheet versiyon sorgulamaların hepsini tek tek denemeliydik. Aşağıdaki sorguyu göndererek versiyon ve tür çıktımızı alarak labı tamamlıyoruz.
'+UNION+select+%40%40version+NULL#
Oracle Dışındaki Veritabanlarında SQLi Saldırıları
Bu örnekte veritabanındaki kullanıcı adı ve parolaların olduğu tablo ve sütun bilgisini çekip daha sonra çektiğimiz administrator kullanıcı bilgisi ile giriş yapmamız beklenmektedir. Uygulamayı ziyaret ediyoruz category değişkenine tek tırnak ( ‘ ) atarak veritabanı hatası alıyoruz ve SQLi olduğunu emin oluyoruz. Burp Suite ile araya girerek veritabanı hakkında ne öğrenebiliriz ona bakacağız ama öncesinde sütun sayısını ve çıktıyı ekrana basacağımız text sütun kısmını bulmamız gerekmektedir. Sırası ile işlemleri gerçekleştiriyoruz.
'ORDER+BY+3--
3. sütunun listelenmesini istediğimizde hata aldık. Buradan tablonun 2 sütundan oluştuğunu tespit etmiş olduk. Daha sonra 1. ve 2. sütunlardan hangileri text girdi alıyor diye kontrol ettik. 2 sütun için de text veri kabul ettiğini gördük.
Text sütunlardan birinde ya da ikinsinde fark etmez SQL injection cheat sheet adresindeki veritabanı versiyon bulma komutlarını deneyeceğiz. Daha sonra veritabanı türünü bulunca direkt olarak tablo ve sütun bilgisi çekme sorgularını göndermeyi deneyeceğiz.
'UNION+SELECT+version(),'bilisimcikiz2'--
PostgreSQL için tablo bilgisi çekiyoruz.
'UNION+SELECT+table_name,'bilisimcikiz'+from+information_schema.tables--
Kullanıcı bilgilerinin “users_jowqzh” tablosunda tutulduğunu görüyoruz. PostgreSQL için “SELECT * FROM information_schema.columns WHERE table_name = ‘TABLE-NAME-HERE'” sorgu ile sütun bilgisi çekebileceğimizi SQL injection cheat sheet adresinde görüyoruz. Tablo ismini bulduk ama “information_schema.columns” isimleri nasıl oluyor anlamak için kısa bir araştırma yapıyoruz.
'UNION+SELECT+column_name,+NULL+from+information_schema.columns+WHERE+table_name%3d+'users_jowqzh'--
Elde ettiğimiz bu sütun bilgileri ile yeni bir sorgu gönderiyoruz. Artık elimizde tablo ve sütun isimleri bulunuyor. Tek yapmamız gereken içeriğin bir sorgu ile çekilmesidir.
- username_tqsccf
- password_iolsok
'UNION+SELECT+username_tqsccf,+password_iolsok+from+users_jowqzh--
Oracle Veritabanlarında SQLi Saldırıları
Uygulamada bulunan zafiyet yardımı ile öncelikle kullanıcı adı ve parolalarının bulunduğu tabloya, daha sonra tablodaki sütunlara ve sütunlardaki bilgilere erişeceğiz. URL’deki category değişkeninden sonra tek tırnak ( ‘ ) atarak sunucu hatası alıyoruz ve SQLi olduğunu doğruluyoruz.
Sütun sayısından sonra sütulardaki verinin türünü belirlemek için istekte bulunuyoruz ama sunucu hatası ile karşılaşıyor. Daha önce Oracle veritabanında kullandığımız yöntemi burada deniyoruz ve DUAL veritabanını çağırıyoruz. Bu tablo Oracle’da tüm kullanıcılar tarafından erişime açık olan bir tabloydu.
'+UNION+select+'bilisimcikiz1',+'bilisimcikiz2'-- // Hata aldığımız sorgu
'+UNION+select+'bilisimcikiz1',+'bilisimcikiz2'+from+dual-- // Çalışan sorgumuz.
Bu aşamadan sonra yapmamız gereken veritabanında bulunan tabloları ekrana getirmek olacaktır. Yukarıdaki sorguda DUAL kullandığımız için veritabanının Oracle olduğunu biliyoruz bu nedenle daha önceki örneklerde olduğu gibi versiyon sorgusu yapmadan direkt tablo çekme sorgusuna geçebiliriz. SQL injection cheat sheet adresinden Oracle veritabanında tabloların hepsinin “SELECT * FROM all_tables” sorgusu ile çekilebildiğini öğrendik. Yıldız ( * ) yerine gelmesini istediğimiz sütun isimlerini yazmalyız ama ne yazacağımızı bilmiyoruz. Bu nedenle küçük bir araştırma yapıyoruz Oracle veritabanlarında sütun isimleri defaulda nasıl yazılır öğreniyoruz.
Sütunlarda tablolanın sahibi (OWNER) ve tablo ismi (TABLE_NAME) gibi sütunlar olduğunu öğreniyoruz. Zaten bize tablo ismi gerekliydi. Kullanıcıların olduğu tabloyu bulup daha sonra kullanıcı adı ve parolalara erişecektik. Sorgumuzu yazıyoruz. Bizim uygulamamızda 2 tane sütün olduğu için birine table_name direğine ise bilisimcikiz ya da NULL yazabiliriz.
'+UNION+select+table_name,+'bilisimcikiz2'+from+all_tables--
Tablo ismini bulduk. (USERS_MGQWPY) Şimdi bu tablonun sütunlarına ihtiyacımız var. Onun için ise “SELECT * FROM all_tab_columns WHERE table_name = ‘TABLE-NAME-HERE'”sorgusunu kullanacağız.
Tespit ettiğimiz tablodaki sütun isimlerini bilmiyoruz bu nedenle kısa bir araştırma ile nasıl yazıldığını öğreniyoruz. Daha sonra sorgumuzu yazıyoruz.
'+UNION+select+column_name,+'bilisimcikiz2'+from+all_tab_columns+WHERE+table_name+%3d+'USERS_MGQWPY'--
'+UNION+select+PASSWORD_LBIELG,+USERNAME_AMLAPI+from+USERS_MGQWPY--
Koşullu Yanıtlar İle Blind SQLi Zafiyeti
Blind SQLi zafiyetinde gönderilen sorgularda hata ekranı göremeyiz. Uygulamanın davranışlarını göz önünde bulundurup ona göre SQL sorguları göndererek deneme yanılma yöntemi ile veritabanına erişim olup olmadığını kontrol ederiz. Yukarıdaki örneklerde olduğu gibi satır ve sütunları öylece sıralama mümkün değildir. Bu nedenle Blind yani kör atış, okunaksız gibi isimlendirilir.
Lab bize uygulamayı ziyaret ettiğimizde “Welcome back!” mesajı aldığımızı söylüyor. Lab’ın açıklamasından cookiede bir zafiyet olduğunu biliyoruz. “Users” diye bir tablo olduğunu ve burada “username”, “password” sütunlarının olduğundan bahsediyor. Bizde Blind SQLi zafiyeti istismar ederek bu tablodaki bilgilere erişim sağlayacağız.
Uygulamayı ziyaret ettiğimizde daha önceki lablarda olan uygulamalara benzer alanları olduğunu görüyoruz. Cookie de zafiyet olduğunu biliyoruz bu nedenle nasıl göründüğüne tarayıcımıza eklediğimiz bir eklenti yardımı ile bakıyoruz. Buna Burp Suite’den de bakabilirdik. Kullandığımız eklentiye buradan ulaşabilirsiniz.
İki tane cookie olduğunu görüyoruz. Bir tanesi “session” diğeri ise “TrackingId”. “TrackingId” isminden anlaşılacağı gibi izleme kimliği oluyor. Yani biz uygulamayı ilk ziyaret ettiğimizde alıyoruz. Henüz sayfada “Welcome back!” diye bir ifade yok fakat sayfalar arasında gezdiğimizde örneğin “My Account” butonuna tıkladığımda “TrackingId’mi” uygulama tanıyacağı için geri geldimi düşünecek ve “Welcome back!” diyecek. Eğer biz cookiede zafiyet olduğunu bilmeseydik veritabanı ile iletişim kurduğunu düşündüğümüz tüm değişkenleri incelemek durumunda kalacaktık.
Uygulamayı ziyaret ettik ve bir izleme kimliği oluştu. Bu izleme kimliği artık veritabanına kayıtlı ve ürün kategorilerinde ya da diğer menülerde gezerken uygulama tarafından eşleştirilip sağ üst köşede “Welcome back!” yazısı olarak gelecek. Güzel! Şimdi biz burada başka neler çağırabiliriz? Ya da bu veritabanına yazıldığını bildiğimiz izleme kimliği ile neleri çağırabiliriz ona bakacağız. Başlangıçta istek gittiğinde aldığımız sonuç ile değiştirdiğimizde giden sonucu inceleyeceğiz.
“TrackingId’nin” veritabanından nasıl sorgu çektiğini hayal etmeliyiz. Etmeliyiz ki benzer sorgular gönderebilelim. Daha önceki lablarda SELECT ile birçok sorgu çağırdık.
select tracking_no from tracking_tabloismi where trackingId = 'X4e2dFXO8H7goMtn' // buradaki tracking_no, tracking_tabloismi tamamen uydurma muhtemelen uygulama üzerinde farklı isimlerle tutuluyordur. Buradaki amacımız nasıl sorgulandığını şekillendirmek.
İzleme kimliğinin sonuna farklı bir harf ekledik ve yeniden istek yaptığımızda bu sefer “Welcome back!” yazısının gelmediğini gördük. Bu da “TrackingId” değişkenindeki zafiyeti doğrulamamızı sağladı.
“TrackingId” değişkenine bazı SQL sorguları yazarak uygulamanın nasıl davrandığını gözlemliyoruz.
'+and+1%3d1-- // trackingId devamına ekliyoruz.
1=1 doğru bir ifade olduğu için ve izleme kimliğide daha önce veritabanında olan bir bilgi olduğu için yeniden geldiğimizi düşündü ve “Welcome back!” yazısını gömüş olduk.
1=0–‘ yazsaydık 1, 0’a eşit olmadığı için veritabanında izleme kimliği olsa bile dönmeyecektir. Çünkü arada “and” ifadesi bulunmaktadır.
Eğer “and” değil de “or” deseydik. Şartlardan birini sağladığımız için yine “Welcome back!” yazısını görecektik. Bu testlerde uygulama üzerinde SQLi çalıştırabildiğimizi desteklemektedir.
SQL sorgusu çalıştabildiğimizi ve eğer veritabanında istediğimiz veri varsa “Welcome back!” yoksa bir şey yazmadığını biliyoruz. Şimdi ise “users” tablosunun olup olmadığını sorgulamalıyız. SQL sorgularda LIMIT kullanma için bakınız.
'+and (select 'a' from users LIMIT 1)='a'-- // Burada users tablosunda içinde a harfi olanları seç ve bir tane döndür gerisini yorumla (--) diyoruz. a yerine tahmini herhangi bir şey yazabilirdiniz. LIMIT kullanmamızın nedeni ise içinde a harfi geçen 100lerce kayıt olabilir. Hepsini okumasına gerek yok.
Buradan çıkaracağımız sonuç “users” diye bir tablonun var oluşudur. Çünkü veritabanı eğer tanıdık bir “TrackingId” gönderirsek ekrana “Welcome back!” yazıyordu.
and (select username from users where username='administrator')='administrator'--
Bu sorgudan “administrator” kullanıcısının veritabanında olduğunu tespit etmiş oluyoruz. Biraz daha anlaşılması için sorguyu yorumlayacağız. “TrackingId’den” sonra yazılı olan pgF1fkWgzjwWPM1D
çerezi zaten var olduğunu bildiğimiz çerezimiz “and” ile diğer bilgiye koşulluyoruz. Parantez içinde yazılan olan mavi çerçeveli kısmı 1
sayısı gibi hayal edelim. kırmızı çerçeveli olan =
ifadesi yeşil çerçeveli olanı da yine 1
ifadesi olarak hayal edelim. Günün sonunda “TrackingId=gerçek_cerez ve 1=1” doğru bir ifade olduğu için ekranda “Welcome back!” yazdığını göreceğiz. Administrator kullanıcısı veritababında “users” tablosunun “username” sütununda yer aldığı için yazdı.
Bir sonraki aşamada parolanın değerini bulmalıyız. Parolayı direkt olarak kullanıcı ismi gibi tahmin etmemiz oldukça zor. Göndereceğiniz sorguda 1. harfi a ise, b ise , c ise diyerek bütün alfabeyi sıra ile şartlı sorgu olarak gönderip parolayı bulabiliriz. Fakat bu eziyet parolanın uzunluğuna göre değişecektir. Bunun yerine önce parolanın uzunluğu bulmak daha mantıklı olacaktır. SQL sorgularda bir şeyin uzunluğu için LENGTH kullanılıyor. Bkz.
' and (select username from users where username='administrator' and LENGTH(password)>19)='administrator'--
Parolanın karakter sayısını öğrenmek için Burp Suitein nimetlerinden yararlanıyoruz.
Reguestimizi sağ click yapıp Intruder’a attık. Önce tüm seçili olanları Clear ettik. Daha sonra sadece sayının döndüğü kısmı seçip Add yaptık ve Payloads sekmesine geçtik. 1’de 50’e kadar 1er 1er değiştir yazdık ve sonra “Start Attack” dedik.
Çıktıyı incelediğimizde LENGTH değerinin değiştiği yer kadar karakter olduğunu gördük. Şimdi karakter sayısını bildiğimiz için brute force yöntemi ile parolayı tespit edeceğiz. Substring() yöntemi kullanacağız ve aşağıdaki sorgu olan Request değerini Intruder’a göndereceğiz.
' and (select substring(password,1,1) from users where username='administrator')='a'--
zr5s12bbjgslg9fa4aj8 //Parola
Kullanıcı adımızı zaten biliyorduk. Elde ettiğimiz parola ile girip yapıp labı tamamlıyoruz.
Koşullu Sorgularda Hataları Yorumlayarak Blind SQLi Sömürme
Bir önceki çalışmamızda uygulama üzerindeki cookie değerine koşullu SQL sorguları yazdık ve normal durumda olması gereken davranış oluyor mu kontrol ettik ve Blind SQLi olduğunu tespit edip istediğimiz sorguları göndererek kullanıcı adı ve parolaya ulaştık.
Fakat her zaman bu kadar rahat olmayabilir. Requestte giden cookie değerini bozmadan çalışabilecek basit bir SQL satırını koşullu halde yazdığımızda sorgumuz çalışmayabilir. Bu durumda daha önceki örneklerde olduğu gibi farklı bir veritabanıyla karşı karşıya olabileceğimizi düşünmeliyiz.
Bu lab bizlere Oracle veritabanlarında CASE WHEN, THEN TO_CHAR ve ROWNUM komutları ile sorgu nasıl yazılır onu gösterecek. Bunların hepsini bilmek zorunda değilsiniz. Ben de test esnasında bilemem fakat arama motorlarında neyi search edeceğinizi bilmek testi başarı ile yapmanızı sağlayacaktır.
Uygulamamızı açıyoruz ve ilk giden paketimizi Burp Suite ile araya girerek durduruyoruz. Denemeler gerçekleştireceğimiz için Repeater’a atıyoruz. Teste “TrackingId” değişkenine tek tırnak ( ‘ ) atarak başlıyoruz.
Açtığımız ve değeri bozan tek tırnağı ( ‘ ) kapadığımızda ise sorgu sorunsuz çalışıyor. Bu durum Blind SQLi açığının olduğu bilgisini kuvvetlendiriyor.
Bu durumda 2 tane tek tırnak ( ‘ ) arasına yazacağım bir SQL komutunun çalışmasını beklemekteyim. SELECT komutu ile boş bir sütun getirmesini istedik.
' (select ' ') '
Yukarıdaki komut basit ve çalışabilir bir komuttur. Fakat uygulama üzerinde çalışmamıştır.
Bu durumda daha önceki pratiklerde öğrendiğimiz gibi veritabanının farklı olma ihtimalini göz önünde bulundurarak denemeler yapıyoruz ve Oracle veritabanları için kullanılan DUAL tablosunu çekmek istiyoruz. Eğer olumlu bir sonuç alamazsak diğer veritabanlarının sorgularını deneyeceğiz
' UNION SELECT ' ' from dual-- '
Veritabanımızın Oracle olduğunu tespit ettik. Gönderdiğimiz sorguların çalıştığını da gördük. Bizim için kullanıcı tablosu ve içerisindeki kullanıcı bilgileri oldukça önemli. Kullanıcı tablolarının genelde ismi “users” olduğundan şimdi böyle bir tablo olup olmadığını tespit edebilecek sorgular göndereceğiz.
' UNION SELECT ' ' from users-- '
“Users” tablosunun varlığını tespit edebildik. Şimdi ise içerisinde Administrator diye bir kullanıcı var mı buna bakmalıyız. Daha önceki çalışmalarımızda olduğu gibi hemen “users” tablosunun “username” değerinde “administrator” kullanıcısı çağırma sorgusu yapacağız.
' UNION SELECT ' ' from users where username='administrator'-- '
Sorguyu gönderdiğimizde 200 OK ekran görüntüsü aldık. Bir de olmayan bir kullanıcı deneyelim bakalım nasıl davranacak.
“Administrator” kullanıcısının var olup olmadığından emin değiliz ama “bilisimcikiz” kullanıcısının olmadığından eminiz 😄 Bir terslik var. Burada labdan kopya çekebiliriz ya da arama motorlarında Oracle veritabanlarında koşullu sorgular nasıl oluyor araştırarak bulabiliriz. Burada biraz matematik zekası iyi olacaktır. Öyleyse, değilse, eşitse, veya ya da ve gibi komut ve operatörlere hakim olmak gerekir. Yine tekrar ediyoruz bunları ezberlemek zorunda değilsiniz. Çünkü test ettiğiniz sistemler sürekli olarak çeşitlilik gösterecektir. Önemli olan analitik düşünebilmek. Oracle veritabanında bir şeyin durumunu öğrenmek için CASE WHEN kullanılmaktadır. Bu if-else gibidir. CASE WHEN 1=1’e THEN yani şu işlevi yap XXX END gibi yazım türleri bulunmaktadır. Bu bilgidir. Bunu yorumlayamayız. Bilmemiz gerekir. Bkz.
' UNION SELECT CASE WHEN (username='administrator') THEN to_char(1/0) ELSE NULL END FROM users--
Eğer “users” tablosundaki “username” sütunu “administrator” değerine eşitse 1’i 0’a böl değilse boş bırak dedik aşağıdaki sunucu hatası ekran görüntüsünü aldık. Bu da “administrator” kullanıcısı var demek.
Koşulu değiştirdiğimizde çıktıya bakalım.
' UNION SELECT CASE WHEN (username='administrator') THEN to_char(1/1) ELSE NULL END FROM users--
Kullanıcımızı tespit ettiğimize göre artık parolası için sorgu gönderebiliriz. Burada daha önce kullandığımız Substring() yöntemi kullanacağız ama Oracle veritabanlarında değişiklik gösterdiğini unutmamak gerekir. Bkz.
Aşağıdaki sorgu ile parolamızın uzunluğunu öğreniyoruz. 20 değerine kadar hep hata aldık. 20’de 200 OK aldık. Eğer parola 20den büyükse 1’i 0’a böl, değilse NULL yani boş döndür. Parola 20’den büyük olmadığı için bölme işlemini yapmadı ve else kısmına atladı NULL 200 OK döndü.
Rakamın 19 olması durumunda parola 19’dan büyük ise 1’i O’a böl, değilse NULL yani boş döndür. Parola zaten 20 karakter ve 19’dan büyük olduğu için bölme işlemini yapıyor ve 1, 0’a bölünmediği için hata ekran görüntüsü alıyoruz. Bu da bize parolamızın 19’dan büyük olduğunu doğruluyor.
' UNION SELECT CASE WHEN LENGTH (password)>20 THEN+to_char(1/0) ELSE NULL END FROM users WHERE username='administrator'--'
' UNION SELECT CASE WHEN SUBSTR (password,1,1)='a' THEN to_char(1/0) ELSE NULL END FROM users WHERE username='administrator'--'
0edvsl74sv0z3h51cxqk // Parola