Posted on October 30th, 2012

Kapalı kutu (black-box) uygulama güvenliği testleri gerçekleştirirken , ilk yazımızda bahsettiğimiz gibi önce uygulama hakkında bilgi edinmekle işe başlamak gerekiyor. Uygulamanın özelliklerinin tespiti, çağırdı sistem kütüphanelerinin tespiti…  (örneğin ws2_32.dll kütüphanesini import ediliyorsa , uygulama network işlemleri gerçekleştirecektir.)

Bu yazımızda ekibimizin güvenlik testini yaptığı gerçek bir uygulamada network tabanlı bir zafiyeti nasıl tespit ettiğimiz ele alınacaktır.

Network işlemleri gerçekleştiren bir windows uygulaması pakat alım işlemini genelde  recv() , WSARecv(), WSARecvEx gibi fonksiyonları kullanarak yapar. Network tabanlı zafiyet araştırmalarında bu  fonksiyonlar genellikle incelenir ve potansiyel güvenlik açıkları, uygulamanın aldığı paketler manipüle edilerek saptanabilir.

Bazen test edilen uygulamanın client ve server tarafı aynı sistemde (localhost) çalışıyor  olabilir,  server uygulaması yine aynı sistem üzerinde başka bir yazılımla haberleşiyor olabilir. Böyle durumlarda, windows’da bu paketleri (loopback packet) Wireshark vb. yazılımlar ile dinleyemeyebilir ve doğal olarak manipüle edemeyebilirsiniz.

Loopback paketlerin tespiti ve manipülasyonu için reversing (tersine mühendislik) yöntemlerine ihtiyaç duyulur.Yapılması gereken ilk iş uygulamada recv , wsarecv gibi fonksiyonları tespit etmektir.

Yukarıdaki resimde test ettiğimiz uygulamanın çağırdı bir recv() fonksiyonu görülebilir. Yukarıdaki fonksiyonu C koduna çevirirsek şöyle olacaktır;

recv(var_1c, var_14, var_10, 0)

Bu durumda var_14  alınan pakete,  var_10 ise alınan paketin uzunluğuna (len) tekabül ediyor. recv() fonksiyonu çalıştıktan sonra , uygulama gelen bir paket var mı yok mu diye  “cmp “ ile kontrol edip  “jle” ile şartlı zıplama gerçekleştiriyor.

Uygulama bir paket aldığında yukarıdaki fonksiyona zıplıyor ve burada görüldüğü gibi gelen paketin “KEY” ile başlayıp başlamadığını kontrol ediyor. Eğer gelen paket “KEY” ile başlıyorsa yazının devamında da göreceğimiz gibi uygulama bu paketi işlemeye başlıyor.  Bazı uygulamalarda paket / protokol yapısına, bilgisine ulaşmak bu kadar kolay olmayabilir,  gelen-giden paketlerde çeşitli encryption ve decryptionlar uygulanabilir.

recv() fonksiyonu ile uygulamanın aldığı paketi yakalamak için  debuggerda recv()’den sonra bir  breakpoint koyuyoruz ve uygulamayı çalıştırıyoruz.

Uygulamamız bir paket aldığında ve recv() fonksiyonu çalıştığında, breakpoint koyduğumuz yerde duruyor.  Gelen paketi EAX ‘ e atana kadar  debuggerda “t”  komutu ile uygulamayı adım adım çalıştırmaya devam ediyoruz.(step into)  En son “d eax” ile komutuyla gelen paketin içeriğini görüyoruz. Paket beklediğimiz gibi “KEY” imzası ile başlıyor.

 

 

Gelen paket “KEY” ile başlıyorsa , uygulama yukarıdaki işlemleri gerçekleştiriyor.  (IDA Yorum satırları  ekleyerek, her satırı oldukça açıklamaya çalıştık. )

Uygulama gelen paketin boyutu kadar malloc fonksiyonu ile hafıza tahsisi gerçekleştirip , tahsis ettiği hafızaya , gelen paketi kopyalıyor.  Bu kopyalama işlemini görüldüğü gibi strNcpy güvenli fonksiyonu ile yapıyor , üstelik strncpy’nin “size” değişkenini de gelen paketin boyutunun 1 eksiği olarak belirleyip, off-by-one hatası önlemini de gerçekleştirmiş oluyor.

İlk bakışta buradan birşey bulamayacağımızı düşünürken ,  strncpy’den hemen sonra, uygulamanın aldığı paket (recvbuf) ve paketin boyutu (recvbuflen) ile sub_401822  alt fonksiyonunu çağırdığını farkediyoruz.

 

Uygulama bu alt fonksiyonda 800h’lık (2048 byte) bir alan tahsisi gerçekleştiriyor ve bu alana memcpy ile gelen paketi kopyalıyor. İlk bakışta buradaki zafiyet/hata farkedilmeyebilir.  Ancak  “memcpy” öncesi gelen datanın bir kontrolü yapılmamış ve “memcpy”  fonksiyonunun “size” değişkenine doğrudan uygulamanın aldığı paketin uzunluğu (recvbuflen) verilmiş.  Dolayısıyla uygulamaya “KEY” ile başlayan ve 2048 byte’dan daha uzun bir paket geldiğinde heap tabanlı bir hafıza taşması meydana gelecektir.

Bu alt fonksiyonda  , hafızı tahsisinin boyutu statik değil de dinamik olarak gelen datanının uzunluğuna göre (malloc(sizeof(recvbuf) ) belirlenmiş olsaydı  ya da “memcpy fonksiyonun size değişkenine statik olarak 800h’den daha küçük bir integer yazılsaydı , hafıza taşmasının oluşması engellenebilirdi.

Reply