Bu sonraki bölümde, pawnable.kr üzerindeki UAF meydan okumasına göz gezdireceğiz. Bu bir 64-Bit (x64 bit) Linux UAF açığıdır. UAF'ı toddler bölümüne koymak biraz bir yüze şaplak atmaya benzer ama gördündükleri kadar korkunç değiller. Hadi konuya girelim.
Meydan Okumayı Keşfetmek
Tekrar, binary için birkaç kaynak sağladık. Aşşağıda var
Kodları tekrar gözden geçirmek biraz zaman alacaktır. Herşeyden önce, program kurulduğu zaman "man" ve "woman" adında 2 obje oluşturur. Aşşağıda ana fonksiyon proloğu görüyorsunuz.
Her 2 obje için de 0x18 (24 Bayt) miktarında boyut ayrıldığına dikkat edin (minimum bir malloc boyutu). Bizde str "Jack" + int 0x19 (25) ve str "Jill" + int 0x15 (21) var.
Prologdan sonra, dallara ayrılmış bir menüye ulaşıyoruz. Kaynak koddan burada problem olduğu açık. Eğer "free"yi ve daha sonrasında "use"yi seçersek program segfault'daki silinmiş "man" ve "woman" objelerini sonuçları girişim yöntemini çağırmaya çalışacaktır.
Bu, 2 argüman alan "after" seçeneğini bırakır. İkinci argüman bir dosya yoludur ve birinci argüman dosyadan hafızanın içine yazılan X baytlarının okunmasında kullanılan bir tamsayıdır.
Tamamdır, oldukça basit. Eğer menu seçeneği olan "free"yi seçersek, ve daha sonra kendi özel objelerimizi bölüştürürsek (eşit boyutlarda) ve verilere menu seçeneği olan "use"yle referans verirdiğimizde birkaç çeşit ilkel kod yürütebilmeyi yapabililiriz.
Kalan son soru ise meydan okumayı tamamlamak için odaklanmamız gereken nedir? "Human" sınıfı bizim için bash shell yaratabilen"give_shell" denilen özel bir yönteme sahip. Bu oldukça uygun bir hedef gibi görünüyor.
Herşeyi Pwn'lıyalım
Bunun çalışması için "use" seçeneğini daha iyi anlamamız lazım. Aşşağıda parçalara ayrılmış grafiği görüyorsunuz.
Burada birbirine yakın 2 eş çağrı var gibi görünüyor. Muhtemelen birisi "man" objesi için diğeri de "woman" objesi için. Hadi GDB'deki "use"yi kıralım ve neler olacağına bakalım.
Merakla, "Human::give_shell" yönteminin işaretçilerini görebiliyoruz. QWORD işaretçisi RDX'e yüklenmeden ve sonra main+286'da çalıştırılmadan önce RAX'a 8(IntPtr boyutu) eklediğimize dikkat edin. 8'i ekledikten sonra QWORD işaretçisi "Man::introduce"ya dönüşür.
Programa 24 tampon verelim ve neler olacağına bakalım. Dosyaları aşşağıdaki gibi kurabiliriz.
Etrafında biraz oynadıktan sonra kodumuzu ilkel yapmak içim menu seçeneklerinden "after"ı 2 kere seçmek zorunda olduğumuzu buldum. 24 baytlık 2 obje siliyoruz bu yüzden 2 tane 24 baytlık ayırma yapmamız gerektiğini varsayıyorum. Yada daha doğrusu, menü seçeneklerinden "use"ye tıkladığımızda ilk çağrı aslında 2. bölmeyi buna karşılık 2. çağrı da 1. bölmeyi referanslar.
Oyun burda neredeyse sona eriyor. Keyfi olarak bir address ve daha önce "Human::give_shell" yöntemiyle bulduğumuz 2 adet QWORDS'u çağırabiliriz. Bunlardan 2 tanesini alıp ve 8 çıkartırsak (eşitlemek için rax'a 8 eklememiz gerektiğini hatırlayalım) bash shell'e yönlendirileceğiz.
Oyun Bitti
Hadi kutuya girelim ve şu bayrağı alalım !
Source: https://www.fuzzysecurity.com/tutorials/expDev/13.html
Translator: M3m0ry
Meydan Okumayı Keşfetmek
Tekrar, binary için birkaç kaynak sağladık. Aşşağıda var
Kod:
#include <fcntl.h>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
using namespace std;
class Human{
private:
virtual **** give_shell(){
system("/bin/sh");
}
protected:
int age;
string name;
public:
virtual **** introduce(){
cout << "My name is " << name << endl;
cout << "I am " << age << " years old" << endl;
}
};
class Man: public Human{
public:
Man(string name, int age){
this->name = name;
this->age = age;
}
virtual **** introduce(){
Human::introduce();
cout << "I am a nice guy!" << endl;
}
};
class Woman: public Human{
public:
Woman(string name, int age){
this->name = name;
this->age = age;
}
virtual **** introduce(){
Human::introduce();
cout << "I am a cute girl!" << endl;
}
};
int main(int argc, char* argv[]){
Human* m = new Man("Jack", 25);
Human* w = new Woman("Jill", 21);
size_t len;
char* data;
unsigned int op;
while(1){
cout << "1. use\n2. after\n3. free\n";
cin >> op;
switch(op){
case 1:
m->introduce();
w->introduce();
break;
case 2:
len = atoi(argv[1]);
data = new char[len];
read(open(argv[2], O_RDONLY), data, len);
cout << "your data is allocated" << endl;
break;
case 3:
delete m;
delete w;
break;
default:
break;
}
}
return 0;
}
Kodları tekrar gözden geçirmek biraz zaman alacaktır. Herşeyden önce, program kurulduğu zaman "man" ve "woman" adında 2 obje oluşturur. Aşşağıda ana fonksiyon proloğu görüyorsunuz.
Her 2 obje için de 0x18 (24 Bayt) miktarında boyut ayrıldığına dikkat edin (minimum bir malloc boyutu). Bizde str "Jack" + int 0x19 (25) ve str "Jill" + int 0x15 (21) var.
Prologdan sonra, dallara ayrılmış bir menüye ulaşıyoruz. Kaynak koddan burada problem olduğu açık. Eğer "free"yi ve daha sonrasında "use"yi seçersek program segfault'daki silinmiş "man" ve "woman" objelerini sonuçları girişim yöntemini çağırmaya çalışacaktır.
Bu, 2 argüman alan "after" seçeneğini bırakır. İkinci argüman bir dosya yoludur ve birinci argüman dosyadan hafızanın içine yazılan X baytlarının okunmasında kullanılan bir tamsayıdır.
Tamamdır, oldukça basit. Eğer menu seçeneği olan "free"yi seçersek, ve daha sonra kendi özel objelerimizi bölüştürürsek (eşit boyutlarda) ve verilere menu seçeneği olan "use"yle referans verirdiğimizde birkaç çeşit ilkel kod yürütebilmeyi yapabililiriz.
Kalan son soru ise meydan okumayı tamamlamak için odaklanmamız gereken nedir? "Human" sınıfı bizim için bash shell yaratabilen"give_shell" denilen özel bir yönteme sahip. Bu oldukça uygun bir hedef gibi görünüyor.
Herşeyi Pwn'lıyalım
Bunun çalışması için "use" seçeneğini daha iyi anlamamız lazım. Aşşağıda parçalara ayrılmış grafiği görüyorsunuz.
Burada birbirine yakın 2 eş çağrı var gibi görünüyor. Muhtemelen birisi "man" objesi için diğeri de "woman" objesi için. Hadi GDB'deki "use"yi kıralım ve neler olacağına bakalım.
Merakla, "Human::give_shell" yönteminin işaretçilerini görebiliyoruz. QWORD işaretçisi RDX'e yüklenmeden ve sonra main+286'da çalıştırılmadan önce RAX'a 8(IntPtr boyutu) eklediğimize dikkat edin. 8'i ekledikten sonra QWORD işaretçisi "Man::introduce"ya dönüşür.
Programa 24 tampon verelim ve neler olacağına bakalım. Dosyaları aşşağıdaki gibi kurabiliriz.
Kod:
python -c 'print ("\x41"*8 + "\x42"*8 + "\x43"*8)' > OutFile
Etrafında biraz oynadıktan sonra kodumuzu ilkel yapmak içim menu seçeneklerinden "after"ı 2 kere seçmek zorunda olduğumuzu buldum. 24 baytlık 2 obje siliyoruz bu yüzden 2 tane 24 baytlık ayırma yapmamız gerektiğini varsayıyorum. Yada daha doğrusu, menü seçeneklerinden "use"ye tıkladığımızda ilk çağrı aslında 2. bölmeyi buna karşılık 2. çağrı da 1. bölmeyi referanslar.
Oyun burda neredeyse sona eriyor. Keyfi olarak bir address ve daha önce "Human::give_shell" yöntemiyle bulduğumuz 2 adet QWORDS'u çağırabiliriz. Bunlardan 2 tanesini alıp ve 8 çıkartırsak (eşitlemek için rax'a 8 eklememiz gerektiğini hatırlayalım) bash shell'e yönlendirileceğiz.
Kod:
0x401570 - 8 = 0x401568 => \x68\x15\x40\x00\x00\x00\x00\x00
0x401550 - 8 = 0x401548 => \x48\x15\x40\x00\x00\x00\x00\x00
Oyun Bitti
Hadi kutuya girelim ve şu bayrağı alalım !
Source: https://www.fuzzysecurity.com/tutorials/expDev/13.html
Translator: M3m0ry