20:30 | Temat 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:27 | fr3m3n | witam wszystkich na wykladzie |
20:28 | fr3m3n | bedzie on poswiecony exploitacji bledow w programie |
fr3m3n | czyli po prostu wykorzystywaniu :) | |
fr3m3n | opisze tutaj wykorzystywanie bledow buffer overflow na stosie (nazywane takze stack overflow) do przejecia kontroli | |
fr3m3n | wszystkie przyklady beda na system windows, aczkolwiek na linuxa wszystko bedzie bardzo podobne | |
fr3m3n | wiec nawet linuxowcy powinni sie czegos nauczyc ;> | |
fr3m3n | (o ew. rozniach bede w razie czego mowil) | |
20:29 | fr3m3n | wyklad wymaga podstaw c (co to wskaznik) i assemblera (umiejetnosc napisania hello world powinna wystarczyc ) |
fr3m3n | najpierw troche teorii, pozniej przejdziemy do najprostszego przykladu (strcpy) | |
fr3m3n | chcialem 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:30 | fr3m3n | dobrze |
fr3m3n | na poczatek powiem co to jest stos | |
fr3m3n | stos jest to struktura danych rosnaca w dol | |
fr3m3n | istnieje cos takiegp jak stack pointer | |
fr3m3n | (wskaznik stosu) | |
20:31 | fr3m3n | na poczatku wskaznik stosu jest ustawiany na sam 'szczyt' obszaru pamieci |
fr3m3n | wyglada to mniej wiecej tak: | |
fr3m3n | PAMIEC <- wskaznik stosu | |
fr3m3n | PAMIEC | |
fr3m3n | PAMIEC | |
fr3m3n | ... | |
fr3m3n | przy operacji 'wrzucenia' danej na stos | |
fr3m3n | tzw. push | |
fr3m3n | dana jest kopiowane do aktualnego obszaru wskazywanego przez wskaznik | |
20:32 | fr3m3n | po czym wskaznik jest 'opuszczany' w dol |
fr3m3n | czyli po prostu zmniejszany | |
fr3m3n | dlatego stos 'rosnie' w dol | |
fr3m3n | w assemblerze x86 stos jest zaimplementowany hardware'owo przez procesor | |
20:33 | fr3m3n | instrukcja 'push' wstawia na stos zmienna, po czym odejmuje od wskaznika stosu jej rozmiar (najczesciej 4) |
fr3m3n | instrukcja pop odwrotnie - pobiera zmienna z stosu i zwieksza go | |
fr3m3n | poprzez dodanie jej rozmiaru do wskaznika stosu | |
20:34 | fr3m3n | wskaznik stosu na 32 bitowej architekturze x86 nazywa sie esp |
fr3m3n | jest to rejestr, pojecie znane koderom assemblera | |
fr3m3n | teraz - jaki to ma zwiazek z przepelnieniem stosu? | |
fr3m3n | kazda funkcja (o tym za chwile) moze miec tzw. zmienne lokalne | |
20:35 | fr3m3n | zmienna lokalna jest to po prostu obszar pamieci zaalokowany na stosie - poprzez odjecie zadanego rozmiaru od rejestru wskaznikowego (esp) |
fr3m3n | czyli np: sub esp,8 - zaalokuje nam 8 bajtow | |
20:36 | fr3m3n | nalezy tutaj dodac, ze choc dane ze stosu,jako teoretycznej struktury danych, moga byc sciagane tylko za pomoca push i pop |
fr3m3n | to w fizycznej implementacji sprzetowej jest to po prostu obszar pamieci | |
fr3m3n | do ktorego mozna sie odnosic jak do zwyklej tablicy (buforu) | |
20:37 | fr3m3n | stos jest takze uzywany przez funkcje |
fr3m3n | zastanawialiscie sie kiedys, skad funkcja 'wie' gdzie ma wrocic? | |
fr3m3n | jakakolwiek, np printf ;> | |
fr3m3n | wywolanie funkcji w assemblerze wyglada tak: | |
fr3m3n | call funkcja | |
20:38 | fr3m3n | call sa to jakby dwie polaczone instrukcje |
fr3m3n | push adres_nastepnego_kodu | |
fr3m3n | jmp funkcja | |
fr3m3n | czyli | |
fr3m3n | najpierw wrzuca na stos adres kodu, do ktorego funkcja ma wrocic | |
fr3m3n | czyli kod tuz za instrukcja call | |
fr3m3n | np: | |
fr3m3n | call funkcja | |
20:39 | fr3m3n | cos: |
fr3m3n | mov eax,ebx | |
fr3m3n | funkcja powroci (przynajmniej powinna ;) do 'cos' | |
fr3m3n | czyli do mov eax,ebx | |
fr3m3n | (jmp jest to instrukcja skoku - po prostu goto) | |
20:40 | fr3m3n | funkcja najczesciej wyglada tak: |
fr3m3n | 1. ustaw ramke stosu | |
fr3m3n | 2. zaalokuj potrzebne zmienne/bufory lokalne | |
fr3m3n | 3. kod funkcji, np wypisanie tekstu | |
20:41 | fr3m3n | (czesto po punkcie 1 jest jeszcze zachowanie zmienianych podczas dzialania rejestrow) |
fr3m3n | 4. zdjecie ramki stosu | |
fr3m3n | 5. powrot | |
fr3m3n | powrot jest realizowany jedna instrukcja - ret | |
fr3m3n | ret 'sciaga' adres z stosu | |
fr3m3n | i skacze do niego | |
fr3m3n | i tutaj pojawia sie wlasnie mozliwosc zmiany dzialania programu | |
20:42 | fr3m3n | w sposob niekoniecznie przewidziany przez programiste |
fr3m3n | zalozmy, ze funkcja jeden argument - adres buforu | |
20:43 | fr3m3n | ah, argumenty sa kladzione na stos przed call |
fr3m3n | czyli np | |
fr3m3n | push adres_tekstu "ala ma kota" | |
fr3m3n | call puts | |
fr3m3n | jest rownoznaczne | |
fr3m3n | puts("ala ma kota"); | |
fr3m3n | w c | |
fr3m3n | wiec, zalozmy ze funkcja dostaje jeden argument - adres jakiegos bufora | |
fr3m3n | moze to byc adres tekstu, jak w przypadku funkcji puts | |
20:44 | fr3m3n | wygladalo by to tak: (w funkcji) |
fr3m3n | funkcja: | |
fr3m3n | [esp] = adres powrotu | |
fr3m3n | [esp+4] = argument 1 | |
20:45 | fr3m3n | tak wygladaja adresy przy starcie |
fr3m3n | prawie zawsze jednak funkcje (szcegolnie w hllach) wykorzystuja tzw. ramke stosu | |
fr3m3n | ulatwia to obliczanie kompilatorowi zmiennych | |
20:46 | fr3m3n | jest to wlasciwie pozostaloasc z dawnych czasow, gdy nie wszystkie rejestry mogly byc uzywane jako adresowe |
fr3m3n | ale o tym kiedys indziej ;> | |
fr3m3n | zalozenie ramki stosu wyglada tak: | |
fr3m3n | push ebp | |
fr3m3n | mov ebp,esp | |
fr3m3n | stos wyglada teraz tak: | |
fr3m3n | ARGUMENT 1 | |
fr3m3n | ADRES POWROTU | |
fr3m3n | ZACHOWANY EBP | |
20:47 | fr3m3n | w ebp znajduje sie esp |
fr3m3n | wiec mozna to przedstawic jako adresy relatywne do ebp | |
fr3m3n | [ebp] - zachowany ebp | |
20:48 | fr3m3n | ( nawiasy kwadratowe w assemblerze oznaczaja pobranie zawartosci spod adresu, cos jak * w c) |
fr3m3n | [ebp+4] = adres powrotu | |
fr3m3n | [ebp+8] | |
fr3m3n | adresy te sa stale przez caly czas dzialania funkcji, ebp nie jest zmieniane, w przeciwienstwie do esp | |
20:49 | fr3m3n | po postawieniu ramki stosu, bardzo prawdopodobna jest alokacja zmiennych lokalnych: |
fr3m3n | sub esp,8 ;odejmij od wskaznika 8 - zaalokuj obszar 8 bajtow | |
fr3m3n | jest to rownoznaczne | |
fr3m3n | char bufor[8]; | |
fr3m3n | w c. | |
fr3m3n | adres takiego buforu przy uzyciu rejestru ebp wyglada tak: | |
20:50 | fr3m3n | [ebp-8] |
fr3m3n | dobrze, zalozmy ze dalej jest kod funkcji - powiedzmy ze jest to strcpy | |
fr3m3n | tylko taki specjalny, kopiujacy do swojego buforu ;> | |
fr3m3n | w c wygladalo by to mniej wiecej tak: | |
20:51 | fr3m3n | int strcpy(char* argument){ |
fr3m3n | char bufor[8]; | |
fr3m3n | for(int i=0;;i++){ | |
20:52 | fr3m3n | if(argument[i]==0) break; |
fr3m3n | bufor[i]=argument[i]; | |
fr3m3n | } | |
fr3m3n | (mozna to zapisac z warunkiem z for, ale imho tak lepiej widac co dokladnie kod robi i kiedy) | |
fr3m3n | wszystko bedzie dobrze, kiedy dlugosc naszego tekstu bedzie np. 5 | |
20:53 | fr3m3n | wtedy zostanie skopiowane 6 bajtow (5 znakow+null) do bufora |
fr3m3n | i funkcja ladnie sie zakonczy | |
fr3m3n | jednak w przypadku gdy string, ktorego adres jest podawany jako argument | |
20:54 | fr3m3n | bedzie mial np. 10 znakow |
fr3m3n | bufor zostanie zapisany az do bufor[10]; | |
fr3m3n | co sie tam znajduje? | |
fr3m3n | w assemblerze przy uzyciu rejestru ebp wygladalo by to tak: | |
fr3m3n | [ebp-8] - poczatek bufora (bufor[0]) | |
20:55 | fr3m3n | [ebp-1] - bufor[7] |
fr3m3n | [ebp] czyli zachowany rejestr ebp z poprzedniej funkcji = bufor[8] | |
fr3m3n | mozna wiec nadpisac wazne dane | |
20:56 | fr3m3n | przy jeszcze dluzszym stringu, np. 16 znakow |
fr3m3n | zostanie nadpisany rowniez adres powrotu | |
fr3m3n | czyli tam, gdzie funkcja skacze po zakonczeniu swojego dzialania :) | |
20:57 | fr3m3n | w przypadku jakiekolwiek tekstu nastapi po prostu blad - segmentation fault |
fr3m3n | funkcja wroci 'w kosmos' co spowoduje blad i zakonczenie programu | |
fr3m3n | jednak przygotowujac specjalny string jako argument - taki, w ktorym w odpowiednim miejscu bedzie adres jakiejs funkcji | |
20:58 | fr3m3n | (np SformatujDyskTwardy, oczywiscie zakladajac ze taka istnieje ;d) |
fr3m3n | zostanie ona wykonana | |
fr3m3n | za chwile pokaze dziurawy kod z takim bledem, i program exploitujacy | |
20:59 | fr3m3n | pytania? |
Ballerg | nie | |
fr3m3n | chyba nie | |
Vizzdoom | leicmy dalej | |
fr3m3n | http://www.fr3m3n.yoyo.pl/wyklad/wyklad_strcpy.c | |
21:00 | fr3m3n | tutaj znajduje sie dziurawy kod |
fr3m3n | zrobilem specjalna funkcje - runme | |
fr3m3n | zeby bylo co wykonywac | |
fr3m3n | mozna wrzucic swoj kod jako argument i skoczyc do niego - takie cos nazywa sie shellcode | |
fr3m3n | o tym jednak byc moze powiem kiedy indziej | |
21:01 | fr3m3n | zobaczmy, jak to wyglada w assemblerze - dobry do tego jest ollydbg |
fr3m3n | mzoan tez oprzec sie na tym co nam da kompilator (opcja -S), ale bardzo czesto jedyna rzecza jako dysponujemy jest binarka | |
21:02 | fr3m3n | http://www.fr3m3n.yoyo.pl/wyklad/wyklad_strcpy.exe |
fr3m3n | tutaj jest plik exe | |
fr3m3n | ||
fr3m3n | tutaj, ten sam plik otwarty w debuggerze | |
fr3m3n | widac tutaj funkcje main | |
21:03 | fr3m3n | dwie instrukcje na poczatku - push ebp ; mov ebp,esp |
fr3m3n | to wlasnie ustawianie ramki stosu | |
fr3m3n | dalej - mamy alokacje bufor[8] | |
fr3m3n | poprzez dwa pushe - taka optymalizacja | |
fr3m3n | jest to calkowicie rownowazne | |
fr3m3n | sub esp,8 | |
21:04 | fr3m3n | (jeden push odejmuje od esp 4, wiec dwa alokuja nam akurat caly bufor) |
fr3m3n | dalej mamy instrukcje if - jesli argumentow jest mniej niz 2 (nazwa programu + argument) program sie konczy | |
fr3m3n | if(argc<2) | |
fr3m3n | return -1; | |
21:05 | fr3m3n | w eax znajduje sie wartosc zwracana, 0xFFFFFFFF to -1 |
fr3m3n | nastepnie jest wywolywana funkcja strcpy - jeden argument to argv[0] | |
fr3m3n | drugi to adres naszego lokalnego bufora | |
21:06 | fr3m3n | pozniej jest wywolanie puts i wypisanie tego bufora :) |
fr3m3n | jest duzo komentarzy, wiec zrozumienie kodu assemblera nie powinno stanowic zbyt duzego problemu | |
21:07 | fr3m3n | tfu, nie bufora, tylko napisu koniec :) |
fr3m3n | zostawmy na razie analize kodu | |
fr3m3n | i sprobujmy uruchomic funkcje w konsoli | |
21:08 | fr3m3n | D:\wyklad_exploity\gotowe>wyklad_strcpy |
fr3m3n | nic sie nie stalo - funkcja nie ma rgumentow | |
fr3m3n | D:\wyklad_exploity\gotowe>wyklad_strcpy 12345678 | |
fr3m3n | koniec | |
fr3m3n | funckja ma argument, wiec przechodzi przez if - ale cia |