Pinopuskurin ylivuoto - Stack buffer overflow

Ohjelmisto, joka on pino puskurin ylivuoto tai pinon puskurin ylitys tapahtuu, kun ohjelma kirjoittaa sille muistin osoitteen ohjelman kutsupino ulkopuolella tarkoitettu tietojen rakenne, joka on yleensä kiinteä pituus puskuria . Pinopuskurin ylivuotovirheet johtuvat siitä, että ohjelma kirjoittaa enemmän tietoja pinossa olevaan puskuriin kuin mitä puskurille on todella varattu. Tämä johtaa lähes aina pinon viereisten tietojen vioittumiseen, ja tapauksissa, joissa ylivuoto aiheutti vahingossa, ohjelma usein kaatuu tai toimii väärin. Pinopuskurin ylivuoto on eräänlainen yleisempi ohjelmointivika, joka tunnetaan puskurin ylivuotona (tai puskurin ylivuotona). Pinon puskurin ylitäyttö pilaa todennäköisemmin ohjelman suorituksen kuin puskurin ylitäyttö kasan päällä, koska pino sisältää kaikkien aktiivisten toimintojen kutsujen paluuosoitteet.

Pinopuskurin ylivuoto voi johtua tahallisesti osana hyökkäystä, joka tunnetaan nimellä pinon murskaus . Jos kyseinen ohjelma on käynnissä erityisoikeuksia tai hyväksyy dataa epäluotettavan verkon isännät (esim webserver ) niin vika on mahdollisia tietoturvaongelmia. Jos pinopuskuri on täynnä tietoja, jotka on lähetetty epäluotettavalta käyttäjältä, kyseinen käyttäjä voi vioittaa pinon siten, että se antaa suoritettavan koodin käynnissä olevaan ohjelmaan ja hallitsee prosessin. Tämä on yksi vanhimmista ja luotettavimmista tavoista, joilla hyökkääjät voivat saada luvattoman pääsyn tietokoneeseen.

Pinopuskurin ylivuotojen hyödyntäminen

Kaanoninen menetelmä pinopohjaisen puskurin ylivuoton hyödyntämiseksi on korvata funktion palautusosoite osoittimella hyökkääjän ohjaamiin tietoihin (yleensä itse pinoon). Tätä havainnollistaa strcpy()seuraava esimerkki:

#include <string.h>

void foo(char *bar)
{
   char c[12];

   strcpy(c, bar);  // no bounds checking
}

int main(int argc, char **argv)
{
   foo(argv[1]);
   return 0;
}

Tämä koodi ottaa argumentin komentoriviltä ja kopioi sen paikalliseen pinomuuttujaan c. Tämä toimii hyvin komentoriviargumenteissa, jotka ovat pienempiä kuin 12 merkkiä (kuten näet alla olevasta kuvasta B). Kaikki yli 11 merkkiä pitkät argumentit johtavat pinoon. (Turvallisten merkkien enimmäismäärä on yksi vähemmän kuin puskurin koko tässä, koska C-ohjelmointikielellä merkkijonot päättyvät null-tavuiseen merkkiin. Kaksitoista merkin syöttö vaatii siten kolmetoista tavua tallennettavaksi. sentinelin nollatavulla. Nolla tavu päätyi sitten korvaamaan muistipaikan, joka on yksi tavu puskurin lopussa.)

Ohjelma foo()sisältää useita tuloja:

Image
A. - Ennen kuin tiedot kopioidaan.
Image
B. - "hei" on ensimmäinen komentoriviargumentti.
Image
C. - "AAAAAAAAAAAAAAAAAAAAA \ x08 \ x35 \ xC0 \ x80" on ensimmäinen komentoriviargumentti.

Huomaa yllä olevassa kuvassa C, kun komentoriville annetaan suurempi kuin 11 tavun argumentti, joka foo()korvaa paikallisen pinon tiedot, tallennetun kehysosoittimen ja ennen kaikkea palautusosoitteen. Kun foo()palauttaa se ponnahtaa palautusosoitteen pinosta ja siirtyy kyseiseen osoitteeseen (eli alkaa käskyjen kyseisestä osoitteesta). Hyökkääjä on siis korvannut palautusosoitteen osoittimella pinopuskuriin char c[12], joka sisältää nyt hyökkääjän toimittamat tiedot. Todellisessa pinopuskurin ylivuotohyödyntämisessä "A": n merkkijono olisi sen sijaan alustalle ja halutulle toiminnolle sopiva shellcode . Jos tällä ohjelmalla oli erityisiä oikeuksia (esim. SUID -bitti asetettu toimimaan pääkäyttäjänä ), hyökkääjä voisi käyttää tätä haavoittuvuutta saadakseen pääkäyttäjän oikeudet kyseiselle koneelle.

Hyökkääjä voi myös muokata sisäisten muuttujien arvoja hyödyntääkseen joitakin vikoja. Tällä esimerkillä:

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

void foo(char *bar)
{
   float My_Float = 10.5; // Addr = 0x0023FF4C
   char  c[28];           // Addr = 0x0023FF30

   // Will print 10.500000
   printf("My Float value = %f\n", My_Float);

    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       Memory map:
       @ : c allocated memory
       # : My_Float allocated memory

           *c                      *My_Float
       0x0023FF30                  0x0023FF4C
           |                           |
           @@@@@@@@@@@@@@@@@@@@@@@@@@@@#####
      foo("my string is too long !!!!! XXXXX");

   memcpy will put 0x1010C042 (little endian) in My_Float value.
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

   memcpy(c, bar, strlen(bar));  // no bounds checking...

   // Will print 96.031372
   printf("My Float value = %f\n", My_Float);
}

int main(int argc, char **argv)
{
   foo("my string is too long !!!!! \x10\x10\xc0\x42");
   return 0;
}

Alustaan ​​liittyvät erot

Monilla alustoilla on pieniä eroja puhelupinon toteuttamisessa, mikä voi vaikuttaa tapaan, jolla pinopuskurin ylivuotohyödyntäminen toimii. Jotkut konearkkitehtuurit tallentavat puhelupinon ylätason paluuosoitteen rekisteriin. Tämä tarkoittaa, että mitään päällekirjoitettua palautusosoitetta ei käytetä ennen kuin puhelupino puretaan myöhemmin. Toinen esimerkki konekohtaisesta yksityiskohdasta, joka voi vaikuttaa hyödyntämistekniikoiden valintaan, on se, että useimmat RISC -tyyliset konearkkitehtuurit eivät salli tasaista pääsyä muistiin. Yhdistettynä kiinteään pituuteen koneen opcodeille tämä konerajoitus voi tehdä siirtymisen ESP -tekniikkaan melkein mahdottomaksi toteuttaa (ainoana poikkeuksena on, että ohjelma sisältää itse asiassa epätodennäköisen koodin, joka hyppää nimenomaisesti pinorekisteriin).

Pinot, jotka kasvavat

Pinopuskurin ylivuotojen aihepiirissä usein keskusteltu, mutta harvoin nähty arkkitehtuuri on sellainen, jossa pino kasvaa vastakkaiseen suuntaan. Tätä arkkitehtuurin muutosta ehdotetaan usein ratkaisuksi pinopuskurin ylivuoto -ongelmaan, koska mikä tahansa pinopuskurin ylivuoto, joka esiintyy samassa pinokehyksessä, ei voi korvata paluukohdistinta. Tämän väitetyn suojan lisätutkimus havaitsee sen parhaimmillaan naiiviksi ratkaisuksi. Kaikki ylivuoto, joka esiintyy puskurissa edellisestä pino -kehyksestä, korvaa edelleen palautusosoittimen ja sallii virheen hyväksikäytön. Esimerkiksi yllä olevassa esimerkissä palautusosoitinta fooei korvata, koska ylivuoto todella esiintyy pinon kehyksessä memcpy. Kuitenkin, koska puhelun aikana ylivuotava puskuri memcpysijaitsee edellisessä pinokehyksessä, paluuosoittimella memcpyon numeerisesti korkeampi muistiosoite kuin puskurilla. Tämä tarkoittaa sitä, fooettä korvaavan paluukohdistimen sijasta korvataan palautusosoitin memcpy. Tämä tarkoittaa korkeintaan sitä, että pinon kasvattaminen vastakkaiseen suuntaan muuttaa joitakin yksityiskohtia siitä, kuinka pinopuskurin ylivuotoja voidaan hyödyntää, mutta se ei vähennä merkittävästi hyödynnettävien vikojen määrää.

Suojausjärjestelmät

Vuosien mittaan on kehitetty useita ohjausvirran eheysmalleja haitallisen pinopuskurin ylivuotokäytön estämiseksi. Nämä voidaan yleensä luokitella kolmeen luokkaan:

  • Tunnista, että pinopuskurin ylivuoto on tapahtunut, ja estä siten käskyosoittimen uudelleenohjaus haitalliseen koodiin.
  • Estä haitallisen koodin suorittaminen pinosta havaitsematta suoraan pinopuskurin ylivuotoa.
  • Satunnaista muistitila siten, että suoritettavan koodin löytäminen muuttuu epäluotettavaksi.

Pinoa kanarioita

Pino -kanarioita, jotka on nimetty vastaavasti hiilikaivoksen kanarialle , käytetään tunnistamaan pinopuskurin ylivuoto ennen haittakoodin suorittamista. Tämä menetelmä toimii asettamalla pieni kokonaisluku, jonka arvo valitaan satunnaisesti ohjelman käynnistyksen yhteydessä, muistiin juuri ennen pinon palautusosoitinta. Useimmat puskurin ylivuotot korvaavat muistin alemmasta muistista suurempiin osoitteisiin, joten palautusosoittimen korvaamiseksi (ja siten prosessin hallitsemiseksi) myös kanarian arvo on korvattava. Tämä arvo tarkistetaan sen varmistamiseksi, että se ei ole muuttunut, ennen kuin rutiini käyttää pinon paluukohdistinta. Tämä tekniikka voi suuresti lisätä pinon puskurin ylivuoton hyödyntämisen vaikeutta, koska se pakottaa hyökkääjän hallitsemaan käskyosoittimen jollakin epätavallisella tavalla, kuten korruptoimalla muita tärkeitä muuttujia pinossa.

Ei -suoritettava pino

Toinen tapa estää pinopuskurin ylivuotohyödyntäminen on ottaa käyttöön pino -muisti -alueen muistikäytäntö, joka estää suorittamisen pinosta ( W^X , "Write XOR Execute"). Tämä tarkoittaa sitä, että jotta voidaan suorittaa shellcode pinoista, hyökkääjän on joko löydettävä tapa poistaa suorituksen suojaus muistista tai löydettävä tapa sijoittaa shellcode-hyötykuorma suojaamattomalle muistialueelle. Tästä menetelmästä on tulossa suositumpi nyt, kun useimpien työpöytäsuorittimien laitteistotuki ei-suorituslipulle on saatavilla.

Vaikka tämä menetelmä tekee ehdottomasti kanonisen lähestymistavan pinopuskurin ylivuotohyödyntämiseen, se ei ole ongelmaton. Ensinnäkin on tavallista löytää tapoja tallentaa shellcode suojaamattomille muistialueille, kuten kasaan, ja siksi hyvin vähän on muutettava hyväksikäytön tapaa.

Vaikka näin ei olisi, on muitakin tapoja. Kaikkein pahinta on niin sanottu paluu libc- menetelmässä kuorikoodin luomisessa. Tässä hyökkäyksessä haitallinen hyötykuorma ei lataa pinoa shellcode -koodilla, vaan asianmukaisella puhelupinolla niin, että suoritus on vakioitu kirjaston vakiokutsujen ketjuun, yleensä siten, että muistin suoritus estetään ja shellcode toimii normaalisti. Tämä toimii, koska suoritus ei oikeastaan ​​koskaan kanna itse pinoa.

Eräs vaihtoehto paluusta libc: iin on paluulähtöinen ohjelmointi (ROP), joka määrittää sarjan palautusosoitteita, joista jokainen suorittaa pienen sarjan kirsikkapoimittuja koneen käskyjä olemassa olevan ohjelmakoodin tai järjestelmäkirjastojen sisällä. päättyy paluuseen. Nämä ns. Gadgetit suorittavat kukin yksinkertaisen rekisterin käsittelyn tai vastaavan suorituksen ennen paluuta, ja niiden yhdistäminen saavuttaa hyökkääjän tavoitteet. On jopa mahdollista käyttää "palauttamatonta" paluukeskeistä ohjelmointia hyödyntämällä ohjeita tai käskyryhmiä, jotka toimivat paljolti paluukäskyn tavoin.

Satunnaistaminen

Koodin erottamisen datasta sijaan toinen lieventämistekniikka on tuoda satunnaistaminen suoritettavan ohjelman muistitilaan. Koska hyökkääjän on määritettävä, missä suoritettava koodi, jota voidaan käyttää, on joko suoritettava hyötykuorma (suoritettavalla pinolla) tai yksi on rakennettu käyttämällä koodin uudelleenkäyttöä, kuten ret2libc tai paluulähtöinen ohjelmointi (ROP). Muistin asettelun satunnaistaminen estää konseptina hyökkääjää tietämästä koodin sijaintia. Toteutukset eivät kuitenkaan tyypillisesti satunnaista kaikkea; yleensä suoritettava tiedosto ladataan kiinteään osoitteeseen, joten hyökkääjä voi käyttää tätä kiinteää muistialuetta myös silloin, kun ASLR (osoiteavaruuden asettelun satunnaistaminen) yhdistetään ei -toteutettavissa olevaan pinoon. Siksi kaikki ohjelmat tulisi koota PIE: llä (paikasta riippumattomat suoritettavat tiedostot) siten, että jopa tämä muistialue on satunnaistettu. Satunnaistamisen entropia on erilainen toteutuksesta toteutukseen, ja riittävän alhainen entropia voi sinänsä olla ongelma satunnaistetun muistitilan raa'an pakottamisen kannalta.

Merkittäviä esimerkkejä

  • Morris mato 1988 levitä osittain hyödyntämällä pinopuskurin ylivuodon Unix sormi palvelin. [1]
  • Slammer-mato vuonna 2003 levisi hyödyntämällä pinon ylivuoto vuonna Microsoftin n SQL Server. [2]
  • Blaster vuonna 2003 levisi hyödyntämällä pinon ylivuoto Microsoft DCOM palveluun.
  • Witty-mato vuonna 2004 levisi hyödyntämällä pinon ylivuoto on Internet Security Systems BlackICE Desktop Agent. [3]
  • On olemassa muutama esimerkki Wii: stä, joka sallii mielivaltaisen koodin ajamisen muokkaamattomassa järjestelmässä. "Twilight -hakkerointi", joka sisältää pitkän nimen antamisen päähenkilön hevoselle The Legend of Zelda: Twilight Princess , ja "Smash Stack" Super Smash Bros. Brawlille, johon kuuluu SD -kortin käyttäminen erityisesti valmistetun tiedoston lataamiseen pelin tason editori. Vaikka molemmat voidaan suorittaa minkä tahansa koodin, jälkimmäinen on usein yksinkertaisesti ladata Brawl itsensä muutoksin soveltaa.

Katso myös

Viitteet