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

5. Prekoračenje kapaciteta cijelog broja

5.1. Eksploatacija
5.2. Primjer

Aritmetički preljev (engl. overflow) ili jednostavnije preljev, kod digitalnih računala označava stanje koje nastaje kada aritmetička operacija proizvede rezultat koji je izvan opsega brojeva koji se mogu pohraniti u registar ili na neku memorijsku lokaciju. Na primjer, n–bitni registar može poprimiti broj iz intervala od 0 do (2 N – 1) ukoliko su brojevi prikazani prirodnim binarnim kodom (engl. Natural Binary Code, NBC) ili broj iz intervala od (–2N-1) do (2N-1 - 1), ukoliko su brojevi u registru prikazani dvojnim komplementom. Ako aritmetička operacija proizvede rezultat koji nije u tom opsegu brojeva, došlo je do preljeva. Iznos za koji je izračunata vrijednost veća od opsega brojeva, a koji se može prikazati, također se naziva preljev i on se može pohraniti na neku lokaciju u memoriji.

Prekoračenje opsega cijelog broja (eng. integeroverflow) je oblik aritmetičkog preljeva. Ako, na primjer najvećem broju, koji se može prikazati, pribrojimo neki pozitivni broj, nastat će preljev ili prekoračenje opsega cijelog broja. Neki procesori će u slučaju preljeva ući u zasićenje, tj. kada se dosegne najveći prikazivi broj, svaki pokušaj da se taj broj uveća vratit će kao rezultat taj isti, maksimalni broj. Isto vrijedi i za minimalnu vrijednost.

Primjer ovog nalazimo u računalnoj grafici ili pri obradi signala, gdje se uglavnom radi sa podacima čije vrijednosti se nalaze unutar intervala od 0 do 1 ili intervala od -1 do 1. Neka na primjer imamo sliku u nijansama sive boje, gdje 1 predstavlja crnu, a 0 bijelu boju, dok sve vrijednosti između predstavljaju razne nijanse sivila. Ako želimo npr. posvijetliti sliku zapravo množimo vrijednost svakog piksela sa određenom konstantom. Aritmetika zasičanja, dozvoljava da se vrijednost svakog piksela ''slijepo’’ pomnoži sa konstantom, bez straha da će doći do preljeva, jer svi oni pikseli čija je vrijednost veća od 1, tj. tamniji od crne, postaju crni, a svi manji od 0, tj. svijetliji od bijele jednostavno postaju bijeli.

ISO C99 norma definira sljedeće: "Izračun koji uključuje operatore bez predznaka ne može nikad uzrokovati preljev, zato što se rezultat koji se ne može prikazati određenim brojem bitova kao cijeli broj bez predznaka, reducira modulo broj koji je za jedan veći od najveće vrijednosti koja se može prikazati zadanim brojem bitova.''

Veličina registara procesora određuje opseg vrijednosti brojeva koji se mogu prikazati. Tipične veličine registara su 8, 16, 32, 64 I 128 bita. S obzirom da su cijeli brojevi (engl. integer) uvijek iste veličine, (najčešće 4 bajta u 32 bitnim procesorim), postoji neka maksimalna vrijednost koju mogu poprimiti. ISO C99 norma kaže da preljev cijelog broja uzrokuje ''nedefinirano ponašanje'', što znaći da prevoditelji koji su u skladu sa ovim standardom mogu učiniti što god žele od toga da potpuno ignoriraju preljev pa sve do toga da prekinu izvođenje programa. Većina prevoditelja ignorira preljev i pohranjuje neočekivane ili netočne rezultate.

Nedefinirano ponašanje je svojstvo nekih programskih jezika, od kojih je najpoznatiji programski jezik C, u kojima, da bi se pojednostavio tehnički opis i dopustio određen stupanj fleksibilnosti u implementaciji, specifikacija izričito ostavlja rezultat pojedinih operacija nedefiniranim (npr. dijeljnje s nulom).

U nekim situacijama program može pretpostaviti da vrijednost neke varijable uvijek poprima pozitivnu vrijednost. Ako je ta varijabla cijeli broj bez predznaka, tada će posljedica preljeva biti da taj broj poprimi negativnu vrijednost, što može dovesti do neočekivanog ponašanja, jer je prekršena početna pretpostavka da je broj pozitivan. Slično, oduzimanjem neke vrijednosti od malog prirodnog broja može za rezultat imati veliki prirodni broj, što je možda isto bilo neočekivano ponašanje.

Prekoračenja kapaciteta cijelog broja mogu biti iznimno opasna i to djelomično zbog toga što ih je nemoguće otkriti jednom kad se dogode. Ako dođe do preljeva, program ne može znati da je izračun zbog preljeva pogrešan pa nastavit će s izvođenjem kao da je sve u redu.

5.1. Eksploatacija

[<< natrag na vrh]

Pogrešan izračun može biti opasan ako se rezultat koristi za npr. određivanje veličine međuspremnika ili određivanje indeksa polja. U većini slučajeva pogreške ovog tipa se ne mogu eksploatieati jer se memorija ne prepisuje direktno, ali ponekad one mogu voditi ka drugim klasama pogrešaka kao što je na primjer prekoračenje kapaciteta međuspremnika.

No usprkos tome što ih je teško, a često i nemoguće eksploatirati, preljevi cijelog broja mogu uzrokovati neočekivano ponašanje, što nikad nije dobro kad je riječ o sigurnosti sustava.            

Pri eksploataciji se običajeno koristi cijeli broj (s predznakom ili bez) kao ulaz koji će izazvati određen oblik ranjivost sustava, tj. preljev pri pisanju ili ''curenje informacija'' pri čitanju iz privremene memorije, što se moglo spriječiti da je izvršene provjera granica.

Iako preljevi nisu jedna od češćih programskih pogrešaka i dozvoljavaju direktno prepisivanje memorije ili upravljanje tokom izvođenja programa, u nekim slučajevima je ipak moguće u ključnu varijablu pohraniti pogrešanu vrijednost što će dovesti do problema kasnije u programu.


Primjer 1. Eksploatacija trivijalne pogreške zbog širine varijable. Program preko komandne linije prima duljinu niza i sam niz koji se kopira u buffer nakon čega ispisuje duljinu niza kao short integer i sadržaj buffer-a.

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

      int main(int argc, char *argv[])
      {
         unsigned short s;
         int i;
         char buffer[80];

         if(argc < 3)
         {
             return -1;
         }

         i = atoi(argv[1]);
         s = i;

         if(s >= 80) /* [P] */
         {           
            printf("Ulazni argument mora biti < od 81!\n");
             return -1;
         }

            printf("s = %d\n", s);

            memcpy(buffer, argv[2], i);
            buffer[i] = '\0';
            printf("%s\n", buffer);

            return 0;
      }

Pogledajmo sljedeće ulaze:
   $ ./primjer41 5 hello
  s = 5
  hello

   
   $ ./primjer41 80 hello
  Ulazni argument mora biti < od 81!

   
   $ ./primjer41 65536 hello
  s = 0
  Segmentation fault (core dumped)

Duljina argumenata se uzima iz komandne linije i pohranjuje u varijablu cijelog broja. Kad se vrijednost prenese u short integer s, ukoliko je prevelika ona će se skratiti i na taj način ćemo zaobići provjeru granica kod [P] i preplaviti buffer[]. Sada možemo koristiti standardne tehnike prekoračenja međuspremnika na stogu da bi eksploatirali proces.

Ako inicijaliziramo varijablu tipa cijelog broja s predznakom na najveću pozitivnu vrijednost koju može poprimiti, pri povećanju vrijednosti, najznačajniji bit će se postaviti i cijeli broj će postati negativan. Zbrajanje nije jedina operacija koja može uzrokovati preljev. Gotova svaka operacija koja mijenja vrijednost varijable može uzrokovati preljev.

Kod zbrajanja i množenja rezultat aritmetičke operacije je prevelik da bi se prikazao cjelobrojnom varijablom pa se smanjuje po modulu za jedan većem maksimalnog od broja. Kod oduzimanja se u varijablu pokušava pohraniti vrijednost manja od najmanje cjelobrojne vrijednosti varijable, što uzrokuje omatanje (engl. wrap around) i umjesto negativne rezultat poprima pozitivnu vrijednost. Na ovaj način možemo natjerati zbrajanje da oduzima, množenje da dijeli ili oduzimanje da zbraja.

Jedan od najčešćih načina na koji se aritmetički preljevi mogu eksploatirati je kad se izračun odnosi na to koliko velik treba biti međuspremnik za koji rezerviramo memoriju.  Često program treba zauzeti prostor u memoriji za polje objekata, pa koristi funkcije poput malloc() ili calloc() da bi rezervirao prostor i računa koliki bi taj prostor trebao biti tako da pomnoži broj elemenata sa veličinom objekta.  Kao što je pokazano prethodno, ako možemo kontrolirati bilokoji od operanada (broj elemenata ili veličinu objekta) lako postižemo pogrešku u izračunu zbog koje će se zauzeti pogrešna količina memorijskog prostora, kao što je prikazano u sljedećem programskom odsječku:

    int mojaFunkcija(int *array, int duljina)
     {
          int *myArray, i;

          myArray = malloc(duljina * sizeof(int)); /* [1] */
         
          if(myarray == NULL)
          {
                return -1;
          }

          for(i = 0; i < duljina; i++) /* [2] */
          {             
                myarray[i] = array[i];
          }

          return myarray;
    }

Ova naizgled bezazlena funkcija može biti uzrokom propasti sustava zboga što ne obavlja provjeru duljine parametara duljina. Množenje kod [1] će prouzročiti preljev ukoliko je parametar duljina dovoljno velik. Koristeći preljev napadač će namjestiti duljinu polja myArray tako da petlja kod [2] prepiše memoriju iza granice kraja međuspremnika myArray, što će izazvati prekoračenje u gomili (heap overflow). Ovo može utjecati na izvođenje proizvoljnog programskog kôda na pojedinim implementacijama tako što prepiše malloc kontrolne strukture.

5.2. Primjer

[<< natrag na vrh]

U svibnju 2007. uočen je sigurnosni nedostatak kod programskog paketa GIMP. GIMP (GNU Image Manipulation Program) je besplatan paket otvorenog koda, distribuiran pod GPL (eng. GNU General Public License) licencom, namijenjen obradi rasterske grafike na računalima.

Sigurnosni nedostatak paketa GIMP moguče je iskoristiti za nanošenje štete korisnicima. Radi se o prepisivanju programske gomile uzrokovanom pojavom pogreške prepisivanja cjelobrojne varijable (engl. integer overflow). Neodgovarajuća implementacija kôda nalazi se unutar funkcije "seek_to_and_unpack_pixeldata()" koja je sastavnica "plug-ins/common/psd.c" datoteke izvornog kôda, a namijenjena je obradi PSD (eng. Photoshop Document) slikovnih datoteka. Posebnim oblikovanjem takve datoteke napadaču je omogućeno pokretanje proizvoljnog programskog kôda na ranjivom računalu. Za uspješno iskorištavanje, međutim, potrebno je najprije uvjeriti žrtvu na otvaranje zlonamjerno oblikovane datoteke. Ranjive su bile GIMP 2.2.15 i sve ranije inačice paketa.

Opisana ranjivost udaljenom napadaču omogućuje izvođenje potencijalno opasnih aktivnosti na ranjivom računalu. Razlog tomu je pogreška prepisivanja cjelobrojne varijable koja se može pojaviti za vrijeme obrade PSD datoteka. Uzrok pojavi propusta je neodgovarajuća obrada velikih iznosa vrijednosti koje opisuju duljinu ili širinu slike sadržane u PSD datoteci. Konačno, to vodi ka pojavi pogreške prepisivanja spremnika programske gomile.

LITERATURA

[<< natrag na vrh]

autor: Marijana Zelanto, 0036400264