Cum poate un subfluor de tampon să conducă la executarea codului la distanță pe 64 de biți?

Am următorul pseudo cod rezumat în C89 dintr-o bibliotecă server ssh care oferă acces doar la lucruri precum git-shell ( este înlocuit cu programul de rulat, deci nu este posibil să faceți altceva) :

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; } 

Iată modul în care comanda este executată mai târziu (șirul command este neschimbat după get_command()) .

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. 

Nu pot face memcpy(command,string.st.data,0) deoarece al treilea membru al memcpy are dimensiunea minimă de 1 și, în contextul meu, size_t folosește un număr întreg pe 64 de biți, nu pot efectua o depășire a bufferului, deoarece există len.

Tot ce pot face este să setez len la o valoare mai mare decât cea alocată string.st.data. Acesta este un flux de memorie tampon care îmi permite să citesc memoria nealocată.
Pot citi memoria serverului, totuși, nu văd ce date sensibile poate deține un server ssh public (în cazul meu, lista de utilizatori care poate efectua ssh este public) .

La fel, un subfluor tampon pe memcpy permite telecomanda executarea codului?

Comentarii

  • Dacă codul chiar face command[len] = 0 atunci acesta este un depășire a bufferului, deoarece indicele maxim pentru un buffer de lungime len este len-1. Alternativ, dacă codul real face un malloc(len+1) în loc de un malloc(len), atunci puteți face o depășire masivă a bufferului setând len valoarea la 0xFFFFFFFF.
  • @ThomasPornin: Nu este o eroare din partea mea, de fapt, apelează mai multe funcții de făcut acest. Să presupunem că restul este adevărat.
  • @ ThomasPornin: vorbesc despre 64 de biți, deoarece len este size_t, folosește un număr întreg de 64 de biți. Deci, nu există nicio modalitate de depășire a numărului întreg.
  • @ThomasPornin Ei alocă len+1, astfel încât setarea la 0 ar trebui să fie validă.
  • @ RoraΖ: Vorbea despre setarea string.st.len la -1.

Răspuns

În general, nu, nu se poate utiliza un flux de memorie tampon pentru executarea codului la distanță. Deoarece datele controlate de atacatori nu părăsesc niciodată spațiul alocat pentru aceasta, nu are niciodată capacitatea de a prelua fluxul de execuție al programului.

Un sub-flux de tampon are potențialul pentru alte tipuri de atacuri, cum ar fi divulgarea informațiilor (dacă programul se bazează pe conținutul original al bufferului șters de noile date).

Comentarii

  • Sau ca I văzut recent în git, transformat mai târziu într-un buffer overflow.

Răspuns

Când am scris inițial acest răspuns , se pare că am analizat câteva neînțelegeri pe care le dețineți. Aceste neînțelegeri v-ar împiedica probabil să înțelegeți răspunsul meu. Pentru a fi clar, voi face ca acest text să fie mare pentru a atrage accentul, nu pentru a fi lipsit de respect:

Mulți dintre termenii pe care îi folosiți nu înseamnă ceea ce credeți că înseamnă.

De exemplu:

  • „al treilea membru al memcpy este inexistent deoarece memcpy este o funcție, nu un struct sau union.
  • „Nu pot efectua o depășire a tamponului, deoarece există len și nu pot rămâne fără benzină, deoarece există benzină. Cerșesc Întrebare? Mi se pare o analogie potrivită, mai ales că teoretic vorbind, codul tău ar putea invoca o depășire a bufferului.
  • „Tot ce pot face este să setez len la o valoare mai mare decât cea alocată string.st.data. Acesta este un buffer underflow … „ Nu, aceasta nu este definiția unui buffer underflow. Un buffer underflow apare atunci când accesați în afara unei limite o matrice utilizând un index negativ sau un index care ar cauza Pointer aritmetic se va înfășura. Acesta din urmă poate fi posibil pentru anumite configurații (chiar și pe „sisteme pe 64 de biți”, indiferent ce înseamnă asta), dar mă îndoiesc că asta v-ați referit atunci când scriați aceste cuvinte, pentru că le-ați urmat apoi cu:
  • „… permițându-mi să citesc memoria nealocată.” Cred că probabil ai vrut să spui „memorie neinitializată”. Pentru a fi clar, cred că ai vrut să afirmi că tu „Am alocat un exces și am lăsat excesul neinițializat și poate doriți să faceți ceva în acest sens (consultați calloc sau memset).

Să luăm în considerare char *fubar = NULL; pentru o clipă … acest tip de pointer are în general un zero valoare.Dereferențierea este considerată o dereferere a indicatorului nul , dar dacă scriem ceva de genul 0["hello"] obținem același lucru ca "hello"[0] (adică "h"). Astfel, o dereferință a indicatorului nul poate fi utilizată în atacuri atunci când atacatorul controlează expresia dintre paranteze pătrate (așa cum se întâmplă în situația dvs.).

Revenind la fubar lucru; să spunem că memset(&fubar, UCHAR_MAX, sizeof fubar);, acum fubar este tot pe 1 biți, ceea ce înseamnă că ar putea fi o adresă aceasta este cea mai mare adresă pe care sistemul nostru ar putea să o găzduiască (teoretic). Ce se întâmplă dacă accesăm fubar[1]? Cum puteți accesa elementul după cea mai mare adresă? Adresă apoi înfășurată? Din punct de vedere tehnic, toate acestea sunt un comportament nedefinit, dar dacă aș numi asta pe arhitectura comună, ar fi:

  1. Revărsarea aritmetică a un pointer, care duce în …
  2. Nereferențierea indicatorului nul și / sau potențial un buffflow underflow .

un buffer underflow pe memcpy permite executarea codului la distanță?

Un buffer underflow-ul poate permite unui atacator să suprascrie pointerele funcționale situate în regiunile de memorie dinaintea matricei în cauză. Dacă acele pointeri funcționale sunt îndreptate către shellcode, acel shellcode poate să fie executate atunci când sunt „invocate mai târziu.

În această bucată de cod oarecum obscură, apare„ un risc de subfluență a tamponului atunci când int are un domeniu decât uint32_t, deoarece ntohl(string.st.len)+1 ar face ca valoarea uint32_t să fie convertită la int tip. Luați în considerare, de exemplu, dacă INT_MIN este -4294967296 (care este cu unul mai puțin decât 0 - UINT32_MAX) și INT_MAX este 4294967295 … acesta ar fi în esență un int cu 33 de biți umplutură pentru a tampona până la lățimea unui octet; neobișnuit, dar posibil. În această circumstanță, expresia ntohl(string.st.len)+1 nu este de tip uint32_t; este „de tip int și, mai degrabă decât să se întoarcă la 0 când apare depășire de număr nesemnat , probabil se înfășoară la -4294967296 atunci când apare overflow de număr semnat .

Dacă „căutați o garanție împotriva buffflow underflow , utilizați U sufix literal întreg (adică ntohl(string.st.len)+1U). Apoi, în acea situație, veți termina cu expresia fie uint32_t, fie unsigned int (în funcție de tipul care are cel mai mare domeniu).

Dacă considerați că ntohl(string.st.len) poate returna cu mai puțin decât valoarea maximă pentru acel tip nesemnat (oricare ar fi acesta), atunci len=ntohl(string.st.len)+1 va avea ca rezultat valoarea maximă, malloc(len+1) va provoca împachetarea nesemnată așa că veți ajunge să invocați malloc(0) și apoi command[len]=0 va scrie bine și cu adevărat dincolo de sfârșitul matricei. Apoi, bineînțeles, veți de asemenea să aveți o problemă cu memcpy(command,string.st.data,len); (aceasta este o depășire a bufferului).

Subfluxurile bufferului și deversările nu sunt singurul risc. Dacă nu „t verificați valoarea returnată a malloc și malloc returnează NULL apoi se poate utiliza o dereferență a indicatorului nul pentru a provoca executarea arbitrară a codului. Aceasta implică faptul că ar trebui să aveți o metodă de comunicare malloc eșuează înapoi către apelant. Aceasta implică, de asemenea, că ați putea utiliza aceeași metodă pentru a verifica și a comunica acea problemă de împachetare înapoi către apelant.

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *