Minulla on seuraava yhteenveto pseudokoodi C89: ssä ssh-palvelinkirjastosta, joka tarjoaa pääsyn vain esimerkiksi git-shelliin (/bin/bash
korvataan suoritettavalla ohjelmalla, joten muuta ei voi tehdä) :
struct _raw_uncapped_ssh_string { // no limit on the size of the string; uint32_t len; char non_null_terminated_string[]; // by protocol design it have a minimum length of 1 }; typedef struct _raw_uncapped_ssh_string raw_ssh_string; union buffer { void * uncapped_zlib_decompressed_network_data; // yes, the size is uncapped, so it’s possible to put 4Gb of // data in it that would be copied later into memory. zlib // allow easily to turn some Mb in Gb of data, but it’s not // the point of the question. raw_ssh_string st; }; get_command (compressed_network_data) { size_t len; char * command; buffer string=uncompress_to_buffer(compressed_network_data); len=ntohl(string.st.len)+1; command=malloc(len+1); command[len]=0; // here’s the point, both the string length and content as // well it’s supplied size is controlled by the attacker. memcpy(command,string.st.data,len); return command; }
Tässä on miten komento suoritetaan myöhemmin (merkkijono command
ei muutu get_command()
) jälkeen.
const char *args[]={"/bin/bash",command,NULL}; // /bin/bash isn’t the shell, it has been replaced by git‑shell. // redirect the program output to the network. dup2(stdin, 0); dup2(stdout,1); dup2(stdout,2); close(stdin); close(stdout); //if this return execution failed and print an error message return execv(args[0],(char * const *)args); // I don’t know which is the system, so I can’t know about the libc behaviour.
En voi tehdä memcpy(command,string.st.data,0)
, koska ryhmän memcpy
kolmannella jäsenellä on pienin koko 1, ja kontekstissani size_t
käyttää 64-bittistä kokonaislukua, en voi suorittaa puskurin ylivuotoa, koska len
.
Voin vain asettaa len
arvoon, joka on suurempi kuin arvo, joka on varattu ryhmälle string.st.data
. Tämä on puskurin alivuoto, jonka avulla voin lukea kohdistamatonta muistia.
Voin lukea palvelimen muistia, mutta en näe, mitä arkaluontoisia tietoja julkinen ssh-palvelin voi pitää sisällään (minun tapauksessani luettelo käyttäjistä, jotka voi suorittaa ssh on julkinen) .
Joten puskurin alivirta memcpy
sallii etäyhteyden koodin suorittaminen?
Kommentit
Vastaus
Yleensä ei, puskurin alivuotoa ei voida käyttää koodin etäsuorittamiseen. Koska hyökkääjän hallitsema data ei koskaan jätä sille varattua tilaa, sillä ei koskaan ole kykyä ottaa haltuunsa ohjelman suoritusta. tietojen paljastaminen (jos ohjelma luottaa siihen, että uudet tiedot pyyhkivät puskurin alkuperäisen sisällön).
Kommentit
- Tai kuten minä näki äskettäin gitissä, muuttuu myöhemmin puskurin ylivuotoksi.
Vastaa
Kun kirjoitin tämän vastauksen alun perin , näyttää siltä, että olen hämmentänyt joitain väärinkäsityksiä, joita pidät. Nämä väärinkäsitykset estäisivät sinua todennäköisesti ymmärtämästä vastaustani. Selvyyden vuoksi aion tehdä tästä tekstistä suuren korostettavaksi, olematta epäkunnioittavaa:
Monet käyttämäsi termit eivät tarkoita sitä, mitä luulet niiden mielestä tarkoittavan.
Esimerkiksi:
- ”
memcpy
” kolmatta jäsentä ei ole, koskamemcpy
on funktio, eistruct
taiunion
. - ”En voi suorittaa puskurin ylivuotoa, koska siellä on
len
” enkä bensiini voi loppua, koska bensiiniä on. Kerronko kysymys? Minusta tuntuu sopivalta analogialta, varsinkin kun teoreettisesti puhuessasi koodi voisi kutsua puskurin ylivuotoa. - ”Voin vain asettaa
len
arvoon, joka on suurempi kuin arvo, joka on varattu ryhmällestring.st.data
. Tämä on puskurin alivuoto … ” Ei, se ei ole puskurin alivirran määritelmä. Puskurin alivuoto tapahtuu, kun pääset matriisin ulkopuolelle negatiivisen indeksin tai indeksin avulla, joka aiheuttaisi jälkimmäinen saattaa olla mahdollista joillekin kokoonpanoille (jopa ”64-bittisissä järjestelmissä”, mitä se tarkoittaa), mutta epäilen, että tarkoitit tätä kirjoittaessasi näitä sanoja, koska seurasit niitä kanssa: - ”… antaa minulle mahdollisuuden lukea kohdentamatonta muistia.” Luulen, että tarkoitit ehkä ”alustamatonta muistia”. Selkeyden vuoksi mielestäni tarkoitit sanoa, että ”olemme jakaneet ylimäärän ja jättäneet ylimäärän alustamattomaksi , ja ehkä haluat tehdä asialle jotain (katso
calloc
taimemset
… tällaisella osoittimella on yleensä nolla arvo.Sen poikkeamista pidetään nollaosoittimen poikkeamana , mutta jos kirjoitamme jotain0["hello"]
, saamme saman kuin"hello"[0]
(eli"h"
). Täten nollaosoittimen poikkeamista voidaan käyttää hyökkäyksissä, kun hyökkääjä hallitsee neliönmuotoisten aaltosulkeiden välistä lauseketta (kuten he tekevät tilanteessasi).
Palataan fubar
juttu; sanotaan sanotaan, että memset(&fubar, UCHAR_MAX, sizeof fubar);
, nyt fubar
on kaikki 1-bittisiä, mikä tarkoittaa, että se voi olla osoite se on suurin osoite, johon järjestelmämme voisi (teoreettisesti) mahtua. Entä jos käytämme fubar[1]
? Kuinka pääset elementtiin suurimman osoitteen jälkeen ? Teknisesti kaikki tämä on määrittelemätöntä käyttäytymistä, mutta jos nimittäisin sen yhteiselle arkkitehtuurille, se olisi:
- aritmeettinen ylivuoto osoitin, joka johtaa …
- Nolla osoittimen poikkeama ja / tai mahdollisesti puskurin alivirta .
sallii puskurin alivirta
memcpy
-palvelussa koodin etäsuorittamisen?
Puskuri alivuoto voi antaa hyökkääjälle mahdollisuuden korvata muistiosioilla olevat toiminnon osoittimet ennen kyseistä taulukkoa. Jos nämä toiminto-osoittimet tehdään osoittamaan kuorikoodiin, kyseinen kuorikoodi voi suoritetaan, kun niitä ”kutsutaan myöhemmin myöhemmin.
Tässä hieman hämärässä koodinpätkässä näyttää olevan puskurin alivuotoriski, kun int
on verkkotunnus kuin uint32_t
, koska ntohl(string.st.len)+1
aiheuttaisi uint32_t
-arvon muuntamisen int
-tyyppi. Harkitse esimerkiksi, jos INT_MIN
on -4294967296
(mikä on yksi vähemmän kuin 0 - UINT32_MAX
) ja INT_MAX
on 4294967295
… tämä olisi olennaisesti 33-bittinen int
pehmuste pehmustettavaksi tavun leveydelle; melko harvinaista, mutta mahdollista. Tässä tilanteessa lauseke ntohl(string.st.len)+1
ei ole uint32_t
-tyyppi; se ”s int
-tyyppinen ja sen sijaan, että kääritään takaisin 0: een, kun allekirjoittamaton kokonaisluvun ylivuoto tapahtuu, se todennäköisesti käärittyy -4294967296
kun allekirjoitettu kokonaisluvun ylivuoto tapahtuu.
Jos etsit takuuta puskurin alivuoto , käytä U
kokonaisluku kirjaimellinen loppuliite (ts. ntohl(string.st.len)+1U
). Silloin tässä tilanteessa lauseke on joko uint32_t
tai unsigned int
(riippuen siitä, minkä tyyppisellä on suurin verkkotunnus).
Jos katsot, että ntohl(string.st.len)
voi palauttaa yhden alle kyseisen allekirjoittamattoman tyypin enimmäisarvon (mikä se sitten onkin), niin len=ntohl(string.st.len)+1
johtaa enimmäisarvoon, malloc(len+1)
aiheuttaa allekirjoittamaton rivitys joten päädyt kutsumaan malloc(0)
ja sitten command[len]=0
kirjoittaa hyvin ja aidosti taulukon lopun jälkeen. Sitten tietysti sinulla on memcpy(command,string.st.data,len);
: n kanssa (tämä on puskurin ylivuoto).
Puskurin alivirrat ja ylivuoto ei ole ainoa riski. Jos et ”t tarkista paluuarvo malloc
ja malloc
palauttaa NULL
, jolloin null-osoittimen poikkeamaa voidaan käyttää mielivaltaisen koodin suorittamiseen. Tämä tarkoittaa, että sinulla on oltava menetelmä kommunikoida malloc
epäonnistuu takaisin soittajalle. Se tarkoittaa myös, että voit käyttää samaa tapaa tarkistaa ja välittää kääreongelma takaisin soittajalle.
command[len] = 0
niin se on puskurin ylivuoto, koska puskurin, jonka pituus onlen
, enimmäisindeksi onlen-1
. Vaihtoehtoisesti, jos varsinainen koodi tekeemalloc(len+1)
malloc(len)
: n sijasta, voit tehdä massiivisen puskurin ylivuoton asettamallalen
arvo arvoon0xFFFFFFFF
.len+1
, joten asetuksen arvoksi 0 pitäisi olla kelvollinen.string.st.len
-asetukseksi –1.