05 Kasım 2015

Burp Extension Geliştirme - 2

Burp Extension Geliştirme makalemize birinci bölümde kaldığımız yerden devam ediyoruz.

Makalenin can alıcı bölümü aslında aşağıda bulunan bölüm. Bu kod parçası anlamamız gereken temel nokta. Burada kullanılan interface’ler sadece bizim yukarıda belirttiğim ihtiyacımıza özel olarak kullandığımız interface’ler. Farklı ihtiyaçlar için diğer interface’lere de ihtiyaç olacağı açık. Hatta cookie’yi alıp bir HTTP başlığı olarak gönderilecek istek mesajının içine gömmenin dahi farklı yolları olmalı. Bu yüzden bu makaleyi sadece bir ısınma çalışması olarak okuyun, daha sonra interface’lerin sağladığı fonksiyonalitenin anlatıldığı Burp kaynaklarına (https://portswigger.net/burp/extender/api/index.html veya Burp Suite’in içindeki Extender modülü içinde yer alan API bilgileri) danışmanız lazım.





import java.io.PrintWriter;
import java.util.List;

Extension debugging için elimizde klasik runtime’da yazdırma imkanı var. PrintWriter sınıfını bu yüzden import ediyoruz. Kodumuzun ihtiyaç duyduğumuz bölümlerinde belirli veri yapılarını standart output’a yazdırarak işlerin nasıl gittiğini görebiliriz.

java.util.List sınıfını HTTP istek başlıklarını saklamak için kullanacağız.


public class BurpExtender implements IBurpExtender, ISessionHandlingAction
{
    private IExtensionHelpers helpers;
    private PrintWriter stdout;
    private IBurpExtenderCallbacks _callbacks;

BurpExtender sınıfı her extension için implement edilmesi zorunlu bir sınıf. Java dosyamızın ismini de klasik Java zorunluluklarından dolayı bu sınıf ismi ile oluşturmuştuk. Mecburi olarak implement etmek zorunda olduğumuz interface IBurpExtender interface’i. Bunun yanında import edeceğimiz interface’ler elbette extension’ı hangi ihtiyaca yönelik olarak geliştirdiğimize bağlı.

IExtensionHelpers sınıfı adından da anlaşılacağı gibi bir takım yardımcı metodlar barındıran bir sınıf. Biz uygulamamız içinde HTTP isteğinin parse edilmesi, bir HTTP isteğinin sıfırdan oluşturulması ve bir byte array’inin string veri tipine dönüştürülmesi işlemleri için bu sınıfı kullanacağız.

PrintWriter sınıfından oluşturacağımız nesneyi debug amaçlı olarak Extender modülünün Output tab’ına veri yazdırmak için kullanacağız.

Hemen aşağıda göreceğiniz registerExtenderCallback metodu bir extension yüklendiğinde çalıştırılan bir metod. Bu metod IBurpExtenderCallbacks sınıfından bir nesneyi parametre olarak alıyor. Bu nesne üzerinden belirli olaylar oluştuğunda hangi metodların çalıştırılacağını kaydediyoruz. Ayrıca diğer metodlarımızın da kullanacağı temel veri yapılarını da tekrar tekrar oluşturmamak için bu metodun içinde oluşturabiliriz.


@Override
    public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks)
    {
     helpers = callbacks.getHelpers();
        callbacks.setExtensionName("Request Baslik Duzenleme");
        stdout = new PrintWriter(callbacks.getStdout(), true);
        this._callbacks = callbacks;
        callbacks.registerSessionHandlingAction(this); 
    }

registerExtenderCallbacks metodumuzun içinde extension açısından en önemli satırımız oturum ele alma callback fonksiyonu için kendi sınıfımızı kaydettiğimiz “callbacks.registerSessionHandlingAction(this);” satırımız. Kodlara bakılarak doğrudan anlaşılamayacak bir konu, ancak bu satır ile daha alt bölümde bulunan performAction metodunun oturum ele alma durumlarında çalıştırılması gerektiğini belirtiyoruz. Daha sonra göreceğimiz gibi extension’ımızı bir session handling rule’un içine eklediğimizde bu kuralın geçerli olduğu modüllerden her bir yeni istek iletildiğinde performAction metodu çalıştırılacak ve bu metod içinde gerçekleştirdiğimiz işlemler hayata geçecek. Burası aslında Burp extension’ı geliştirmek isteyen bir kişi için an hayati nokta, bu bölümü ne kadar iyi anlarsanız o kadar rahat edersiniz.


@Override
    public String getActionName() { return "Baslik Duzelt"; }

Bu metod ile extension’ımızın session handling editörde hangi isimle görülmesi gerektiğini belirliyoruz.


@Override
    public void performAction(IHttpRequestResponse currentRequest, IHttpRequestResponse[] macroItems) {
        List<ICookie> cookies;          
        String oturumParametresi = "";
        cookies=this._callbacks.getCookieJarContents();
        IRequestInfo rqInfo = helpers.analyzeRequest(currentRequest);
        List<String> headers = rqInfo.getHeaders();

Yukarıda performAction metodunu ISessionHandlingAction interface’inin içinde declare edildiği şekilde tanımlıyoruz. Bu metod çağrıldığında currentRequest parametresi ilgili istek nesnesini içeriyor. macroItems parametresinin ise eğer extension’ımızdan önce çağrılmış olan bir macro varsa onun sonuçlarını içerdiğini Burp dokümantasyonundan anlıyoruz. Açıkçası ben bu parametre ile ilgili bir çalışma yapmadığım için pek de somut bir açıklama yapamayacağım. Burada bizim için önemli olan parametre currentRequest parametresi, diğerini kullanmayacağız.

Amacımızı açıklarken cookie jar içindeki bir cookie’nin değerini istek başlıklarından birisi olarak ileteceğimizi belirtmiştim. Bu yüzden cookie’ler üzerinde çalışmak üzere ICookie cinsinden List veri tipinde bir lokal değişkeni tanımlıyoruz.

Oturum parametresini belli bir cookie’nin değerine ulaşarak belirleyeceğiz. Bu değeri tutmak üzere oturumParametresi lokal değişkenini tanımlıyor ve sıfırlıyoruz.

IBurpExtenderCallbacks interface’inin getCookieJarContents isimli bir metodu var, bu işlemi nasıl yapıyor bir fikrim yok, ama Burp bu API’yi bize sağlıyor. Cookie Jar’ın taze oturum çerezlerinin saklandığı bir alan olduğundan bahsetmiştim. registerExtenderCallbacks metodu içinde extension’a aktarılmış olan callbacks değerini bir global değişkene atamıştık. performAction metodu içinde bu nesneyi kullanarak Cookie Jar’daki tüm cookie’lere erişiyor ve bu cookie’leri cookies nesne array’imize atıyoruz.

analyzeRequest metodu ile ilgili çok net bir açıklama yok dokümantasyonda. Ancak HTTP istek başlıklarını elde edebilmek için öncelikle bu metodu çağırmak durumundayız. Bir sonraki adımda da başlıkları bir String array’ine atıyoruz.


String request = new String(currentRequest.getRequest());
        String messageBody = request.substring(rqInfo.getBodyOffset());
        for (int i = 0; i < cookies.size();i++){
         if(cookies.get(i).getName().toString().startsWith("MySessionid"))
          oturumParametresi = cookies.get(i).getValue();
        }

Yukarıdaki kod parçasında ilk olarak currentRequest nesnesi içindeki istek mesajını String veri tipi olarak alıyoruz.

getBodyOffset metodu mesajın içinde mesaj verisinin (data’sının) bulunduğu alanın başlangıç karakter sırasını veriyor. Mesaj verisini istek mesajımızın başlıklarına müdahale ettikten sonra mesajı tekrar oluşturmak için kullanacağız.

For döngüsü içinde kolayca anlaşılabileceği gibi daha önce Cookie Jar’dan aldığımız tüm cookie’leri tarayarak adı MySessionid adlı bir cookie varsa değerini oturumParametresi adlı lokal değişkenimize atıyoruz. Bu değeri bir HTTP başlığının değeri olarak kullanacağımızı söylemiştik.


for (int i = 0; i < headers.size();i++){
         if(headers.get(i).startsWith("X-My-SessionId:"))
          headers.set(i, "X-My-SessionId: " + oturumParametresi);
        }

Bu noktada mevcut isteğimiz içindeki başlıkları tarıyoruz. Bu extension’ı intruder modülünde kullanacağız ve kullanacağımız şablon istek içinde bu başlık zaten bulunacak. Ancak extension’ımızla bu başlığın değerini geçerli bir değer olarak belirleyeceğiz. Bu bölümde de kolayca anlaşılabileceği gibi başlıkların üzerinden geçip adı X-My-SessionId olan bir başlık bulduğumuzda bir önceki adımda Cookie Jar’dan tazelediğimiz cookie değerini bu başlığın değeri olarak belirliyoruz.


byte[] message = helpers.buildHttpMessage(headers, messageBody.getBytes());
        stdout.println(helpers.bytesToString(message));
        stdout.println("------------------------------------------------------");
        currentRequest.setRequest(message);  
    }
}

Son bölümde tekrar düzenlediğimiz HTTP başlıklarını ve daha önceden messageBody değişkenine aktardığımız mesaj verisini birleştirerek message adında yeni bir istek oluşturuyoruz.

Yeni oluşturduğumuz mesajı ve bir ayıracı konsola yazdıktan sonra işlemekte olduğumuz isteği yenisiyle değiştirerek akışına bırakıyoruz.

Extension’ımızın çalışması sırasında debug etme ve izleme amacıyla yazdığımız çıktıların görüntüleneceği Burp alanı aşağıdaki gibidir:


Kodumuzu geliştirdik, şimdi sıra derleme ve yeni extension’ımızı Burp’e yüklemede.

Derleme işlemini Eclipse’te Build All veya Build Project seçenekleri ile gerçekleştirebiliriz.


Extension’ımızı Burp’e tanıtabilmek için Jar arşivi haline getirmemiz gerekiyor, bunun için Eclipse’in Export fonksiyonalitesini kullanabiliriz.


Sadece BurpExtender.jar dosyasını export etmemiz yeterli.



Bu adımları sonuna kadar takip ettiğimizde Jar dosyamız oluşmuş ve içinde BurpExtender sınıfımız bulunuyor olacak.


Artık yeni extension’ımızı Burp’e tanıtabiliriz.


Aşağıdaki ekranı gördüğümüzde extension’ımız başarı ile yüklenmiş demektir. Tabi runtime’da yaşabileceğimiz sıkıntılar için konsola yazdıracağımız bilgiler debug için bize yardımcı olabilir.


Aşağıda gördüğünüz gibi Burp bizim kodumuzun içinde verdiğimiz isimle yeni yüklediğimiz extension’ımızı gösterdi.


Son olarak yeni Extension’ımızı bir Session Handling Rule içinde kullanabiliriz.






Böylece yeni extension’ımızı kullanabilir hale geldik.

Dürüst olmak gerekirse bir extension geliştirmek ilk aşamada biraz zor. Ancak bu araca hakim olduğunuzda, çok sık olmasa da, mevcut fonksiyonlarla gerçekleştiremediğiniz pek çok yöntemi uygulayabilir hale geleceksiniz.

Not: BTRisk tarafından geliştirilen makalelerin kısmen veya tamamen kopyalanarak yayınlanması durumunda intihal suçu duyurusu yapılacak ve yasal süreç başlatılacaktır.