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

4. Prekoračenje kapaciteta međuspremnika u gomili

4.1. Područje gomile
4.2. Prekoračenje kapaciteta međuspremnika
4.3. Jednostavna gomila
4.4. Eksploatacija
4.5. Primjer

Gomila (engl. heap) je potpuno binarno stablo gdje se čvorovi mogu uspoređivati nekom uređajnom relacijom (npr. <=) i gdje je bilo koji čvor u smislu te relacije veći ili jednak od svoje djece (ako postoje). Najčešće se n elemenata složi u gomilu, pa je najjednostavnije koristiti potpuno binarno stablo za prikazivanje gomile, makar bi i druga binarna stabla mogla zadovoljavati. Gomila je zapravo područje memorije koje se dinamički alocira od strane programa.

4.1. Područje gomile

[<< natrag na vrh]

Program dinamički rezervira memoriju za međuspremnike u gomili. Nakon što je programer napiše program, on ne zna koji će se podaci pohraniti u međuspremnik u gomili, već je program napisan tako da prilagodi veličinu potrebnog prostora za vrijeme izvođenja programa. Ovisno o programskom jeziku, platformi i namjeni softvera, postoji izvjestan broj funkcija koje se koriste da bi se razervirao memorijski prostor. Neke od njih su:

Pozivi ovih funkcija rezerviraju memoriju u virtualnom adresnom prostoru procesa i zatim vraćaju adresu te rezervirane memorije procesu. Nakon korištenja rezervirana memorija se mora osloboditi.

U pravilu, operacijski sustav dodjeljuje svakom procesu gomilu određene veličine, a ta se veličina može promijeniti za vrijeme izvođenja programa. Obično, knjižnica sustava koja je preslikana u adresni prostor procesa, kada je učitan, upravlja ovim postupkom i dijeli gomilu na manja područja kojima programer može povoljnije rukovati.

Ako se premaše granice memorijskog prostora na stogu koje je odredila jezgra operacijskog sustava, doći će do povrede pristupa (engl. access violation) što najčešće uzrokuje prekid izvođenja procesa. Međutim, u memorijskom prostoru gomile nema takvih granica pa ni CPU ni jezgra ne mogu prepoznati da li je došlo do prekoračenja kapaciteta unutar gomile.

Količina prostora koju jezgra treba za gomilu ovisi o tome koji se program izvodi. Izvjestan broj oblika zapisa podataka za izvršne datoteke sadrži informacije o tome kolika je očekivana veličina gomile. Formati PE i PE32 za Windows izvršne datoteke sadrže te informacije u dijelu koji se naziva ''optional header''. Taj dio sadrži informacije o tome koliko se memorije treba rezervirati i koliko je memorije potrebno odmah za stog i gomilu zajedno. Format zapisa ELF32 koji se prvenstveno koristi za Linux sadrži slične specifikacije.

Poput stoga, gomila također sadrži upravljačke podatke zajedno sa upotrebljivim podacima. Kao rezultat toga, napadi bazirani na prekoračenju kapaciteta međuspremnika u gomili u osnovi funkcioniraju na isti način kao oni na stogu: napadač može prepisati određene upravljačke podatke s namjerom da promjeni tok izvođenja programa ili da ubaci vlastiti programski kod.

4.2. Prekoračenje kapaciteta međuspremnika

[<< natrag na vrh]

Prekoračenje kapaciteta gomile (eng. heap overflow) je oblik prekoračenja kapaciteta međuspremnika do kojeg dolazi u memorijskom području za pohranu gomile. Memorija gomile se dinamički alocira za vrijeme izvođenja aplikacije, odnosno programa i u pravilu sadrži podatke programa.


Primjer 1. Prekoračenje kapaciteta međuspremnika u gomili:

   #include <stdio.h>
   #include <stdlib.h>
   #include <unistd.h>
   #include <string.h>

   #define BUFSIZE 16
   #define OVERSIZE 8    int main()
   {
      long diff;
      char *buf1 = (char *)malloc(BUFSIZE);
      char *buf2 = (char*)malloc(BUFSIZE);

      diff=(long)buf2 - (long)buf1;

      printf("buf1=%p,buf2=%p,diff=0x%x bytes\n",
                buf1, buf2,diff);       
      memset(buf2, 'A', BUFSIZE-1);
      buf2[BUFSIZE-1] = '\0';

      printf("prije: buf2=%s\n", buf2);
      memset(buf1,'B',(unsigned int)(diff+ OVERSIZE));

      printf("poslije: buf2=%s\n", buf2);

      return 0;
   }

Nakon izvođenja ovog programa, dobit nešto slično sljedećem rezultatu:
      buf1=0x804e000,buf2=0x804eff0,diff=0xff0 bytes
      prije: buf2 = AAAAAAAAAAAAAAA
      poslije: buf2 = BBBBBBBBAAAAAAA

Ovo je temelj velikog broja prekoračenja kapaciteta u gomili. Na ovaj način možemo prepisati naziv datoteke, pohranjene podatke, i sl.

Prekoračenje kapaciteta gomile ponekad koriste zlonamjerni hakeri da bi eksploatirali loše napisane programe. Ako program kopira podatke bez prethodne provjere da li veličina odgovara odredišnoj destinaciji, napadač može opskrbiti program sa blokom podataka koji je prevelik i na taj način prepisati podatke za upravljanje gomilom (metapodaci, podaci za opis podataka) blizu odredišta. Ovo omogućava napadaču da prepiše proizvoljnu memorijsku lokaciju sa malom količinom podataka. U većini okruženja, ovakav napad će omogućiti napadaču da dobije kontrolu nad izvršavanjem programa.

Eksploatacija se izvodi tako što se iskvare podaci na neki poseban način kako bi aplikacija prepisala svoje unutarnje strukture poput pokazivača povezanih listi. Standardne tehnike prekoračenja kapaciteta u gomili prepisuju dinamički alocirane i povezane memorijske strukture i koriste rezultirajuću izmjenu pokazivača da bi prepisale funkcijski pokazivač programa.

4.3. Jednostavna gomila

[<< natrag na vrh]

Svako okruženje ima vlastiti način upravljanja gomilom za vrijeme izvođenja, koji je prilagođen posebnim značajkama sustava. Program, također, može implementirati vlastiti način upravljanja memorijom.


slika 3.7. SimpleHeap kao dvostruko povezana lista

U nastavku ćemo koristiti jednostavnu gomilu kakva se vjerojatno ne može pronaći u praksi, ali će nam pomoći u razumjevanju nekih osnovnih tehnika. Ova jednostavna implementacija, koju ćemo nazvati SimpleHeap, sastoji se od dvostruko povezane liste koja sadrži rezervirane blokove memorije. Svaki memorijski blok počinje sa zaglavljem koje sadrži podatke za upravljanje: pokazivače na prethodni i sljedeći memorijski blok, veličinu rezervirane memorije i oznaku koja pokazuje da li se memorijski blok koristi ili ne. Nakon ovog dolazi stvarno memorijsko područje. U programskom jeziku C, to izgleda ovako:

     typedef struct MemBlok
      {
            struct MemBlok    *sljedeci;
            struct MemBlok    *predhodni;
            unsigned int      velicina;
            unsigned int      koristi_se;
    //Ovdje pocinje dio memorije koji mozemo koristiti
      } HeapHdr_t;

Kad se program pokrene, SimpleHeap dobije dobije dovoljno memorije od operacijskog sustava, kojom zatim upravlja samostalno. 
Pomoćna funkcija SimpleHeap_alloc() dodjeljuje memoriju tako što traži slobodan blok dovoljne veličine.


Slika 3.7.  SimpleHeap_alloc() rezervira memorijski blok potrebne veličine.

Funkcija SimpleHeap_free() oslobađa memoriju tako što u zaglavlju bloka upiše 0 u varijablu koristi_se. Ova funkcija također spaja susjedne slobodne memorijske blokove i odvaja posljednji blok iz dvostruko povezane liste.

4.4. Eksploatacija

[<< natrag na vrh]

Poput prekoračenja kapaciteta međuspremnika na stogu, napadači mogu koristiti prekoračenje granica u gomili da bi injektirali i izvršili bilokakav kôd. Naoko,  čak i bezopasne datoteke mogu skrivati vrlo opasne programe.

Pregledavanje slikovnih datoteka, na primjer, čini se kao bezopasni pothvat i ne očekujemo da na taj način možemo ''uhvatiti'' virus ili sl., jer naposlijetku, ne izvršava se nikakav programski kôd već program za prikaz slika samo prevodi podatke u slikovnoj datoteci da bi odredio boju nekoliko piksela na ekranu.

No ako program za prikaz slika nije napisan kako treba, nedopuštene strukture podatataka u slikovnoj datoteci mogu dovesti do prekoračenja međuspremnika. U najgorem slučaju, slikovna datoteka može biti vješto načinjena s namjerom da ''prokrijumčari'' strojni kôd u sustav, kojeg tada program za prikaz slika izvede.

Osnovni problem je taj, što sustav ''mješa'' varijable programa i međuspremnike zajedno sa upravljačkim podacima programa pa ako dođe do prekoračenja granica međuspremnika, postoji mogućnost da će se prepisati upravljački podaci, što može za posljedicu imati neočekivan učinak na to kako se program izvodi.

Dvije vrste pogrešaka mogu izazvati prekoračenje međuspremnika u gomili: ili je veličina rezervirane memorije stalna i napadač može osigurati da se previše podataka upiše u međuspremnik; Ili, se u izračunu, koliko treba rezervirati memorije, dogodila greška zbog toga što se temeljio na informacijama koje je dao napadač.

Za razliku od stoga, u gomili se ne mogu prepisati memorijska područja koja program koristi izravno kao adresu skoka. On može samo samo manipulirati podatke za upravljanje gomilom, što su u slučaju naše jednostavne gomile pokazivači prethodni i sljedeci te varijable velicina i koristi_se.

Da bi mogli izvršiti vlastiti kôd, napadači moraju osigurati da žrtvin sustav prepiše područje u memoriji s kojeg će se kasnije učitati adresu skoka. Ako sad pogledamo funkciju SimpleHeap_free() s ove perspektive, proces defragmentacije je omah upadljiv. Ovdje se, upravljačke informacije kojim se  može lako manipulirati koriste za operacije pisanja. Primjer je osobito u:

hdr->sljedeci->sljedeci->prethodni= hdr->sljedeci->prethodni;

Napadači može kontrolirati i vrijednost koja se treba upisati i ciljanu adresu, pod uvjetom da uspije osigurati da hdr->sljedeci pokazuje na zaglavlje koje je stvoreno prekoračenjem međuspremnika. Sad samo treba postaviti varijablu koristi_se na 0 da bi SimpleHeap izveo naredbu kad se međuspremnik otpušta. Ispravne vrijednosti u hdr -> sljedeci -> prethodni i  hdr -> slljedeci -> sljedeci -> prethodni osigurat će prepisivanje izabrane memorijske lokacija željenom vrijednošću. U stvari, sa hdr -> sljedeci napadač može kontrolirati sve daljnje dereferenciranje. U računarstvu, referenca je objekt koji sadrži informaciju koja se odnosi na podatke koji su pohranjeni negdje drugdje, za razliku od toga da sam sadrži podatke. Pristupanje vrijednosti na koju se odnosi referenca, naziva se dereferenciranje.

Spajanje slobodnih blokova stavlja željenu adresu na stog i povratna naredba na kraju funkcije SimpleHeap_free() koristi ju kao adresu na koju će skočiti. CPU tada izvede ubačeni programski kôd, i napad je bio uspješan.

Napadač može također prepisati i druge memorijske adrese poput adrese za rukovanje iznimkama na stogu ili u području podataka koji su posebno popularni. Rezultirajući exploit se naziva SEH exploit (engl. Structured Exception Handler). Dodatno, prepisivanje pokazivača na funkcije u području podataka ili adrese funkcija koje operacijski sustav pohranjuje kao kontekst procesa, također su česti oblici eksploatacije.

4.5. Primjer

[<< natrag na vrh]

Primjer opasnosti, koju prekoračenje kapaciteta gomile može predstavljati za korisnika računala, je Microsoft JPEG GDI+ ranjivost koja može omogućiti daljinjsko izvršavanje koda. Napadač koji uspješno ekspoatira ovu ranjivost može pomoću nje dobiti iste ovlasti kako i korisnik, ukoliko natjera korisnika da otvori posebno izrađenu datoteku ili da pregleda direktorij koji sadrži tu datoteku.

DieHard isključuje, ili uvelike smanjuje vjerojatnost sigurnosnih ranjivosti nazivanih memorijske greške. DieHard spriječava da se određene vrste pogrešaka uopće dogode. Također smanjuje mogućnost da će greška u programu imati ikakav učinak. DieHard radi tako da slučajno određuje memorijske lokacije za programskie objekte tako da budu daleko jedan od drugog u memoriji. Ovakvo raspršenje memorijskih objekata po cijelom memorijskom području uvelike smanjuje mogućnost događanja nekih grešaka i čini gotovo nemogućim otkrivanje gdje se u memoriji nalaze ranjivi dijelovi programa. Na ovaj način moguće je spriječiti veliki broj exploita.

Mnogi sustavi danas sprječavaju jednostavne exploit-e tako što obavljaju dodatna ispitivanja. Na primjer, povjeravaju da su pokazivači u gomili nepromjenjeni, prije nego ih koriste, tj. provjeravaju da li prethodni blok pokazuje na trenutni, npr:

if ( this_block->prev-> next == this_block )

No, nitijedna mjera provjere ne može u potpunosti osigurati da neće doći do prekoračenja kapaciteta međuspremnika.

Početkom 2004. godine otkriven je sigurnosni propust koji se odnosi na pogrešku prepisivanja gomile unutar shimgvw.dll sistemske knjižnice koju Windows Explorer koristi prilikom pregledavanja grafičkih datoteka. Spomenuti nedostatk bilo je moguće iskoristiti oblikovanjem zlonamjerne .emf (engl. Enhanced Metafile) grafičke datoteke, nakon čega je napadač dolazio do mogućnosti izazivanja DoS stanja te izvođenja proizvoljnog programskog kôda na ranjivom sustavu.

Sigurnosni nedostatak primjećen je na Windows XP operacijskim sustavima inačica Home, Home SP1, Media Center Edition, Professional te Professional SP1. Ukoliko je .emf grafička datoteka u atributu "total size" sadržavala vrijednost  koja je bila manja od veličine zaglavlja dolazilo je do klasičnog prepisivanja gomile te rušenja  explorer.exe procesa.

LITERATURA

[<< natrag na vrh]

autor: Marijana Zelanto, 0036400264