SlideShare a Scribd company logo
WEB FOR PENTESTER II ile WEB
UYGULAMA GÜVENLİĞİNE GİRİŞ
Umut ERGİN
umutergincs@gmail.com
Web for pentester II uygulaması www.pentesterlab.com tarafından
geliştirilmiş olan ve çeşitli zafiyetler barındıran bir web uygulamasıdır.
SQL INJECTION
Birinci örneğimizde karşımıza basit bir login sayfası gelmekte. Web
for Pentester I'in çözümünde kullandığımız OR '1'='1' mantığını, sık
kullanılan bir kullanıcı adı(admin) ile denediğimizde başarıyla giriş
yapabilmekteyiz. Dilediğimiz takdirde username kısmında da bir takım
SQL sorguları verilerek benzer sonuçlara erişilip, veritabanı üzerinde
manipülasyonlar yapılabilir.
İkinci örnekte ise aynı payload kullanıldığında başarısız olmaktayız. Ancak
giriş adı olarak admin' gibi bir değer verdiğimizde hata alıyoruz. Buradan da
giriş adının bulunduğu sorgudan sonra bir takım kontrol yapıldığı kanısına
varabiliriz. Bu kontrolleri geçersiz kılmak için LIMIT 1 yazıp sonuna da #
ifadesini koyduğumuzda, sonrasında gelen komutları geçersiz saydırmış
olacağız.
Üçüncü örnekte ise tek tırnak ifadesi engellenmiş. Payload'ımızı
belirlemek için arkada çalışan SQL sorgusunu tahmin etmeye çalışalım.
SELECT * FROM users WHERE username='[kullaniciadi]' AND
password='[parola]'
Teorik olarak, bir tırnaktan kurtulabilmek için, sorguya başka bir tırnak
eklememiz gerek. Ancak  karakterini kullanarak da tırnak içindeki ifadeden
kurtulabilmemiz mümkün. Kullanıcı adı tarafına  yazdığımız takdirde oluşacak
sorgu şu şekilde görünecek.
SELECT * FROM users WHERE username='' AND password=' '
Burada  işareti kendisinden sonra gelen tırnak normal bir karakter gibi
algılanacak ve username parametresi ' AND password= olarak kalacak. Parola
kısmına da or 1=1 # yerleştirerek mantıksal olarak doğru bir cevap
döndürülecek ve bu şekilde başarılı bir şekilde giriş yapmış olacağız.
Dördüncü örnekte karşımızda herhangi bir textbox yok, URL'de
example4/?req=username%3d'hacker' gibi bir ifade görmekteyiz. %3d ifadesi
eşittir anlamına gelmekte. URL'i tekrar gözden geçirelim.
example4/?req=username='hacker'
username=' ifadesi verdiğimizde tüm sorguyu görmekteyiz. Burada da daha
önce kullandığımız ' ' or 1=1 # ifadesini kullanabiliriz.
Beşinci örnekte URL'de Limit ifadesi verilmiş. Limit ifadesini komple
URL'den silerek diğer kullanıcılara da ulaşabiliyoruz.
Altıncı örnekte de aynı şekilde bir zafiyet var. URL'in sonuna union “all
select * from users” ekleyerek ya da direkt olarak group=username ifadesini
silerek diğer kullanıcılara ulaşabilmekteyiz.
Yedinci örnekte URL'de id=1 ifadesini görüyoruz. Tek tırnak ifadesi
ekleyerek durum hakkında biraz daha bilgi sahibi olabiliriz. Gördüğümüz
sorgudan da anlayabiliyoruz ki, altıncı örnekte kullandığımız metotlarla yine
diğer kullanıcılara ulaşabilmekteyiz.
Sekizinci örnekte ise kullanıcı adı ve parola ile üyelik oluşturabilmekteyiz.
İlk aşamada özel karakterler ile kullanıcı adı oluşturabiliyoruz gibi gözükse de,
bu koruma sadece kullanıcı oluşturulurken devreye sokulmuş. Ancak
veritabanından okurken böyle bir koruma bulunmamakta. İlk önce normal bir
kullanıcı oluşturuyoruz ve arkasından da kullanıcı adına payloadımızı
vereceğimiz bir kullanıcı oluşturuyoruz.
ID’si 6 olan kullanıcımıza tıkladığımızda ise, id’si 5 olan kullanıcının
bilgilerini görebilmekteyiz. Başka bir kullanıcıyı görmek istediğimizde de
kullanıcı id’sinin farklı olduğu bir üyelik oluşturmamız yeterli olacaktır.
Dokuzuncu örnekte ise biraz daha özel bir durum var. SQL Injection
zafiyetinden kaçınmak için mysql_real_escape_string() adında bir Javascript
fonksiyonu kullanılmış. Ancak çince karakterlerde bu fonksiyon işe
yaramamakta. Buna göre kullanacağımız payload;
呵' or 1=1 #
CAPTCHA
Bu bölümde dizayn/kodlama kaynaklı hatalar sayesinde captcha baypas
etmeye çalışacağız.
Birinci örnekte captcha alanına rastgele bir değer verip gönderilen isteği
Burp ile incelediğimizde URL’de captcha=girilen_değer olarak gönderildiğini
görüyoruz.
Captcha değerinin doğru olup olmadığı kontrol edilmesi ön planda
tutulmuş ancak Captcha değerinin varlığı kontrol edilmemiş. Verdiğimiz URL’de
captcha parametresini sildiğimiz takdirde doğru değeri girmeden
ilerleyebilmekteyiz.
İkinci örnekte ise cevap aslında sayfanın HTML kodunda saklı. Doğru
değer direkt olarak sayfada saklanıyor.
Üçünü örnek de ikinci örneğe biraz benzer, yine uygulamanın kendisinden
edindiğimiz bilgiler ile captcha’yı baypas edebiliyoruz. Ancak bu sefer captcha
değeri sayfanın kaynak kodunda değil, cookie değerinde saklanmış.
Dördüncü örneği baypas edebilmemiz için bir seferliğine doğru captcha’yı
girmiş olmamız gerek. Giriş yaptıktan sonra bize gelen cevapta bir session id
atanması yapıldığını görüyoruz.
Bu session id ve captcha değerleri işimize yarayacak, bu yüzden bu
değerleri not almamızda fayda var. Not aldıktan sonra tarayıcımızı kapatıp
tekrar captcha örneğine giriş yapıyoruz. Bu durumda doğal olarak karşımıza
farklı bir captcha geliyor.
Yine bir istek gönderip Burp’te inceleyelim.
Şimdi captcha değerine daha önce giriş yapmış olduğumuz ‘watson’
değerini ve session id’ye de ‘watson’ ile girdiğimizde bize atanmış olan session
id değerini verelim.
Beşinci örnekte teknik bir sıkıntı görünmese de, birkaç deneme yaptıktan
sonra kullanılan kelimelerin az ve belirli olduklarını farkedebilmekteyiz. Bu
işlemi otomatize edebilmek için, kelimeleri ve kelimeleri barındıran resimlerin
MD5 hash’lerini kıyaslayarak doğru captcha değerini bulan bir script yazılabilir.
Altıncı örnekte ise tesseract isimli OCR(Optical Character Recognition)
aracını kullanacağız. Debian tabanlı işletim sistemlerinde aşağıdaki komut ile
tesseract’ı yükleyebilirsiniz.
$ sudo apt-get install tesseract-ocr
Aracımızı yükledikten sonra örneğimize geri dönebiliriz. Benim karşıma
gelen captcha şu şekilde;
Karşımıza gelen resmi indiriyoruz ve tesseract ile çalıştırıyoruz.
Dilendiği takdirde bu işlemler ruby veya başka bir dil aracılığı ile bir script
yazılıp otomatize edilebilir.
Yedinci örnekte ise captcha kodunun arkaplanında çizgiler bulunmakta ve
bu çizgiler tesseract’ın karakter tanıması yapmasını zorlaştırmakta. Tesseract
ile okumayı denediğimiz takdirde başarılı bir sonuç alamamaktayız, bunun için
kelimeyi, arkaplandaki çizgilerden arındırabilmemiz gerek. Ben bu işlem için
ruby dilinde bulunan ‘rmagick’ adında bir gem kullanacağım.
require 'rmagick'
image = Magick::Image.read("captcha.png").first
image = image.threshold(5)
image.write("captcha.png")
Threshold değerini artırıp azaltarak arkaplandaki gürültüye göre
ayarlayabilirsiniz. Ben bu örnekte 5 değerini kullanacağım. Captchanın
işlemden önceki hali;
Sonraki hali;
Bu resim üzerinden tesseract’ı kullandığımızda ise;
Sekizinci örnekte de durum benzer, yine arkaplanımızda çizgiler var
ancak bu sefer kelime orta kısmından biraz içeri girmiş gibi görünüyor. Bunun
için de scriptimize ilgili methodu ekleyeceğiz.
image = image.implode(1)
Captcha’mızın ilk hali;
Threshold ve implode değerlerinde yaptığım denemeler sonrası son hali;
Daha da belirginleştirmek için değerleri deneyebilirsiniz, ben bu seferlik
tesseract’ın tanıyabileceği kadarı ile yetindim.
Dokuzuncu örnekte ise captcha HTML içinde bir aritmetik işlem olarak
verilmiş. Sayfayı parse edip aritmetik işlemi yapabilecek ufak bir script yazarak
bu captcha’yı da baypas edebiliyoruz.
AUTHENTICATION
Bu bölümde ise basitten karmaşığa doğru kimlik doğrulama aşamalarında
ortaya çıkabilecek zafiyet örneklerinden bahsedeceğim.
Birinci örneğe girdiğimizde karşımıza bir giriş paneli gelmekte. Oldukça
sık kullanılan bir kullanıcı adı/şifre çifti olan admin/admin ile bu örneği hızlıca
geçebiliyoruz.
İkinci örnekte ise brute force yöntemi kullanarak karakter
karşılaştırmalarında geçen zamana göre parolaya ulaşacağız. Örneğe
girdiğimizde karşımıza bir login ekranı gelmekte ve kullanıcı adının ‘hacker’
olduğunu görmekteyiz, bizdense parolayı bulmamız isteniyor. Dilerseniz bu
işlem için bir script yazabilirsiniz, ben hazırda var olan stabil bir brute force
aracını kullanacağım. Patator isimli bu araca aşağıdaki linkten ulaşabilirsiniz.
https://github.com/lanjelot/patator
Şimdi ilk olarak patator aracını çalıştırıp, paroladaki ilk karakterleri
deneyip, isteklere yapılan dönüşler için geçen zamanları inceleyeceğiz. Bunu
yapmamızın sebebi ise doğru karakteri bulduğumuz takdirde yapılacak olan
ikinci karakter kontrolünün, ilk karakterin yanlış olduğu kontrole göre daha uzun
sürecek olması. Örneğin diyelim ki parolamız ‘ABC’, bu durumda işlem şu
şekilde ilerleyecektir;
• A harfi denenecek, doğru olduğu için bir sonraki karaktere
geçilecek, bu durumda geçen süre iki karakterin kontrolü için
gereken süreye tekabül edecek.
• B harfi denenecek, yanlış olduğu için bir sonraki karakter
denenmeden çıkılacak, bu durumda geçen süre bir karakterin
kontrolü için geçen süreye tekabül edecek.
• C harfi içinde B harfinin aynısı geçerli olacak.
Bu durumda da A harfi için kontrol edilen süre, B ve C harflerinden daha
uzun olacağı için, A harfinin parolamızın ilk karakteri olduğunu anlayabiliriz.
Resimde de görüldüğü üzere P harfi için geçen süre diğerlerinden daha
uzun, bu da bize parolanın ilk harfinin p olduğunu söylüyor. Bu işlemleri devam
ettirerek 200 kodlu bir cevap aldığımız takdirde parolaya ulaşmış oluyoruz.
Üçüncü örnekte user1 olarak giriş yapabiliyoruz, ancak bizden admin
olarak giriş yapılmamız isteniyor. Bu örnek için göndereceğimiz HTTP isteğini
incelememiz gerek, ben bu işlem için Burp Suite aracını kullanacağım.
Burp aracılığı ile isteğe baktığımız zaman, cookie değerinin user1
olduğunu görüyoruz. Kullanıcı adımız da user1 idi, cookie değerimiz de user1.
Bu değeri Repeater modülüne gönderip admin yapmayı deneyelim.
Dördüncü örnekte de benzer bir durum söz konusu, ancak isteği
gözlemlediğimiz zaman cookie değerinin 32 karakterden oluşan bir string
olduğunu görmekteyiz. Buradan da cookie değerinin MD5 algoritması ile
hashlendiğini söyleyebiliriz. Cookie değerini herhangi bir MD5 çözücü
kullanarak açıyoruz.
Cookie değerimizin MD5 ile hashlenmiş user1 olduğunu görmüş olduk.
Yine admin değerini MD5 ile hashleyip cookie değerine verdiğimiz takdirde
başarılı bir şekilde giriş yapmış olacağız.
Beşinci örnekte ilk kullanıcının bize admin olduğu söylenmiş. Alt kısımda
ise bir kayıt ol(register) bölümü bulunmakta. Register kısmında admin kullanıcı
adı ile kayıt olmaya çalıştığımızda bu isimde başka bir kullanıcının olduğu
hatasını almaktayız. Burada önemli olan nokta; kullanıcının varolup olmadığı
kontrolü bir progralama dili ile(Ruby, Python gibi) kontrol edilirken, kullanıcı adı
ve şifre kontrolü MySQL ile yapılmakta. Default MySQL ayarlarında büyük-küçük
harf kontrolü yapılmamakta olduğu için “Admin” isimli bir kullanıcı açıp
kaydedebilmekteyiz. Bu şekilde yeni oluşturduğumuz kullanıcı admin’in yerine
geçip bu aşamadaki kimlik doğrulamayı bypass edebiliyoruz.
Altıncı örnekte ise beşinci örnekteki durumun aynısı bulunmakta, ancak
bu sefer büyük küçük harf kontrolü yapılmış bu yüzden aynı şekilde admin
olarak kayıt olamıyoruz. Çözüm olarak MySQL’in string’leri karşılaştırma
kriterlerine bakmamız gerek. Default olarak MySQL kullanımında boşluklar
gözardı edilir, yani ‘umutergin’ ile ‘ umutergin’ aynı anlama gelmektedir. Bu
özelliği kullanarak beşinci örnekteki gibi bu aşamayı da geçebilmekteyiz.
AUTHORIZATION
Türkçe’ye yetkilendirme olarak geçen authorization kelimesi ile ifade
edilen zafiyetler, uygulamada kullanılan mantığa göre oldukça sık karşılaşılan
zafiyetlerdir. Yetkilendirme zafiyetleri otomatik araçlar ile test edilemediği için,
manuel olarak test edilmesi önemlidir. Framework’ler injection saldırılarına karşı
önlem alabiliyorken, yetkilendirme zafiyetlerine karşı otomatik olarak önlemler
alamamaktadırlar.
Birinci örnekte giriş yaptıktan sonra iki adet dosyayı görebilme yetisi
kazanıyoruz. Ancak logout olduktan sonra bile, dosyanın URL’ini girdiğimizde
yine dosyayı görebilmekteyiz. Bu da demek oluyor ki URL bir kere öğrenildikten
sonra erişim konusunda bir sıkıntı bulunmamakta.
İkinci örnekte ise yine giriş yaptıktan sonra karşımıza iki adet dosya
gelmekte. URL’i incelediğimiz zaman, dosyaların nümerik olarak
sınıflandırıldığını görüyoruz. Gerekli kontroller yapılmadığı için, URL’deki sayıları
değiştirerek, erişmememiz gereken dosyalara erişebilmekteyiz.(user2)
Üçüncü örnekte ikinci örneğin aynısı. Ancak burada direkt olarak erişim
sağlayamıyoruz, bunun yerine biz edit yetkisi verilmiş. Editlerken yine URL’de
yapacağımız değişiklik ile user2’nin dosyalarını okuyabilmekteyiz.
MASS-ASSIGNMENT
Veritabanı ile ilgili işlemler için SQL sorgularının kullanıldığını biliyoruz.
Ancak web uygulamalarında kullanılan sorguların sayısı ve karmaşıklığı zamanla
arttığı için manüel olarak bu sorguların yazılması zaman içinde uzun ve yorucu
bir iş haline geldi. Bu problemi ortadan kaldırmak için ORM(Object Relational
Mapping) gibi teknolojiler kullanılmaya başlandı. Bu teknolojileri, veritabanı ile
nesneye dayalı programlama arasındaki köprüler olarak özetleyebiliriz. Örnek
vermek gerekirse;
$kullanici = User::findById(123);
Yukarıdaki kod sayesinde SQL sorgusu ile uğraşmadan ID’si 123 olan User
bulunup kullanici nesnesine atanabilmekte. Bu durum mem yazım hem de
okuma konusunda kolaylık sağlasa da, güvenlik açısından problemler
oluşturabilmekte.
$kullanici = new User(array('username' => 'Umut',
'password' => 'cis' ));
Burada ise kullanıcı adı Umut ve parolası cis olan yeni bir kullanıcı
oluşturmaktayız. Oluşturduğumuz yeni kullanıcının tüm niteliklerini bu şekilde
girebilmek oldukça kolay. Ancak geliştirici yeni oluşturulacak olan kullanıcının
özelliklerini doğru şekilde korumadığı takdirde aşağıdaki gibi bir kullanıcı
oluşturulup, istenmeyen sonuçlar elde edilebilir.
$kullanici = new User(array('username' => 'Umut',
'password' => 'parola',
'IsAdmin' => true
));
Birinci örnekte karşımıza bir register ekranı gelmekte ve bizden admin
niteliğine sahip olarak bir kullanıcı ile kayıt olmamız istenmiş. Burp ile isteği
yakalayıp inceleyelim.
Burada user kullanıcısına ait username ve password niteliklerini
görebilmekteyiz. Yukarıda bahsettiğimiz diğer admin özelliğini de isteğimize
ekleyelim.
İkinci örnekte ise aynı durum var, ancak bu sefer direkt admin niteliğinde
bir üyelik oluşturulmasına izin verilmemiş. Giriş yaptığımızda ise kullanıcı
bilgilerimizi değiştirmemizi sağlayan “modify your account” kısmında birinci
örnekteki işlemlerin aynılarını yaptığımız da yine admin niteliklerine sahip bir
kullanıcıya sahip olmuş oluyoruz.
Üçüncü örnekte ise user1:pentester giriş bilgileri ile giriş yapabiliyoruz ve
karşımıza Company 1 isimli şirketin bilgileri gelmekte ve bizden Company 2
isimli şirketin bilgilerini görmemiz istenmekte. Bunu yapabilmek için mass-
assignment ile Company 1 bilgilerini değiştirebiliyor olmamız gerek. İlk iki
örnekte yaptığımız gibi company_id adında bir nitelik tanımlayıp, gerekli id
numarasını verdiğimiz takdirde, Company 2’nin bilgilerine ulaşabilmekteyiz.
RANDOMNESS ISSUES
Programlama yaparken, rastgele değer üretmenin birçok yolu
bulunmaktadır. Kullanılan bu yollara bağlı olarak ürettiğiniz değerin rastgeleliği
değişmektedir. Bu değerler üretilirken manuel olarak ‘seed’ değerinin girilmesi
sonucunda güvenlik problemleri ortaya çıkabilmektedir. Seçilen seed değeri,
rastgele seçilecek olan değerin başlangıç noktasını belirleyeceği için, atanılan
değerin rastgeleliği de değişecektir.
Birinci örnekte bir Ruby script’i verilmiş ve bunun yanında ikinci
kullanıcının adı ‘hacker’ parolası ise ‘hjtvse’ olarak verilmiş. Parola ataması için
kullanılan scripti incelediğimizde aynı seed değerinin(0) kullanılması sonucu
ikinci kullanıcının ‘hjtvse’ parolasını aldığını görmekteyiz. Bu scripti çalıştırarak
birinci kullanıcı olan admin’in parolasına ulaşabiliriz.
Gördüğümüz gibi ikinci şifre hacker kullanıcısına ait. Birinci şifreyi admin
kullanıcısı için deneyelim.
İkinci örnekte de rastgele değer üretimi yapılırken anlık zaman
değeri seed olarak alınmış. Ben uygulamayı açtığımda kullanıcı adı ‘hacker’
parolası ise ‘jxrbmt’ olan bir kullanıcı oluşturulmuş.(Bu değer zamana bağlı
olarak üretildiği için siz açtığınızda farklı olabilir.) Bunun için ufak bir brute force
yöntemi kullanabiliriz. Uygulamanın parolayı oluşturduğu zamana dönene kadar
tek tek geri saydırabilirsiniz. Script’in oluşturduğu ikinci değer ‘jxrbmt’ -ya da
sizin aldığınız değer olduğunda- olduğunda, alacağınız ilk değer admin’in
parolası olacaktır. Başka bir yöntem olarak uygulama parolayı oluştururken
Ruby scriptini çalıştırabilirsiniz, birkaç kere denediğiniz zaman hacker
kullanıcısının parolasını görebilirsiniz.
Bu parolayı admin kullanıcısı için deneyelim.
Üçüncü örnekte birinci örneğe çok benzer, tek farkı burada parolanın
biraz daha meşakatli bir şekilde oluşturulması. Birinci örnekteki işlemleri
gerçekleştirerek yine admin kullanıcısının parolasına ulaşabilmekteyiz.
Dördüncü örnekte de benzer bir durum söz konusu ancak s.rand
fonksiyonu yerine rand fonksiyonu kullanıldığı için daha öncesinde kaç defa
random üretecinin çağrıldığını bilmediğimiz için brute-force yapacağız. Bunun
için ufak bi script yazıp, değerlerini kaydettikten sonra hacker kullanıcı adını
parolasını bulmamız, bize admin’in parolasını da verecektir.(Ben çalıştırdığımda
hacker’ın parolası sodurs idi, sizde farklı olacaktır.)
Aradığımız değeri bulmuş olduk. Denediğimizde admin olarak giriş
yapabilmekteyiz.
MONGODB INJECTION
MongoDB NoSQL kullanan bir veritabanı türüdür. Bu saldırıların mantığı
SQL injection ile birebir aynı olmakla beraber tek farkı SQL yerine NoSQL
kullanılmasıdır.
Örnekte bir giriş formuyla karşı karşıyayız. SQL injectionda kullandığımız
1=1 true mantığını burada NoSQL hali ile uygulayabiliriz. SQL injectionda
olduğu gibi ‘ işaretini kullandığımızda bir compile error almaktayız. Buradan da
sorgunun arka planda tamamlanmadığını anlayabiliyoruz. Sorguyu ‘ ile
kapattıktan sonra || 1=1 ile true döndürüp, // ile de sorgunun kalan kısmını
yorum satırı haline getirip, payload’ımızı verebiliriz.

More Related Content

Web for Pentester 2 ile Web Uygulama Güvenligine Giris

  • 1. WEB FOR PENTESTER II ile WEB UYGULAMA GÜVENLİĞİNE GİRİŞ Umut ERGİN umutergincs@gmail.com Web for pentester II uygulaması www.pentesterlab.com tarafından geliştirilmiş olan ve çeşitli zafiyetler barındıran bir web uygulamasıdır.
  • 2. SQL INJECTION Birinci örneğimizde karşımıza basit bir login sayfası gelmekte. Web for Pentester I'in çözümünde kullandığımız OR '1'='1' mantığını, sık kullanılan bir kullanıcı adı(admin) ile denediğimizde başarıyla giriş yapabilmekteyiz. Dilediğimiz takdirde username kısmında da bir takım SQL sorguları verilerek benzer sonuçlara erişilip, veritabanı üzerinde manipülasyonlar yapılabilir. İkinci örnekte ise aynı payload kullanıldığında başarısız olmaktayız. Ancak giriş adı olarak admin' gibi bir değer verdiğimizde hata alıyoruz. Buradan da giriş adının bulunduğu sorgudan sonra bir takım kontrol yapıldığı kanısına varabiliriz. Bu kontrolleri geçersiz kılmak için LIMIT 1 yazıp sonuna da # ifadesini koyduğumuzda, sonrasında gelen komutları geçersiz saydırmış olacağız. Üçüncü örnekte ise tek tırnak ifadesi engellenmiş. Payload'ımızı belirlemek için arkada çalışan SQL sorgusunu tahmin etmeye çalışalım. SELECT * FROM users WHERE username='[kullaniciadi]' AND password='[parola]' Teorik olarak, bir tırnaktan kurtulabilmek için, sorguya başka bir tırnak eklememiz gerek. Ancak karakterini kullanarak da tırnak içindeki ifadeden kurtulabilmemiz mümkün. Kullanıcı adı tarafına yazdığımız takdirde oluşacak sorgu şu şekilde görünecek. SELECT * FROM users WHERE username='' AND password=' ' Burada işareti kendisinden sonra gelen tırnak normal bir karakter gibi algılanacak ve username parametresi ' AND password= olarak kalacak. Parola kısmına da or 1=1 # yerleştirerek mantıksal olarak doğru bir cevap döndürülecek ve bu şekilde başarılı bir şekilde giriş yapmış olacağız.
  • 3. Dördüncü örnekte karşımızda herhangi bir textbox yok, URL'de example4/?req=username%3d'hacker' gibi bir ifade görmekteyiz. %3d ifadesi eşittir anlamına gelmekte. URL'i tekrar gözden geçirelim. example4/?req=username='hacker' username=' ifadesi verdiğimizde tüm sorguyu görmekteyiz. Burada da daha önce kullandığımız ' ' or 1=1 # ifadesini kullanabiliriz. Beşinci örnekte URL'de Limit ifadesi verilmiş. Limit ifadesini komple URL'den silerek diğer kullanıcılara da ulaşabiliyoruz. Altıncı örnekte de aynı şekilde bir zafiyet var. URL'in sonuna union “all select * from users” ekleyerek ya da direkt olarak group=username ifadesini silerek diğer kullanıcılara ulaşabilmekteyiz. Yedinci örnekte URL'de id=1 ifadesini görüyoruz. Tek tırnak ifadesi ekleyerek durum hakkında biraz daha bilgi sahibi olabiliriz. Gördüğümüz sorgudan da anlayabiliyoruz ki, altıncı örnekte kullandığımız metotlarla yine diğer kullanıcılara ulaşabilmekteyiz. Sekizinci örnekte ise kullanıcı adı ve parola ile üyelik oluşturabilmekteyiz. İlk aşamada özel karakterler ile kullanıcı adı oluşturabiliyoruz gibi gözükse de, bu koruma sadece kullanıcı oluşturulurken devreye sokulmuş. Ancak veritabanından okurken böyle bir koruma bulunmamakta. İlk önce normal bir kullanıcı oluşturuyoruz ve arkasından da kullanıcı adına payloadımızı vereceğimiz bir kullanıcı oluşturuyoruz. ID’si 6 olan kullanıcımıza tıkladığımızda ise, id’si 5 olan kullanıcının bilgilerini görebilmekteyiz. Başka bir kullanıcıyı görmek istediğimizde de kullanıcı id’sinin farklı olduğu bir üyelik oluşturmamız yeterli olacaktır.
  • 4. Dokuzuncu örnekte ise biraz daha özel bir durum var. SQL Injection zafiyetinden kaçınmak için mysql_real_escape_string() adında bir Javascript fonksiyonu kullanılmış. Ancak çince karakterlerde bu fonksiyon işe yaramamakta. Buna göre kullanacağımız payload; 呵' or 1=1 # CAPTCHA Bu bölümde dizayn/kodlama kaynaklı hatalar sayesinde captcha baypas etmeye çalışacağız. Birinci örnekte captcha alanına rastgele bir değer verip gönderilen isteği Burp ile incelediğimizde URL’de captcha=girilen_değer olarak gönderildiğini görüyoruz. Captcha değerinin doğru olup olmadığı kontrol edilmesi ön planda tutulmuş ancak Captcha değerinin varlığı kontrol edilmemiş. Verdiğimiz URL’de captcha parametresini sildiğimiz takdirde doğru değeri girmeden ilerleyebilmekteyiz.
  • 5. İkinci örnekte ise cevap aslında sayfanın HTML kodunda saklı. Doğru değer direkt olarak sayfada saklanıyor. Üçünü örnek de ikinci örneğe biraz benzer, yine uygulamanın kendisinden edindiğimiz bilgiler ile captcha’yı baypas edebiliyoruz. Ancak bu sefer captcha değeri sayfanın kaynak kodunda değil, cookie değerinde saklanmış. Dördüncü örneği baypas edebilmemiz için bir seferliğine doğru captcha’yı girmiş olmamız gerek. Giriş yaptıktan sonra bize gelen cevapta bir session id atanması yapıldığını görüyoruz.
  • 6. Bu session id ve captcha değerleri işimize yarayacak, bu yüzden bu değerleri not almamızda fayda var. Not aldıktan sonra tarayıcımızı kapatıp tekrar captcha örneğine giriş yapıyoruz. Bu durumda doğal olarak karşımıza farklı bir captcha geliyor. Yine bir istek gönderip Burp’te inceleyelim. Şimdi captcha değerine daha önce giriş yapmış olduğumuz ‘watson’ değerini ve session id’ye de ‘watson’ ile girdiğimizde bize atanmış olan session id değerini verelim.
  • 7. Beşinci örnekte teknik bir sıkıntı görünmese de, birkaç deneme yaptıktan sonra kullanılan kelimelerin az ve belirli olduklarını farkedebilmekteyiz. Bu işlemi otomatize edebilmek için, kelimeleri ve kelimeleri barındıran resimlerin MD5 hash’lerini kıyaslayarak doğru captcha değerini bulan bir script yazılabilir. Altıncı örnekte ise tesseract isimli OCR(Optical Character Recognition) aracını kullanacağız. Debian tabanlı işletim sistemlerinde aşağıdaki komut ile tesseract’ı yükleyebilirsiniz. $ sudo apt-get install tesseract-ocr Aracımızı yükledikten sonra örneğimize geri dönebiliriz. Benim karşıma gelen captcha şu şekilde; Karşımıza gelen resmi indiriyoruz ve tesseract ile çalıştırıyoruz. Dilendiği takdirde bu işlemler ruby veya başka bir dil aracılığı ile bir script yazılıp otomatize edilebilir. Yedinci örnekte ise captcha kodunun arkaplanında çizgiler bulunmakta ve bu çizgiler tesseract’ın karakter tanıması yapmasını zorlaştırmakta. Tesseract ile okumayı denediğimiz takdirde başarılı bir sonuç alamamaktayız, bunun için kelimeyi, arkaplandaki çizgilerden arındırabilmemiz gerek. Ben bu işlem için ruby dilinde bulunan ‘rmagick’ adında bir gem kullanacağım.
  • 8. require 'rmagick' image = Magick::Image.read("captcha.png").first image = image.threshold(5) image.write("captcha.png") Threshold değerini artırıp azaltarak arkaplandaki gürültüye göre ayarlayabilirsiniz. Ben bu örnekte 5 değerini kullanacağım. Captchanın işlemden önceki hali; Sonraki hali; Bu resim üzerinden tesseract’ı kullandığımızda ise; Sekizinci örnekte de durum benzer, yine arkaplanımızda çizgiler var ancak bu sefer kelime orta kısmından biraz içeri girmiş gibi görünüyor. Bunun için de scriptimize ilgili methodu ekleyeceğiz. image = image.implode(1) Captcha’mızın ilk hali; Threshold ve implode değerlerinde yaptığım denemeler sonrası son hali;
  • 9. Daha da belirginleştirmek için değerleri deneyebilirsiniz, ben bu seferlik tesseract’ın tanıyabileceği kadarı ile yetindim. Dokuzuncu örnekte ise captcha HTML içinde bir aritmetik işlem olarak verilmiş. Sayfayı parse edip aritmetik işlemi yapabilecek ufak bir script yazarak bu captcha’yı da baypas edebiliyoruz. AUTHENTICATION Bu bölümde ise basitten karmaşığa doğru kimlik doğrulama aşamalarında ortaya çıkabilecek zafiyet örneklerinden bahsedeceğim. Birinci örneğe girdiğimizde karşımıza bir giriş paneli gelmekte. Oldukça sık kullanılan bir kullanıcı adı/şifre çifti olan admin/admin ile bu örneği hızlıca geçebiliyoruz. İkinci örnekte ise brute force yöntemi kullanarak karakter karşılaştırmalarında geçen zamana göre parolaya ulaşacağız. Örneğe girdiğimizde karşımıza bir login ekranı gelmekte ve kullanıcı adının ‘hacker’ olduğunu görmekteyiz, bizdense parolayı bulmamız isteniyor. Dilerseniz bu işlem için bir script yazabilirsiniz, ben hazırda var olan stabil bir brute force aracını kullanacağım. Patator isimli bu araca aşağıdaki linkten ulaşabilirsiniz. https://github.com/lanjelot/patator
  • 10. Şimdi ilk olarak patator aracını çalıştırıp, paroladaki ilk karakterleri deneyip, isteklere yapılan dönüşler için geçen zamanları inceleyeceğiz. Bunu yapmamızın sebebi ise doğru karakteri bulduğumuz takdirde yapılacak olan ikinci karakter kontrolünün, ilk karakterin yanlış olduğu kontrole göre daha uzun sürecek olması. Örneğin diyelim ki parolamız ‘ABC’, bu durumda işlem şu şekilde ilerleyecektir; • A harfi denenecek, doğru olduğu için bir sonraki karaktere geçilecek, bu durumda geçen süre iki karakterin kontrolü için gereken süreye tekabül edecek. • B harfi denenecek, yanlış olduğu için bir sonraki karakter denenmeden çıkılacak, bu durumda geçen süre bir karakterin kontrolü için geçen süreye tekabül edecek. • C harfi içinde B harfinin aynısı geçerli olacak. Bu durumda da A harfi için kontrol edilen süre, B ve C harflerinden daha uzun olacağı için, A harfinin parolamızın ilk karakteri olduğunu anlayabiliriz. Resimde de görüldüğü üzere P harfi için geçen süre diğerlerinden daha uzun, bu da bize parolanın ilk harfinin p olduğunu söylüyor. Bu işlemleri devam ettirerek 200 kodlu bir cevap aldığımız takdirde parolaya ulaşmış oluyoruz. Üçüncü örnekte user1 olarak giriş yapabiliyoruz, ancak bizden admin olarak giriş yapılmamız isteniyor. Bu örnek için göndereceğimiz HTTP isteğini incelememiz gerek, ben bu işlem için Burp Suite aracını kullanacağım.
  • 11. Burp aracılığı ile isteğe baktığımız zaman, cookie değerinin user1 olduğunu görüyoruz. Kullanıcı adımız da user1 idi, cookie değerimiz de user1. Bu değeri Repeater modülüne gönderip admin yapmayı deneyelim. Dördüncü örnekte de benzer bir durum söz konusu, ancak isteği gözlemlediğimiz zaman cookie değerinin 32 karakterden oluşan bir string olduğunu görmekteyiz. Buradan da cookie değerinin MD5 algoritması ile hashlendiğini söyleyebiliriz. Cookie değerini herhangi bir MD5 çözücü kullanarak açıyoruz.
  • 12. Cookie değerimizin MD5 ile hashlenmiş user1 olduğunu görmüş olduk. Yine admin değerini MD5 ile hashleyip cookie değerine verdiğimiz takdirde başarılı bir şekilde giriş yapmış olacağız. Beşinci örnekte ilk kullanıcının bize admin olduğu söylenmiş. Alt kısımda ise bir kayıt ol(register) bölümü bulunmakta. Register kısmında admin kullanıcı adı ile kayıt olmaya çalıştığımızda bu isimde başka bir kullanıcının olduğu hatasını almaktayız. Burada önemli olan nokta; kullanıcının varolup olmadığı kontrolü bir progralama dili ile(Ruby, Python gibi) kontrol edilirken, kullanıcı adı ve şifre kontrolü MySQL ile yapılmakta. Default MySQL ayarlarında büyük-küçük harf kontrolü yapılmamakta olduğu için “Admin” isimli bir kullanıcı açıp kaydedebilmekteyiz. Bu şekilde yeni oluşturduğumuz kullanıcı admin’in yerine geçip bu aşamadaki kimlik doğrulamayı bypass edebiliyoruz. Altıncı örnekte ise beşinci örnekteki durumun aynısı bulunmakta, ancak bu sefer büyük küçük harf kontrolü yapılmış bu yüzden aynı şekilde admin olarak kayıt olamıyoruz. Çözüm olarak MySQL’in string’leri karşılaştırma kriterlerine bakmamız gerek. Default olarak MySQL kullanımında boşluklar gözardı edilir, yani ‘umutergin’ ile ‘ umutergin’ aynı anlama gelmektedir. Bu özelliği kullanarak beşinci örnekteki gibi bu aşamayı da geçebilmekteyiz. AUTHORIZATION Türkçe’ye yetkilendirme olarak geçen authorization kelimesi ile ifade edilen zafiyetler, uygulamada kullanılan mantığa göre oldukça sık karşılaşılan zafiyetlerdir. Yetkilendirme zafiyetleri otomatik araçlar ile test edilemediği için, manuel olarak test edilmesi önemlidir. Framework’ler injection saldırılarına karşı önlem alabiliyorken, yetkilendirme zafiyetlerine karşı otomatik olarak önlemler alamamaktadırlar. Birinci örnekte giriş yaptıktan sonra iki adet dosyayı görebilme yetisi kazanıyoruz. Ancak logout olduktan sonra bile, dosyanın URL’ini girdiğimizde yine dosyayı görebilmekteyiz. Bu da demek oluyor ki URL bir kere öğrenildikten sonra erişim konusunda bir sıkıntı bulunmamakta. İkinci örnekte ise yine giriş yaptıktan sonra karşımıza iki adet dosya gelmekte. URL’i incelediğimiz zaman, dosyaların nümerik olarak sın��flandırıldığını görüyoruz. Gerekli kontroller yapılmadığı için, URL’deki sayıları
  • 13. değiştirerek, erişmememiz gereken dosyalara erişebilmekteyiz.(user2) Üçüncü örnekte ikinci örneğin aynısı. Ancak burada direkt olarak erişim sağlayamıyoruz, bunun yerine biz edit yetkisi verilmiş. Editlerken yine URL’de yapacağımız değişiklik ile user2’nin dosyalarını okuyabilmekteyiz. MASS-ASSIGNMENT Veritabanı ile ilgili işlemler için SQL sorgularının kullanıldığını biliyoruz. Ancak web uygulamalarında kullanılan sorguların sayısı ve karmaşıklığı zamanla arttığı için manüel olarak bu sorguların yazılması zaman içinde uzun ve yorucu bir iş haline geldi. Bu problemi ortadan kaldırmak için ORM(Object Relational Mapping) gibi teknolojiler kullanılmaya başlandı. Bu teknolojileri, veritabanı ile nesneye dayalı programlama arasındaki köprüler olarak özetleyebiliriz. Örnek vermek gerekirse; $kullanici = User::findById(123); Yukarıdaki kod sayesinde SQL sorgusu ile uğraşmadan ID’si 123 olan User bulunup kullanici nesnesine atanabilmekte. Bu durum mem yazım hem de okuma konusunda kolaylık sağlasa da, güvenlik açısından problemler oluşturabilmekte. $kullanici = new User(array('username' => 'Umut', 'password' => 'cis' )); Burada ise kullanıcı adı Umut ve parolası cis olan yeni bir kullanıcı oluşturmaktayız. Oluşturduğumuz yeni kullanıcının tüm niteliklerini bu şekilde girebilmek oldukça kolay. Ancak geliştirici yeni oluşturulacak olan kullanıcının özelliklerini doğru şekilde korumadığı takdirde aşağıdaki gibi bir kullanıcı oluşturulup, istenmeyen sonuçlar elde edilebilir. $kullanici = new User(array('username' => 'Umut', 'password' => 'parola', 'IsAdmin' => true )); Birinci örnekte karşımıza bir register ekranı gelmekte ve bizden admin niteliğine sahip olarak bir kullanıcı ile kayıt olmamız istenmiş. Burp ile isteği yakalayıp inceleyelim.
  • 14. Burada user kullanıcısına ait username ve password niteliklerini görebilmekteyiz. Yukarıda bahsettiğimiz diğer admin özelliğini de isteğimize ekleyelim. İkinci örnekte ise aynı durum var, ancak bu sefer direkt admin niteliğinde bir üyelik oluşturulmasına izin verilmemiş. Giriş yaptığımızda ise kullanıcı bilgilerimizi değiştirmemizi sağlayan “modify your account” kısmında birinci örnekteki işlemlerin aynılarını yaptığımız da yine admin niteliklerine sahip bir kullanıcıya sahip olmuş oluyoruz. Üçüncü örnekte ise user1:pentester giriş bilgileri ile giriş yapabiliyoruz ve karşımıza Company 1 isimli şirketin bilgileri gelmekte ve bizden Company 2 isimli şirketin bilgilerini görmemiz istenmekte. Bunu yapabilmek için mass- assignment ile Company 1 bilgilerini değiştirebiliyor olmamız gerek. İlk iki örnekte yaptığımız gibi company_id adında bir nitelik tanımlayıp, gerekli id numarasını verdiğimiz takdirde, Company 2’nin bilgilerine ulaşabilmekteyiz.
  • 15. RANDOMNESS ISSUES Programlama yaparken, rastgele değer üretmenin birçok yolu bulunmaktadır. Kullanılan bu yollara bağlı olarak ürettiğiniz değerin rastgeleliği değişmektedir. Bu değerler üretilirken manuel olarak ‘seed’ değerinin girilmesi sonucunda güvenlik problemleri ortaya çıkabilmektedir. Seçilen seed değeri, rastgele seçilecek olan değerin başlangıç noktasını belirleyeceği için, atanılan değerin rastgeleliği de değişecektir. Birinci örnekte bir Ruby script’i verilmiş ve bunun yanında ikinci kullanıcının adı ‘hacker’ parolası ise ‘hjtvse’ olarak verilmiş. Parola ataması için kullanılan scripti incelediğimizde aynı seed değerinin(0) kullanılması sonucu ikinci kullanıcının ‘hjtvse’ parolasını aldığını görmekteyiz. Bu scripti çalıştırarak birinci kullanıcı olan admin’in parolasına ulaşabiliriz. Gördüğümüz gibi ikinci şifre hacker kullanıcısına ait. Birinci şifreyi admin kullanıcısı için deneyelim.
  • 16. İkinci örnekte de rastgele değer üretimi yapılırken anlık zaman değeri seed olarak alınmış. Ben uygulamayı açtığımda kullanıcı adı ‘hacker’ parolası ise ‘jxrbmt’ olan bir kullanıcı oluşturulmuş.(Bu değer zamana bağlı olarak üretildiği için siz açtığınızda farklı olabilir.) Bunun için ufak bir brute force yöntemi kullanabiliriz. Uygulamanın parolayı oluşturduğu zamana dönene kadar tek tek geri saydırabilirsiniz. Script’in oluşturduğu ikinci değer ‘jxrbmt’ -ya da sizin aldığınız değer olduğunda- olduğunda, alacağınız ilk değer admin’in parolası olacaktır. Başka bir yöntem olarak uygulama parolayı oluştururken Ruby scriptini çalıştırabilirsiniz, birkaç kere denediğiniz zaman hacker kullanıcısının parolasını görebilirsiniz. Bu parolayı admin kullanıcısı için deneyelim.
  • 17. Üçüncü örnekte birinci örneğe çok benzer, tek farkı burada parolanın biraz daha meşakatli bir şekilde oluşturulması. Birinci örnekteki işlemleri gerçekleştirerek yine admin kullanıcısının parolasına ulaşabilmekteyiz. Dördüncü örnekte de benzer bir durum söz konusu ancak s.rand fonksiyonu yerine rand fonksiyonu kullanıldığı için daha öncesinde kaç defa random üretecinin çağrıldığını bilmediğimiz için brute-force yapacağız. Bunun için ufak bi script yazıp, değerlerini kaydettikten sonra hacker kullanıcı adını parolasını bulmamız, bize admin’in parolasını da verecektir.(Ben çalıştırdığımda hacker’ın parolası sodurs idi, sizde farklı olacaktır.) Aradığımız değeri bulmuş olduk. Denediğimizde admin olarak giriş yapabilmekteyiz. MONGODB INJECTION MongoDB NoSQL kullanan bir veritabanı türüdür. Bu saldırıların mantığı SQL injection ile birebir aynı olmakla beraber tek farkı SQL yerine NoSQL kullanılmasıdır.
  • 18. Örnekte bir giriş formuyla karşı karşıyayız. SQL injectionda kullandığımız 1=1 true mantığını burada NoSQL hali ile uygulayabiliriz. SQL injectionda olduğu gibi ‘ işaretini kullandığımızda bir compile error almaktayız. Buradan da sorgunun arka planda tamamlanmadığını anlayabiliyoruz. Sorguyu ‘ ile kapattıktan sonra || 1=1 ile true döndürüp, // ile de sorgunun kalan kısmını yorum satırı haline getirip, payload’ımızı verebiliriz.