Zlouporaba ranjivosti računalnih sustava
Exploit
Vrste exploita
Buffer overflow
Heap overflow
Integer overflow
Format string attack
SQL injection
Cross-site scripting

3. Prekoračenje kapaciteta međuspremnika na stogu

3.1. Organizacija procesa u memoriji
3.2. Prekoračenje kapaciteta međuspremnika na stogu
   3.2.1. Područje stoga
   3.2.2. Pozivi funkcija
   3.2.3. Prekoračenje međuspremnika
   3.2.4. Eksploatacija
3.3. Primjeri eksploatacije prekoračenja kapaciteta međuspremnika na stogu

Međuspremnik (engl. buffer) je naziv  koji se koristi za blok susjednih memorijskih lokacija računala, koje sadrže višestruke instance podataka istog tipa, a u programskom jeziku C se obično povezuje sa poljem, odnosno najčešće sa znakovnim poljem (nizom). Polja, poput svih varijabli u C-u, mogu biti deklarirana kao statičke ili dinamičke varijable. Memorija za statičke varijable se rezervira za vrijeme učitavanja programa u memorijskom segmentu podataka, dok se memorija za dinamičke varijable rezervira na stogu za vrijeme izvođenja programa.  

U računalnoj sigurnosti i programiranju, pod pojmom prekoračenje kapaciteta međuspremnika (engl. buffer overflow, buffer overrun) smatramo programsku pogrešku, tj. iznimno stanje u koje se dolazi kada proces pokušava spremiti podatke izvan granica međuspremnika određene duljine. Posljedica toga je da oni podaci koji su ''višak'' prepišu susjedne  memorijske lokacije (primjer). Prepisani podaci na tim susjednim memorijskim lokacijama mogu biti međuspremnici, varijable pa i podaci toka programa.


Primjer 1.

Pretpostavimo da neki program rezervira dvije susjedne memorijske lokacije za pohranu vrijednosti dvije varijable. Varijabla A je tipa string (niz znakova) i u memoriji se za pohranu njene vrijednosti rezervira 8 bajtova (1 znak = 1 bajt). Varijabla B je tipa short integer (cijeli broj) i za njenu pohranu u memoriji se rezerviraju 2 bajta. Na početku su na memorijskim lokacijama rezerviranim za varijablu A zapisane sve nule, a varijabla B ima vrijednost 3.

A

B

0

0

0

0

0

0

0

0

0

3

Slika 3.1. Izged memorije na početku izvodenja programa

Ako program sad pokuša pohraniti znakovni niz ''kapacitet'' na memorijske lokacije rezervirane za varijablu A bez prethodne provjere granica, prepisat će se vrijednost varijable B.

A

B

'k'

'a'

'p'

'a'

'c'

'i'

't'

'e'

't'

'\0'

Slika 3.2. Izgled memorije nakon prekoračenja kapaciteta varijable A

Iako osoba koja je pisala program nije imala namjeru promijeniti vrijednost varijable B, njena je vrijednost promjenjena i poprimila je cjelobrojnu vrijednost znakovnog niza ''t\0'', tj. 29696.

Ako je B jedina varijabla, uz varijablu A, koja je definirana u programu i ako program pokuša pohraniti znakovni niz, duži od 9 znakova, na memorijske lokacije rezervirane za varijablu A, može doći do pogreške u radu programa (segmentation fault,  prekid izvođenja programa).


Kod napada koji se temelje na prekoračenju kapaciteta međuspremnika, taj ''višak'' podataka  mogu biti posebni programski kodovi koji su dizajnirani tako da prouzroče posebno ponašanje, npr. slanje naredbi napadnutom računalu koje bi mogle, na primjer, oštetiti korisnikove datoteke, promijeniti ili otkriti neke povjerljive podatke itd.

Općenito govoreći, prekoračenje kapaciteta međuspremnika dogodit će se svaki put kada program  zapiše više podataka u međuspremnik, nego što je rezervirao prostora u memoriji, za taj međuspremnik. Ovo omogućuje napadaču da prepiše podatke koji kontroliraju tok izvođenja programa i da preuzme kontrolu nad izvođenjem procesa, tako da se izršava napadačev kôd umjesto kôda procesa.

Da bi opisali kako se ova ranjivost može eksploatirati, moramo se upoznati sa nekim osnovnim pojmovima vezanim uz izvođenje programa i organizaciju procesa u memoriji.

3.1. Organizacija procesa u memoriji

[<< natrag na vrh]

Procesi se u memoriji dijele na tri područja: tekst, podaci i stog i gomila.

Područje teksta je određeno programom i obuhvaća kôd programa (naredbe) i  podatke koji su samo za čitanje (engl. read-only). Svaki pokušaj pisanja u ovo područje će izazvati segmentation violation, odnosno pogrešku zbog pokušaja pristupa nekoj nedozvoljenoj memorijskoj lokaciji ili zbog pristupa nekoj memorijskoj lokaciji na nedozvoljen način (npr. pokušaj pisanja tamo gdje postoji samo dozvola za čitanje).

Područje podataka sadrži inicijalizirane i neinicijalizirane podatke. U ovo područje se pohranjuju statičke varijable.

Područje stoga i gomile započinje na kraju područja podataka i raste prema većim memorijskim adresama. Na kraju ovog područja se nalazi oznaka dna stoga koji pak raste prema nižim memorijskim adresama.


Slika 3.3. Organizacija procesa u memoriji

3.2. Prekoračenje kapaciteta međuspremnika na stogu

[<< natrag na vrh]

Stog je apstraktni tip podataka koji se često koristi u računarstvu. To je struktura u memoriji kojoj se pojedini elementi uklanjaju redosljedom obrnutim od onoga kojim su bili dodavani, tako da se kao prvi uklanjaju oni koji su dodani strukturi kao poslijednji. Taj se postupak označava i kao last in, first out (LIFO). Podaci se mogu dodavati tj. stavljati na vrh stoga (engl. push) ili uzimati sa vrha (engl. pop). Stog se također koristi i za dinamičku alokaciju lokalnih varijabli funkcija, za prenošenje parametara funkciji i za vraćanje vrijednosti iz funkcije.

3.2.1. Područje stoga

[<< natrag na vrh]

Stog je blok memorije koji sadrži podatke. Pokazivač stoga (engl. Stack Pointer, SP) pokazuje na vrh stoga, dok se dno stoga nalazi na nekoj fiksnoj memorijskoj adresi. Veličinu stoga dinamički određuje jezgra operacijskog sustava (engl. kernel)  za vrijeme izvođenja procesa.

Pri pozivu funkcije na stog se stavljaju logički okviri stoga, a isti se pri povratku iz funkcije skidaju sa stoga. Ti okviri sadrže parametre funkcije, njene lokalne varijable i podatke potrebne da bi se stog vratio u prethodno stanje nakon povratka iz pozvane funkcije.

Ovisno o implementaciji, stog će rasti ili prema dolje (prema nižim memorijskim adresama) ili prema gore. U našim primjerima koristit ćemo stog koji raste prema dolje. SP također ovisi o implementaciji. On može pokazivati na posljednju adresu stoga (vrh stoga) ili na prvu sljedeću slobodnu memorijsku lokaciju. Za našu raspravu pretpostavit ćemo da pokazuje na poslijednju adresu na stogu.

...
Lokalne varijable
funkcije3
Povratna adresa funkcije2
Lokalne varijable
funkcije2
Povratna adresa funkcije1
Lokalne varijable
funkcije1
...
3.4. Na stogu su upravljački podaci pomješani sa podacima koje koristi program

Uz pokazivač stoga, često se koristi pokazivač okvira (engl. Frame Pointer, FP ili Local Base pointer, LB) koji pokazuje na neko fiksno mjesto unutar okvira. Mnogi prevoditelji koriste FP za referenciranje lokalnih varijabli i parametara funkcije zato što se njihove udaljenosti od FP-a ne mijenjaju (za razliku od udaljenosti od SP-a). Zbog smjera u kojem raste stog, aktualni parametri imaju pozitivne pomake, a lokalne varijable imaju negativne pomake u odnosu na FP.

3.2.2. Pozivi funkcija

[<< natrag na vrh]

U sljedećem jednostavnom primjeru opisano je što se događa na stogu nakon poziva funkcije.


Primjer 2.

Promotrimo što se događa nakon poziva funkcije pri izvođenju sljedećeg programa:

            void funkcija(int a, int b, int c)
            {
                  char buffer1[5];
                  char buffer2[10];
                 //na ovom mjestu promatramo stog(slika 3.5)
            }

            void main()
            {
                  funkcija(1,2,3);
            }

Prvo se na stog stavljaju argumenti funkcije i to obrnutim redosljedom. Naredba koja poziva funkciju stavlja na stog vrijednost instrukcijskog registra (engl. Instruction Pointer, IP). Instrukcijski registar sadrži adresu sljedeće naredbe koja se treba izvršiti po povratku iz funkcije i u trenutku poziva funkcije predstavlja upravo povratnu adresu te funkcije.

Prva stvar koju funkcija mora učiniti, nakon što je pozvana, je spremiti prethodni FP na stog (tako da se može obnoviti staro stanje pri završetku funkcije). Pohranjeni FP se naziva SFP (engl. Saved FP). Zatim se kopira SP u FP da bi se stvorio novi FP i povećava se SP da bi se rezerviralo mjesto za lokalne varijable. Ovaj postupak se naziva prolog procedure. Nakon završetka procedure, stog se mora ponovo ''očistiti'' što se naziva epilog procedure.



Slika 3.5. Izgled stoga nakon poziva funkcije

3.2.3. Prekoračenje međuspremnika

[<< natrag na vrh]

Prekoračenje kapaciteta međuspremnika na stogu (engl. buffer overflow) je rezultat pokušaja da u međuspremnik ''naguramo'' više podataka nego on može primiti. Kako se ova česta programska pogreška može iskoristiti? Pogledajmo sljedeći primjer:


Primjer 3.

            void funkcija2(char *str)
            {
                  char buffer[16];
                  strcpy(buffer,str);
            }

            void main()
            {
                  char velikiNiz[256];
                  int i;

                  for(i = 0; i < 255; i++)
                        velikiNiz[i] = 'A';

                  funkcija2(velikiNiz);
            }


Program u ovom primjeru sadrži funkciju sa tipičnom pogreškom koja može biti uzrokom prekoračenju kapaciteta međuspremnika na stogu. Funkcija kopira zadani niz znakova bez prethodne provjere granica koristeći strcpy(). Ako se ovaj program pokrene vratit će grešku (segmentation violation). Pogledajmo kako izgleda stog nakon poziva funkcije (Slika 3.6.)


Slika 3.6. Izgled stoga nakon poziva funkcije funkcija2

Naredba strcpy() kopira sadržaj niza *str (velikiNiz[]) u buffer[] dok ne pronađe oznaku kraja niza (engl. null character). Polje buffer[] je dugo 16 bajtova, a u njega se pokušava kopirati 256 bajtova. Posljedica toga bit će da će se prepisati još 250 bajtova stoga, poslije buffer[]-a (uključujući SFP, povratnu adresu pa čak i *str). velikiNiz je zapravo niz znakova 'A'. Hesadecimalna vrijednost znaka 'A' je 0x41, što znači da će povratna adresa sada biti 0x41414141. Ova adresa je izvan memorijskog prostora procesa. Zato kad se funkcija vrati i pokuša pročitati sljedeću naredbu sa te adrese dobit ćemo segmentation violation.
Dakle prekoračenje kapaciteta međuspremnika omogućava da se promjeni povratna adresa funkcije, a samim time i tok izvođenja programa.

Jedan od oblika eksploatacije ovakve ranijvosti programa je da napadač stavi u međuspremnik, čiji se kapacitet prekoračuje, kôd kojeg želi izvršiti, a povratnu adresu prepiše adresom upravo tog međuspremnika.

3.2.4. Eksploatacija

[<< natrag na vrh]

Tehnike koje se koriste za eksploataciju ranjivosti koje nastaju zbog prekoračenja kapaciteta međuspremnika variraju ovisno o arhitekturi računala, operacijskom susavu i području memorije koje se eksploatira.

Tehnički stručan i zlonamjeran korisnik može eksploatirati prekoračenje kapaciteta bazirano na stogu kako bi manipulirao programom na jedan od sljedećih načina:

Pomoću metode koja se naziva ''Trampolining'', ako je adresa programskog kôda kojeg želimo izvesti, nepoznata, ali je njena lokacija pohranjena u nekom registru, tada možemo prepisati povratnu adresu sa adresom operacijskog kôda koji predstavlja naredbu skoka na te naredbe u memoriji. Neka je na primjer, lokacija kôda kojeg želimo izvršiti pohranjena u nekom registru R. Skok na  lokaciju u memoriji na kojoj se nalazi operacijski kôd naredbe koja uzrokuje skok na registar R (npr. jump R, call R ili slično), će rezultirati izvođenjem željenih naredbi. Lokacije odgovarajućih operacijskih kodova ili bajtova u memoriji mogu se pronaći u DLL-u (engl. Dynamic-link library) ili u izvršnom kôdu. One u pravilu ne smiju sadržavati prazne znakove, a lokacije tih operacijskih kôdova variraju ovisno o programu i verziji operacijskog sustava.

3.3. Primjeri eksploatacije prekoračenja kapaciteta međuspremnika na stogu

[<< natrag na vrh]

Jedna od najranijih eksploatacija ovog tipa dogodila se 1988. Bio je to jedan od nekoliko exploita kojeg je koristio Morrisov crv (engl. Morris worm) da bi se širio po Internetu. Kasnije, 1995. Thomas Lopatic je nezavisno nanovo otkrio prekoračenje kapaciteta međuspremnika na stogu i objavio svoja otkrića na Bugtraq mailing listi. Godinu dana kasnije Elias Levy (također poznat kao Aleph One) je u časopisu Phrack objavio članak ''Razbijanje stoga zbog zabave i dobitka'' (engl. "Smashing the Stack for Fun and Profit"), u kojem opisuje korak po korak eksploataciju ranjivosti prekoračenja kapaciteta međuspremnika na stogu.

Od tada, najmanje dva veća intrenerska crva su eksploatirali ovu ranjivost da bi ugrozili velik broj sustava. 2001. godine, Code Red crv koristio je prekoračenje međuspremnika u Microsoft-ovom IIS 5.0 (engl. Internet Information Service), a 2003. godine SQL Slammer crv je ugrožavao strojeve  na kojima se pokretao Microsoft SQL Server 2000.

U srpnju 2000. otkrivena je  ranjivost na napad prekoračenja kapaciteta međuspremnika kod Microsoft Outlook-a i Outlook Express-a. Programska pogreška učinila je mogućim napadaču da ugrozi integritet ciljanog računala tako da mu jednostavno pošalje e-mail poruku. Za razliku od tpičnih e-mail virusa, korisnici se nisu mogli zaštititi tako što nebi otvorili priloženu datoteku; naime, korisnik nije čak ni trebao pročitati poruku da bi omogućio napad. Mehanizam zaglavlja poruke je imao grešku koja je omogućavala da pošilatelj preplavi to područje sa podacima, što mu je omogućavalo da izvršava bilokakve programe na primateljevom računalu. Zbog toga što se proces aktivirao čim bi primatelj skinuo poruku sa poslužitelja, od ove vrste bilo se vrlo teško obraniti. Microsoft je od tad napravio ''zakrpu'' koja eliminira ovu ranjivost.

LITERATURA

[<< natrag na vrh]

autor: Marijana Zelanto, 0036400264