20:30Temat wykladu:
Security: Błędy stack overflow, pisanie exploitów i shellcodów (by fr3m3n, 06.01.2007) @ wyklady.net
Komentarze: http://forum.wyklady.net/index.php?topic=101.0
20:27fr3m3nwitam wszystkich na wykladzie
20:28fr3m3nbedzie on poswiecony exploitacji bledow w programie
fr3m3nczyli po prostu wykorzystywaniu :)
fr3m3nopisze tutaj wykorzystywanie bledow buffer overflow na stosie (nazywane takze stack overflow) do przejecia kontroli
fr3m3nwszystkie przyklady beda na system windows, aczkolwiek na linuxa wszystko bedzie bardzo podobne
fr3m3nwiec nawet linuxowcy powinni sie czegos nauczyc ;>
fr3m3n(o ew. rozniach bede w razie czego mowil)
20:29fr3m3nwyklad wymaga podstaw c (co to wskaznik) i assemblera (umiejetnosc napisania hello world powinna wystarczyc )
fr3m3nnajpierw troche teorii, pozniej przejdziemy do najprostszego przykladu (strcpy)
fr3m3nchcialem opisac jeszcze off-by-one, shellcody i return to libc ale z przyczyn losowych
fr3m3n(dysk 'zgubil' kilka plikow...) opisze to byc moze w nastepnej czesci
fr3m3n(stad to przeniesienie, probowalem cos na szybko napisac ;>)
20:30fr3m3ndobrze
fr3m3nna poczatek powiem co to jest stos
fr3m3nstos jest to struktura danych rosnaca w dol
fr3m3nistnieje cos takiegp jak stack pointer
fr3m3n(wskaznik stosu)
20:31fr3m3nna poczatku wskaznik stosu jest ustawiany na sam 'szczyt' obszaru pamieci
fr3m3nwyglada to mniej wiecej tak:
fr3m3nPAMIEC <- wskaznik stosu
fr3m3nPAMIEC
fr3m3nPAMIEC
fr3m3n...
fr3m3nprzy operacji 'wrzucenia' danej na stos
fr3m3ntzw. push
fr3m3ndana jest kopiowane do aktualnego obszaru wskazywanego przez wskaznik
20:32fr3m3npo czym wskaznik jest 'opuszczany' w dol
fr3m3nczyli po prostu zmniejszany
fr3m3ndlatego stos 'rosnie' w dol
fr3m3nw assemblerze x86 stos jest zaimplementowany hardware'owo przez procesor
20:33fr3m3ninstrukcja 'push' wstawia na stos zmienna, po czym odejmuje od wskaznika stosu jej rozmiar (najczesciej 4)
fr3m3ninstrukcja pop odwrotnie - pobiera zmienna z stosu i zwieksza go
fr3m3npoprzez dodanie jej rozmiaru do wskaznika stosu
20:34fr3m3nwskaznik stosu na 32 bitowej architekturze x86 nazywa sie esp
fr3m3njest to rejestr, pojecie znane koderom assemblera
fr3m3nteraz - jaki to ma zwiazek z przepelnieniem stosu?
fr3m3nkazda funkcja (o tym za chwile) moze miec tzw. zmienne lokalne
20:35fr3m3nzmienna lokalna jest to po prostu obszar pamieci zaalokowany na stosie - poprzez odjecie zadanego rozmiaru od rejestru wskaznikowego (esp)
fr3m3nczyli np: sub esp,8 - zaalokuje nam 8 bajtow
20:36fr3m3nnalezy tutaj dodac, ze choc dane ze stosu,jako teoretycznej struktury danych, moga byc sciagane tylko za pomoca push i pop
fr3m3nto w fizycznej implementacji sprzetowej jest to po prostu obszar pamieci
fr3m3ndo ktorego mozna sie odnosic jak do zwyklej tablicy (buforu)
20:37fr3m3nstos jest takze uzywany przez funkcje
fr3m3nzastanawialiscie sie kiedys, skad funkcja 'wie' gdzie ma wrocic?
fr3m3njakakolwiek, np printf ;>
fr3m3nwywolanie funkcji w assemblerze wyglada tak:
fr3m3ncall funkcja
20:38fr3m3ncall sa to jakby dwie polaczone instrukcje
fr3m3npush adres_nastepnego_kodu
fr3m3njmp funkcja
fr3m3nczyli
fr3m3nnajpierw wrzuca na stos adres kodu, do ktorego funkcja ma wrocic
fr3m3nczyli kod tuz za instrukcja call
fr3m3nnp:
fr3m3ncall funkcja
20:39fr3m3ncos:
fr3m3nmov eax,ebx
fr3m3nfunkcja powroci (przynajmniej powinna ;) do 'cos'
fr3m3nczyli do mov eax,ebx
fr3m3n(jmp jest to instrukcja skoku - po prostu goto)
20:40fr3m3nfunkcja najczesciej wyglada tak:
fr3m3n1. ustaw ramke stosu
fr3m3n2. zaalokuj potrzebne zmienne/bufory lokalne
fr3m3n3. kod funkcji, np wypisanie tekstu
20:41fr3m3n(czesto po punkcie 1 jest jeszcze zachowanie zmienianych podczas dzialania rejestrow)
fr3m3n4. zdjecie ramki stosu
fr3m3n5. powrot
fr3m3npowrot jest realizowany jedna instrukcja - ret
fr3m3nret 'sciaga' adres z stosu
fr3m3ni skacze do niego
fr3m3ni tutaj pojawia sie wlasnie mozliwosc zmiany dzialania programu
20:42fr3m3nw sposob niekoniecznie przewidziany przez programiste
fr3m3nzalozmy, ze funkcja jeden argument - adres buforu
20:43fr3m3nah, argumenty sa kladzione na stos przed call
fr3m3nczyli np
fr3m3npush adres_tekstu "ala ma kota"
fr3m3ncall puts
fr3m3njest rownoznaczne
fr3m3nputs("ala ma kota");
fr3m3nw c
fr3m3nwiec, zalozmy ze funkcja dostaje jeden argument - adres jakiegos bufora
fr3m3nmoze to byc adres tekstu, jak w przypadku funkcji puts
20:44fr3m3nwygladalo by to tak: (w funkcji)
fr3m3nfunkcja:
fr3m3n[esp] = adres powrotu
fr3m3n[esp+4] = argument 1
20:45fr3m3ntak wygladaja adresy przy starcie
fr3m3nprawie zawsze jednak funkcje (szcegolnie w hllach) wykorzystuja tzw. ramke stosu
fr3m3nulatwia to obliczanie kompilatorowi zmiennych
20:46fr3m3njest to wlasciwie pozostaloasc z dawnych czasow, gdy nie wszystkie rejestry mogly byc uzywane jako adresowe
fr3m3nale o tym kiedys indziej ;>
fr3m3nzalozenie ramki stosu wyglada tak:
fr3m3npush ebp
fr3m3nmov ebp,esp
fr3m3nstos wyglada teraz tak:
fr3m3nARGUMENT 1
fr3m3nADRES POWROTU
fr3m3nZACHOWANY EBP
20:47fr3m3nw ebp znajduje sie esp
fr3m3nwiec mozna to przedstawic jako adresy relatywne do ebp
fr3m3n[ebp] - zachowany ebp
20:48fr3m3n( nawiasy kwadratowe w assemblerze oznaczaja pobranie zawartosci spod adresu, cos jak * w c)
fr3m3n[ebp+4] = adres powrotu
fr3m3n[ebp+8]
fr3m3nadresy te sa stale przez caly czas dzialania funkcji, ebp nie jest zmieniane, w przeciwienstwie do esp
20:49fr3m3npo postawieniu ramki stosu, bardzo prawdopodobna jest alokacja zmiennych lokalnych:
fr3m3nsub esp,8 ;odejmij od wskaznika 8 - zaalokuj obszar 8 bajtow
fr3m3njest to rownoznaczne
fr3m3nchar bufor[8];
fr3m3nw c.
fr3m3nadres takiego buforu przy uzyciu rejestru ebp wyglada tak:
20:50fr3m3n[ebp-8]
fr3m3ndobrze, zalozmy ze dalej jest kod funkcji - powiedzmy ze jest to strcpy
fr3m3ntylko taki specjalny, kopiujacy do swojego buforu ;>
fr3m3nw c wygladalo by to mniej wiecej tak:
20:51fr3m3nint strcpy(char* argument){
fr3m3nchar bufor[8];
fr3m3nfor(int i=0;;i++){
20:52fr3m3nif(argument[i]==0) break;
fr3m3nbufor[i]=argument[i];
fr3m3n}
fr3m3n(mozna to zapisac z warunkiem z for, ale imho tak lepiej widac co dokladnie kod robi i kiedy)
fr3m3nwszystko bedzie dobrze, kiedy dlugosc naszego tekstu bedzie np. 5
20:53fr3m3nwtedy zostanie skopiowane 6 bajtow (5 znakow+null) do bufora
fr3m3ni funkcja ladnie sie zakonczy
fr3m3njednak w przypadku gdy string, ktorego adres jest podawany jako argument
20:54fr3m3nbedzie mial np. 10 znakow
fr3m3nbufor zostanie zapisany az do bufor[10];
fr3m3nco sie tam znajduje?
fr3m3nw assemblerze przy uzyciu rejestru ebp wygladalo by to tak:
fr3m3n[ebp-8] - poczatek bufora (bufor[0])
20:55fr3m3n[ebp-1] - bufor[7]
fr3m3n[ebp] czyli zachowany rejestr ebp z poprzedniej funkcji = bufor[8]
fr3m3nmozna wiec nadpisac wazne dane
20:56fr3m3nprzy jeszcze dluzszym stringu, np. 16 znakow
fr3m3nzostanie nadpisany rowniez adres powrotu
fr3m3nczyli tam, gdzie funkcja skacze po zakonczeniu swojego dzialania :)
20:57fr3m3nw przypadku jakiekolwiek tekstu nastapi po prostu blad - segmentation fault
fr3m3nfunkcja wroci 'w kosmos' co spowoduje blad i zakonczenie programu
fr3m3njednak przygotowujac specjalny string jako argument - taki, w ktorym w odpowiednim miejscu bedzie adres jakiejs funkcji
20:58fr3m3n(np SformatujDyskTwardy, oczywiscie zakladajac ze taka istnieje ;d)
fr3m3nzostanie ona wykonana
fr3m3nza chwile pokaze dziurawy kod z takim bledem, i program exploitujacy
20:59fr3m3npytania?
Ballergnie
fr3m3nchyba nie
Vizzdoomleicmy dalej
fr3m3nhttp://www.fr3m3n.yoyo.pl/wyklad/wyklad_strcpy.c
21:00fr3m3ntutaj znajduje sie dziurawy kod
fr3m3nzrobilem specjalna funkcje - runme
fr3m3nzeby bylo co wykonywac
fr3m3nmozna wrzucic swoj kod jako argument i skoczyc do niego - takie cos nazywa sie shellcode
fr3m3no tym jednak byc moze powiem kiedy indziej
21:01fr3m3nzobaczmy, jak to wyglada w assemblerze - dobry do tego jest ollydbg
fr3m3nmzoan tez oprzec sie na tym co nam da kompilator (opcja -S), ale bardzo czesto jedyna rzecza jako dysponujemy jest binarka
21:02fr3m3nhttp://www.fr3m3n.yoyo.pl/wyklad/wyklad_strcpy.exe
fr3m3ntutaj jest plik exe
fr3m3n
fr3m3ntutaj, ten sam plik otwarty w debuggerze
fr3m3nwidac tutaj funkcje main
21:03fr3m3ndwie instrukcje na poczatku - push ebp ; mov ebp,esp
fr3m3nto wlasnie ustawianie ramki stosu
fr3m3ndalej - mamy alokacje bufor[8]
fr3m3npoprzez dwa pushe - taka optymalizacja
fr3m3njest to calkowicie rownowazne
fr3m3nsub esp,8
21:04fr3m3n(jeden push odejmuje od esp 4, wiec dwa alokuja nam akurat caly bufor)
fr3m3ndalej mamy instrukcje if - jesli argumentow jest mniej niz 2 (nazwa programu + argument) program sie konczy
fr3m3nif(argc<2)
fr3m3nreturn -1;
21:05fr3m3nw eax znajduje sie wartosc zwracana, 0xFFFFFFFF to -1
fr3m3nnastepnie jest wywolywana funkcja strcpy - jeden argument to argv[0]
fr3m3ndrugi to adres naszego lokalnego bufora
21:06fr3m3npozniej jest wywolanie puts i wypisanie tego bufora :)
fr3m3njest duzo komentarzy, wiec zrozumienie kodu assemblera nie powinno stanowic zbyt duzego problemu
21:07fr3m3ntfu, nie bufora, tylko napisu koniec :)
fr3m3nzostawmy na razie analize kodu
fr3m3ni sprobujmy uruchomic funkcje w konsoli
21:08fr3m3n
D:\wyklad_exploity\gotowe>wyklad_strcpy
fr3m3nnic sie nie stalo - funkcja nie ma rgumentow
fr3m3n
D:\wyklad_exploity\gotowe>wyklad_strcpy 12345678
fr3m3n
koniec
fr3m3nfunckja ma argument, wiec przechodzi przez if - ale cia