“Hangi yazılım dilini öğrenmeliyim?” sorusuyla sıkça
karşılaşıyorum ve genelde verdiğim cevap “tabii ki Python” oluyor. Kullanım
kolaylığının yanında Özellikle web uygulaması sızma testlerinde uygulama ile
aramızdaki iletişimi tam olarak anlamak önemlidir. Parolanın açık halde
gönderilmesi, gizli parametreler veya sunucudan gelecek cevabın içerisinde
bulabilecek ipuçları güvenlik açısından sorun oluşturabilecek noktaların tespit
etmesini kolaylaştırır.
Bu amaçla Burp Suite gibi bir Proxy kullanılabilir. Ancak,
özellikle karmaşık veya çok parametreli uygulamalarda sunucuya hızlıca bir
istek gönderip dönen cevabı görmek isteyebiliriz. Bunun için kolay kullanılabilecek
bir Python kütüphanesi olan Requests işimize yarayabilir.
Python Requests Nedir?
Python ile HTTP istekleri göndermeye yarayan Requests
kütüphanesi başlık (header), form veya benzeri parametrelerin “insanın
okuyabileceği şekilde” ayarlanmasını ve sunucudan gelen cevapların
görüntülenebilmesine imkân verir.
pip install requests ile kurulabilen kütüphane daha sone
“import requests” ile her türlü Python
uygulması içerisinde kullanılabilir.
Python Requests Nasıl Kullanılır?
Meslek icabı bu yazı elbette bir “Python öğreniyoruz” yazısı
değildir, böyle bir beklentiniz varsa internette bulunan sayısız kaynağa
bakmanız daha doğru olacaktır. Aşağıdakiler uygulama testleri sırasında
faydalandığım kullanım şekillerinden bazılarıdır.
Cevapların önemi
Uygulama sunucusunun çeşitli isteklere nasıl yanıtlar
döndüğünü görmek uygulamanın çalışma mantığı ve sunucu konfigürasyonu hakkında
bilgi edinmemizi sağlayabilir. Bu nedenle farklı türde talepler gönderip
yanıtları incelemek gerekir. Sunucu ve istemci arasındaki iletişimi düzenleyen
HTTP gönderdiği her cevabı bir durum kodu (HTTP Status Code) ekler. Bu kodların
amacı karşıdaki sisteme isteğinin akıbeti hakkında bilgi vermektedir.
HTTP bağlantıları kısaca aşağıdaki gibi yapılır (DNS
talepler ve 3’lü el sıkışma gibi detaylar burada gösterilmemiştir).
HTTP isteklerine ve sunucu cevaplarına ayrıntılı olarak
bakacak olursak aşağıdaki bilgileri içerdiklerini görebiliriz;
HTTP isteği:
HTTP isteğinde talep edilen alanadı (www.alperbasaran.com), talebi yapan
tarayıcının bilgileri, sunucudan hangi türde cevapları kabul edebileceği, vb.
bazı bilgiler görülebilir.
HTTP cevabı:
Sunucundan gelen cevapta çerez bilgileri, vb. görülebilir.
İlk satırın sonunda yer alan “200 OK” ise sunucunun gönderilen isteğe nasıl
yanıt verdiğini belirtir.
HTTP Durum Kodları Nedir?
5 kategoride dönebilecek HTTP durum kodları aşağıdaki ana
başlıklara göre sınıflandırılabilir;
100’lü kodlar: Bilgilendirme amaçlı gönderilen yanıtlardır. Örn: “100 Continue”
200’lü kodlar: Sunucunun isteği başarıyla yanıtladığını gösterir. Örn: “200 OK”
300’lü kodlar: İsteğin başka bir adrese yönlendirildiğini gösterir. Örn: “301 Moved Permanently”
400’lü kodlar: İstekte bir hata olduğunu gösterir. Örn: “404 Not Found”
500’lü kodlar: Sunucu kaynaklı bir hata olduğunu gösterir. Örn: “503 Service Unavailable”
Gelen HTTP durum kodları tarayıcının yeni bir istekte
bulunmasını veya hatanın kullanıcıya gösterilmesini sağlayabilir.
Hata kodları uygulama testleri sırasında bizlere birkaç
konuda bilgi verebilir:
Uygulamanın kullandığı sayfalar, dizinler veya altdizinler: Örn. Geçerli bir sayfa için yapılan talep “200 OK” yanıtını dönerken olmayan bir sayfa “404 Not Found” yanıtı verecektir.
Uygulamanın çalışma mantığı: Örn. Giriş işlemi sonrası sunucu “302 Found” kodu ile kullanıcıyı yönlendirebilir.
Uygulamanın nasıl yapılandırıldığı: Örn. Talep edilen adrese eklenen özel karaktere “500 Internal Server Error” yanıtının alınması sunucunun kurulumu sırasında bazı güvenlik ayarlarının devreye alınmadığını gösterebilir.
Uygulamayı test ederken çok sayıda talep göndermek ve
sunucunun gönderdiği HTTP durum kodlarını görmek süreci hızlandırabilir.
Python Requests ile İstek Göndermek
Kullanımı oldukça kolay olan Python Requests ile istekler
aşağıdaki gibi gönderilebilir;
requests.get('http://www.alperbasaran.com/')
Görüldüğü gibi var olan bir sayfaya yapılan bir isteğin
sonucu “<Response [200]>” olarak görülürken var olmayan sayfaya yapılan
istek “<Response [404]>” olarak yanıtlanmıştır.
HTTP cevaplarını Kullanmak
Sunucunun gönderdiği yanıtları daha etkin biçimde
kullanabilmek için bir değişken tanımlamak işimizi kolaylaştırabilir. cevap =
requests.get('http://www.alperbasaran.com/') ile sunucunun gönderdiği cevabı
ekranda göstermek yerine değişken olarak saklayabilirsiniz. Aşağıdaki örnekte
olduğu gibi sadece dönen HTTP kodlarını toparlayıp hangi sayfaların var
olduğunu gösteren kısa bir betik geliştirilebilir.
Sunucudan gelen yanıt bir kere “cevap” olarak kaydedildikten
sonra kolaylıkla işlenebilir.
Bu arada cevapta gelen HTTP durum kodunun en son gelen durum
kodu olduğunu unutmamakta fayda var. Aşağıdaki örneğe bakacak olursak
http://alperbasaran.com için gönderilen isteğe “200 OK” yanıtı alındığı
görülebilir. Ancak cevap.history bize arada bir “301 Moved Permanently” cevabı olduğunu
gösteriyor.
Bu durumda http://www.alperbasaran.com
adresine bir yönlendirme yapılmış ve yönlendirmenin sonucunda yapılan ikinci
istek “200 OK” cevabı almıştır.
Yönlendirmeleri takip etmek isteyip istemediğinizi
belirtebilirsiniz. Bu durumda gönderilen isteğe “allow_redirects” parametresini
ekleyerek Python Requests’in nasıl davranacağını belirleyebilirsiniz. Aşağıda
görüldüğü gibi sunucudan gelen yönlendirme kodu takip edilmezse istek bir 301
koduyla sonlanmaktadır.
HTTP Header Bilgisi
Cevap.headers ile HTTP yanıtının header (başlık) bilgisi
görüntülenebilir.
Özellikle HTTP başlığında bir güvenlik cihazı tespit edilen
uygulamalarda hangi sayfa veya alanadlarının bu sistem tarafından korunmadığını
tespit etmekte faydalı olacaktır. Çok sayıda istek gönderecek bir döngü
yazıldığında isteğin gönderildiği adresi takip etmeyi unutmamak gerekir.
Bunun
için cevap.url kullanılabilir.
HTTP İstek Türleri
Python Requests kullanılarak GET ve POST gibi farklı
türlerde istekler gönderilebilir. Kullanım yukarıda anlatılanla benzer.
Farklı türde istek göndermek için;
GET isteği: requests.get('http://alperbasaran.com/')
POST isteği: requests.post('http://alperbasaran.com/')
OPTIONS isteği: requests.options('http://alperbasaran.com/')
DELETE isteği: requests.delete('http://alperbasaran.com/')
PUT isteği: requests.put('http://alperbasaran.com/')
HTTP Cevap Süreleri
Gönderilen isteğe yanıtın ne kadar sürede geldiği bize
uygulamanın nasıl çalıştığı ve “enumeration” denilen bilgi teyidi konularında
bilgi verebilir. Yaygın olarak bulunabilecek bir durum giriş ekranın var olan
bir kullanıcı adı ile var olmayan bir kullanıcı adına verdiği cevapların
sürelerdir. Var olmayan bir kullanıcı giriş denemesinde sistem önce kullanıcı
adının var olup olmadığını teyit edecek, varsa parolanın doğruluğunu kontrol
edecektir. Bunun sonucunda var olmayan bir kullanıcı için yanıtların daha kısa
sürede dönmesi muhtemeldir.
Bunun için “cevap.elapsed.total_seconds()” kullanılabilir.
URL Parametresi Göndermek
Uygulamanın kabul ettiği parametrelerle oynamak uygulamanın
hata vermesine veya davranışlarında değişikliklere neden olabilir. Bu nedenle
fuzzing olarak bilinen teknik zaman kazandırabilir. Fuzzing sırasında uygulamaya
önceden belirlenmiş içerikler göndererek hangilerine nasıl tepki verdiğine
bakılır. Uygulama çok uzun, çok kısa, özel karakter içeren vs. beklemediği bir
girdi aldığında davranışlarında (cevap süresi, cevap header bilgisi, cevap
içeriği, vb.) herhangi bir farklılık varsa ilgili parametre üzerinde
çalışılmaya değer gibi görünüyor.
Örnek sayfayı ele aldığımızda arama için bir parametre
girildiğinde adres çubuğunda da bu görülebilmektedir. “test” kelimesi için bir
arama yaptığımızda adres çubuğunda “http://www.alperbasaran.com/search?q=test” görülebilmektedir.
Bu durumda “q” arama parametresi olarak karşımıza
çıkmaktadır. Requests kütüphanesi ile bunun gibi parametreleri içeren
istekler de gönderilebilmektedir. Bunun için istek “requests.get('http://www.alperbasaran.com/search',params=b'q=siber',)”
şeklinde gönderilebilir.
Çerez Bilgilerini İşlemek
Özellikle uygulamaların giriş
yapılarak erişilen bölümlerine istek gönderebilmek için çerez bilgisine ihtiyaç
vardır. Bunun yanında uygulamanın farklı çerezlere nasıl tepki verdiğini
anlamak bazı kullanıcı giriş mekanizmalarının atlatılmasını veya gizli
işlevlerin tespit edilmesine imkân verebilir.
Aşağıdaki görüldüğü gibi DVWA
(Damn Vulnerable Web Application) uygulamasının komut çalıştırma zafiyetini barındıran
sayfasına istek göndermeyi denediğimizde, uygulamaya giriş yapmadığımız için,
302 durum koduyla bizi giriş sayfasına yönlendiriyor.
İsteğe çerez değerlerini
eklediğimizde ise sayfaya ulaşılabildiği görülmektedir.
İster özel olarak geliştirilmiş EBYS, HBYS veya ERP
uygulamalarını test edin, ister yaygın olarak kullanılan Wordpress veya Joomla
gibi platformları özel istekler gönderip uygulamadan gelen yanıtları
işleyebilmek gerekir. Requests kütüphanesi, kolay kullanımı ve iyi performansıyla
işinizi kolaylaştıracaktır.