Web Uygulama Güvenliği: SQL Injection

Konu için gereken bilgiler: Temel HTTP Bilgisi Temel PHP ve SQL Bilgisi SQL Injection: Veritabanında kendi SQL sorgularımızı çalıştırabileceğimiz güvenlik açığıdır. Tespiti...

Konu için gereken bilgiler:
Temel HTTP Bilgisi
Temel PHP ve SQL Bilgisi

SQL Injection:
Veritabanında kendi SQL sorgularımızı çalıştırabileceğimiz güvenlik açığıdır.

Tespiti:
İlk önce veri alışverişi veritabanında mı gerçekleşiyor tespit etmek lazım.
Örneğin bizden bir veri bekleniyor ve bu veri veritabanında sorgulanıp bize çıktısı veriliyor.

Normal bir değer girelim
sqli1.pngsqli1.png

Başka bir değer girelim
sqli2.pngsqli2.png

Evet verdiğimiz veriye göre arkaplanda veri veritabanında sorgulanıp bize çıktısı verildiği anlaşılıyor.
Akıllardaki soru şu neden 1 ve 2 verdik her zaman bunu mu vermeliyiz. Hayır aslında 1-2 id değeri olduğundan verdik.
Dediğim gibi temel SQL bilginizin olması gerekiyor konuyu anlamanız için.

Eğer tek tırnak atalım bakalım sözdizimini bozabilecek miyiz?
Çünkü genelde veriler ya direkt olarak alınır yada 2 tek tırnak içerisinde alınır
Eğer biz kendimiz bir tek tırnak daha yollarsak ya direkt veritabanı yazılımı hata verir ya da sondaki kodlarda bulunan tek tırnak sözdizimini (syntax) bozacağından arkaplandaki veritabanı yazılımı hata verecektir.

sqli3.pngsqli3.png

Evet hatamızı aldık şimdi sayfanın statik kodlarını inceleyelim ve biraz konuşalım.

sqli4.pngsqli4.png

Gördüğünüz gibi GET ile gelen id anahtar değerindeki veriyi olduğu gibi sorguya aktarıyor ve sorguyu çalıştırıyor.

SELECT firstname, lastname FROM users WHERE user_id='Verdiğimiz değer buraya geliyor'

Eğer biz 1 verirsek şöyle bir sorgu çalışacak
SELECT firstname, lastname FROM users WHERE user_id='1'

Eğer 2 verirsek başka bir veri getirelecek
SELECT firstname, lastname FROM users WHERE user_id='2'

Neden veriler değişti çünkü where seçicidir. user_id Anahtarındaki değere göre veriyi users Tablosundan seçer.
Buradaki veri ise firstname ve lastname olmaktadır.

Şimdi biz bir tek tırnak eklersek sorgu şöyle olacaktır.

SELECT firstname, lastname FROM users WHERE user_id='1''

Bakın sonda bir tek tırnak daha oldu bizim verdiğimiz tek tırnak ile user_id değeri kapanıyor ama sondaki tek tırnak bir sözdizimini hatasına yol açıyor ve bu yüzden SQL sözdizimi hatasını görüyoruz.

Şimdi geldik kendi SQL sorgularımızı yazıp sistemi istismar etmeye.
İlk önce mysql_numrows vardı kodlarda bunu hatırlayalım.
Bu fonksiyon ile verilen sorgunun çıktı sayısı kadar değeri verir.

Sonrasında 0'dan başlanarak bu değere kadar bir döngü oluşturuluyor.
Ve sorgudaki her bir çıktının firstname ve username adlı değerini gösteriyor.

Şimdi bu SQL sorgusunu nasıl istismar ederiz. Biraz sql bilginiz varsa where yanına or true gelince bütün değerleri vereceğiniz bilirsiniz.
Ve biz 'x'='x' dersek yani x string'i x string'ine eşit olacağından true dönecektir. Ve or eklediğimizde or true değerini elde ederiz.

Örnek:
SELECT firstname, lastname FROM users WHERE user_id='1' OR 'x'='x'

Buradaki sorgu şu anlama geliyor:
users tablosundan user_id değeri 1 olan ya da true değerleri döndür.
Şimdi bütün değerler true anlamındadır. Bu yüzden bütün değerleri döndürecektir.

sqli5.pngsqli5.png

Farkettiyseniz zararlı SQL sorgumuzda sonunda tek tırnak (') eklemedik çünkü sistem zaten en sona tek tırnak ekliyordu bunu php kodlarında görmüştük.
Eğer en son bir tek tırnak biz eklersek, php kodlarında bulunan sondaki tek tırnak sözdizimi hatasına yol açacaktır.

Eğer php kodlarını bilmeseydik sondaki tek tırnak sistemin eklediğini göremeyecektik.
Normal saldırılarda da php kodlarını başka açıklar yoksa ulaşamayız.
O yüzden sondaki tek tırnağı olup olmayacağını bilemeyiz. Deneyip yanılalarak ta ulaşabiliriz.
Ama biz sondaki tek tırnağı eğer yorum içine alırsak arkadaki veritabanı yazılımı onu yorum olarak okuyacaktır.
Arkadaki veritabanı yazılımına göre farklı yorum satırları oluşturmalıyız biz en yaygın olan MySQL için konuşacağız.
Ve MySQL yazılımında yorum satırı oluşturabilmek için alttaki 2 işaret anlamına gelir.
#
--
Biri hashtag işareti diğer 2 tire yanyana ve boşluktan oluşuyor
Yani aslında şunlar kendinden sonra gelen kodu yorum içine alır ve MySQL onu yorumlamaz yani sorgu olarak görmez ve istediğimiz sorguları daha rahat çalıştırabiliriz.
"#"
"-- "

MySQL yazılımın versiyonlarına göre yorum içine alma değişebilir.
Deneme yanılma yoluyla biz sondaki veriyi kapayabiliriz.

1' or 1=1#
1 integer değeri 1 integer değerine eşit olduğundan true dönecektir ve sorgumuz users tablosundan user_id değeri 1 olan ya da true değerleri döndür olacaktır
Bütün veriler varolduğundan true değerine sahiptir. Yani bütün verileri döndürecektir.

sqli6.pngsqli6.png

Yine hatırlatayım sondaki tırnak sıkıntı çıkarabileceğinden yorum içine almalıyız. Çünkü yorum içine alırsak SQL sorgumuzda işleve sahip olmayacaktır.
Yorum içine almak için de # işaretini kullandık. Burada hangi yorum satırı işaretini kullanacağımızı deneme-yanılmayla öğrenebiliriz.

Evet şimdiye kadar SQL injection temellerini aldık.
Şimdi SQL injection ile kritik verileri nasıl okuyabiliriz buna değinelim. - Burada Arkaplanda yine MySQL veritabanı sistemi kullanıldığı varsayılmıştır çünkü en yaygın olarak bu yazılım kullanılıyor.

"order by" komutu ne işe yarar
Aslında listemenin nasıl yapılması gerektiği işlemine yarar.

SELECT firstname, lastname FROM users WHERE user_id='1' ORDER BY user_id

Dersek user_id değerine göre sıralama gerçekleştirir.
Ama biz burada kolonları tespit etmek için kullanacağız.
Eğer kolonları tespit edersek o kolonlar içine kendi istediğimiz veriyi yerleştirebiliriz bu sayede istediğimiz kritik bilgiye sayfa içerisinde görebiliriz.

SELECT firstname, lastname FROM users WHERE user_id='1' ORDER BY 1

Kolon ismi vermemiz gerek yok ORDER BY için kolonların bir integer türünde değeri var.
Eğer order by 1 dersek ilk kolona göre listeler. 2 dersek 2. kolona göre listeleme gerçekleştirir.
Bu sayede o tablo içerisindeki kolon sayısına ulaşabiliriz.
Neden kolon sayısına ulaşmalıyız? Çünkü eğer kolon sayısına ulaşırsak o kolonların nerede kullanıldığını sayfa içerisinde görürüz.
Ve istediğimiz kolona istediğimiz veriyi yerleştirebiliriz.

1' order by 1#
İlk kolona göre listeler.

1' order by 2#
İkinci kolona göre listeler.

1' order by 3#
Üçüncü kolona göre listelemesi gerekirken 3. kolon bulunmadığından bize bilinmeyen kolon hatasını verdi.

sqli7.pngsqli7.png

Demek ki içinde bulunduğumuz SQL sorgusunun tablosunnda 2 kolon varmış.

Şimdi bu kolonların sayfada nerede kullanıldığını görmemiz gerek.
Çünkü saldırımızın şekli o göreceğimiz kolonların içerisine istediğimiz veriyi koymak ve bu veriyi görmek olacaktır.

Şimdi o kolonları sayfa içerisinde nerede olduğunu görmek için union komutu ile birleştirme select ile seçme işlemi gerçekleştirebiliriz.

1' union select 1,2#

Arkada çalışacak SQL sorgusu şu şekildedir:

SELECT firstname, lastname FROM users WHERE user_id='1' UNION SELECT 1,2#

Biz burada naptık? users Tablosu içerisindeki user_id değeri 1 stringine eşit olan verilerin firstname ve lastname değerini seç anlamına geliyor.
UNION birleştir anlamında eğer UNION dersek 2. bir SQL sorgumuzu ilkiyle birleştirebiliriz.

sqli8.pngsqli8.png

Nasıl yani?
Union birleştir manasındaki SQL sorgulurında kullandığımız bir anahtar kelimedir.
Select ise seç manasında kullanılmaktadır.
Union select dediğimizde önceki komut ile birleştir ve seç ama neyi seçmeliyiz ekrandaki kolonları değil mi çünkü biz onları görmek istiyoruz?
Nasıl order by değer için kolonların bir integer değeri varsa select işlemi için de kolonların bir integer değeri var.
Örneğin ilk kolon 1 değerine sahip sonra gelen ikinci kolon 2 değerine sahip.
Eğer biz burada "union select 1,2" dersek önceki komut ile 1,2 seç birleştir. Burada 1,2 kolon değerleri olduğundan sayfada kolonların kullanıldığı yerleri göreceğiz.
Normalde 1,2 demesek 3,3 desek te olur neden çünkü kolon sayısı zaten 2 ve virgül ile ayırıyoruz onları aslında virgül ile ayrılan kısım önemli.

Karışık geldiyse lütfen SQL öğrenin.
Union select 1,2# diyerek bütün kolonların sayfa içinde nerede kullanıldığını görebiliyoruz.
Bazı kolonlar sayfada görükmeyebilirz çünkü onların sayfada kullanılsa bile çıktısı kullanıcıya gösterilmiyor.
Bunu nereden anlayabiliriz. Sayfanın statik kodlarını analiz edersek anlarız. Ama bizim bu örneğimizde iki kolonun da sayfa içerisinde çıktısı mevcut.

Şimdi sayfada istediğimiz kolonları görebiliyoruz şimdi bu kolonların içerisine istediğimiz veriyi sokmalıyız.
İlk önce deneme amaçlı database() fonksiyonunu kullanarak içinde bulunduğumuz veritabanı ismini öğreniyoruz.

sqli9.pngsqli9.png

Evet işlemler başarılı. Sıra geldi istediğimiz veriyi seçmeye.
İstediğimiz veriyi seçmek için verinin hangi tabloda bulunduğunu öğrenmeliyiz.
Ama nasıl öğreneceğiz. Birkaç yol var ama ben en işlevselini göstereceğim.

MySQL içerisinde varsayılan olarak gelen bir veritabanı var ismi de information_schema bu veritabanı içerisinde MySQL yazılımındaki bütün veritabanlarının tablo isimleri kolon isimleri bulunmaktadır.
Biz eğer bu information_schema veritabanına erişirsek oradan içinde bulunduğumuz veritabanının tablolalarını kolonlarını çekebiliriz.

1' union selec 1,2#
Dediğimizde 2 mesela 2.kolonu ifade etmekteydi.
Eğer 2. kolona istediğimiz veriyi yerleştirirsek onu görebiliriz.

1' UNION SELECT 1,table_name FROM information_schema.tables WHERE table_name=database()#

Bu sorgu önceki sorgu ile information_schema içerisindeki tables tablosundaki table_schema değeri içinde bulunduğumuz veritabanına eşit olanları seç anlamına geliyor.

Bu sayede içinde bulunduğumu veritabanının tablolarını information_schema veritabanından çekiyoruz.

sqli10.pngsqli10.png

Tablo isimlerinden users olanı seçiyorum çünkü kullanıcılara ait kritik bilgiler içerdiğini düşünüyorum.
Sonra bu tablonun kolonlarını görmek için de:
Sorguyu biraz değiştirerek kolon isimlerini çekiyorum.

sqli11.png

Bunlar biraz ezbere komutlar çünkü information_schema veritabanı bilmeliyiz içindeki tabloların nerede bulunduğunu kolonların nerede bulunduğunu öğrenmek için bilmeliyiz.

Gerekli tablonun gerekli kolonlarının isimlerini öğrenince geriye kalan tek işlem verileri çekmek onun için de union select ile 2. sorgumu çalıştırabilirim.

sqli12.png

Bonus: Eğer MySQL içerisinde bulunduğumuz kullanıcının dosya okuma yetkisi varsa /etc/passwd gibi kritik dosyaları okuyabiliriz.

sqli13.png

Teşekkürler :)
 
131,899Konular
3,272,391Mesajlar
316,498Kullanıcılar
tvxdevSon Üye
Üst Alt