Buffer overflow: cauze, soluții eficiente și protecția necesară

Toți programatorii sunt conștienți de amenințarea potențială a depășirii bufferului în programele lor. Există multe amenințări legate de aceasta atât în software-ul nou, cât și în cel vechi, indiferent de numărul de remedieri. Atacatorii pot profita de acest bug prin injectarea de cod special conceput pentru a provoca o depășire a setului inițial de date, apoi prin scrierea restului la o adresă de memorie contiguă cu adresa de depășire.

Datele pot conține cod executabil care permite atacatorilor să execute programe mai mari și mai complexe sau să le ofere acces la sistem. Erorile sunt foarte greu de găsit și de corectat, deoarece codul software este format din milioane de linii. Corectarea acestor erori este destul de dificilă și, la rândul său, este predispusă la erori, ceea ce face dificilă.

Detectarea depășirii de buffer

Detectarea depășirii de buffer

Înainte de a căuta o revărsare, trebuie să știți, ce reprezintă. După cum sugerează și numele, aceste vulnerabilități sunt legate de tampoane sau de alocarea memoriei în limbaje care oferă acces direct la nivel scăzut la citire și scriere.

În limbajele C și Assembler, citirea sau scrierea unor astfel de alocări nu implică verificarea automată a limitelor. Prin urmare, în cazul în care se detectează o depășire a bufferului de stivă într-o anumită aplicație, nu se verifică dacă un număr de octeți poate fi plasat în bufferul în cauză. În astfel de cazuri, programul își poate "supraîncărca" capacitatea. Acest lucru face ca datele scrise după umplere să suprascrie conținutul adreselor ulterioare din stivă și să citească date suplimentare. Depășirea poate avea loc în mod neintenționat din cauza erorilor utilizatorului.

Uneori este cauzată de o entitate rău intenționată care trimite date de intrare rău intenționate, atent elaborate, unui program, care apoi încearcă să le salveze într-un buffer insuficient. Dacă în această aplicație se detectează o depășire a bufferului de stivă, datele în exces sunt scrise în bufferul adiacent, unde suprascriu orice date existente.

Acestea conțin în mod normal un pointer de întoarcere la funcția exploatată - adresa la care procesul trebuie să sară în continuare. Un atacator poate seta noile valori pentru a indica o adresă la alegere. Un atacator setează de obicei noi valori pentru a indica unde se află încărcătura utilă. Acest lucru modifică calea de execuție a procesului și oferă instantaneu controlul codului malițios.

Buffer overflow permite unui atacator să controleze sau să încheie un proces sau să modifice variabilele sale interne. Aceasta este una dintre primele 25 cele mai periculoase erori de programare din lume (2009 CWE/SANS Top 25 Most Dangerous Programming Errors) și este listată ca CWE-120 în Dictionary of System Vulnerabilities. Deși bine cercetate, ele continuă să afecteze programele populare.

Vector simplu de utilizare a bufferului

Atunci când se lucrează cu codul sursă, trebuie să se acorde o atenție specială la locurile în care sunt utilizate și modificate tampoanele. O mențiune specială trebuie făcută pentru funcțiile referitoare la datele de intrare furnizate de utilizator sau de o altă sursă externă, deoarece acestea oferă un vector simplu de utilizat atunci când este detectată o depășire a bufferului de stivă. De exemplu, atunci când un utilizator pune o întrebare de tip "da" sau "nu", este adecvat să se stocheze datele șirului de caractere ale utilizatorului într-o mică memorie tampon pentru șirul "da", așa cum se arată în următorul exemplu.

Vector simplu de utilizare a bufferului

Dacă ne uităm la cod, putem vedea că verificarea limitelor nu este efectuată. Dacă utilizatorul introduce "poate", programul se va bloca în loc să ceară un răspuns, care este scris în memoria tampon indiferent de lungimea acestuia. În acest exemplu, deoarece răspunsul utilizatorului este singura variabilă declarată, următoarele valori de pe stivă vor fi valoarea adresei de întoarcere sau locația de memorie la care se va întoarce programul după executarea funcției ask question.

Aceasta înseamnă că, dacă utilizatorul introduce patru octeți de date, ceea ce este suficient pentru a depăși bufferul de comandă al clientului, va urma o adresă de retur validă, care va fi modificată. Acest lucru va face ca programul să iasă din funcție într-un alt punct al codului decât cel prevăzut inițial și poate face ca software-ul să se comporte într-un mod periculos și neintenționat.

Dacă primul pas pentru a detecta depășirile de buffer în codul sursă este de a înțelege cum funcționează, al doilea pas este de a examina intrările externe și manipularea bufferului, al treilea pas este de a afla ce funcții sunt vulnerabile la această vulnerabilitate și care pot acționa ca "steaguri roșii". Funcția gets este foarte bună pentru a scrie în afara buffer-ului care îi este furnizat. De fapt, această calitate se extinde la întreaga familie de funcții conexe, inclusiv strcpy, strcmp și printf/sprintf, ori de câte ori este utilizată una dintre aceste funcții de vulnerabilitate la depășirea limitei de capacitate.

Eliminarea din baza de cod

În cazul în care se detectează o depășire a bufferului de stivă în codul sursă, se efectuează o eliminare consecventă a. Trebuie să fiți familiarizat cu metodele de lucru sigure. Cel mai simplu mod de a preveni aceste vulnerabilități este de a folosi un limbaj care nu le permite. Limbajul C are aceste vulnerabilități din cauza accesului direct la memorie și a lipsei unei tipăriri stricte a obiectelor. Limbile care nu împărtășesc aceste aspecte nu sunt, în general, vulnerabile. Acestea sunt Java, Python și .NET, împreună cu alte limbaje și platforme care nu necesită verificări sau modificări speciale.

Desigur, nu este întotdeauna posibil să se schimbe complet limbajul de dezvoltare. În acest caz, se folosesc metode sigure să lucreze cu depășirea tamponului de comandă. În cazul funcțiilor de manipulare a șirurilor de caractere, s-a discutat mult despre ce metode sunt disponibile, care sunt sigure de utilizat și care ar trebui evitate. Funcțiile strcpy și strcat copiază un șir de caractere în buffer și adaugă conținutul unuia la celălalt. Aceste două metode au un comportament nesigur deoarece nu verifică limitele buffer-ului țintă și scriu în afara limitelor dacă există suficienți octeți pentru a face acest lucru.

Securitate alternativă

O alternativă frecvent sugerată este reprezentată de versiunile legate, care scriu la dimensiunea maximă a bufferului țintă. La prima vedere, aceasta pare a fi o soluție ideală. Din nefericire, aceste funcții au o mică ciudățenie care cauzează probleme. Atunci când se atinge limita, dacă caracterul de terminare nu este plasat în ultimul octet, se produce o gravă eșecuri la citirea tamponului.

Protecție alternativă

Acest exemplu simplificat arată pericolele pe care le prezintă șirurile de caractere care nu se termină în zero. Atunci când foo este plasat în bufferul normal, se termină cu zero, deoarece are spațiu în plus. Acesta este cel mai bun scenariu. În cazul în care octeții din depășirea buffer-ului de pe stivă se află într-un alt șir de caractere sau într-un alt șir imprimabil, funcția de imprimare va continua citirea până când se ajunge la ultimul caracter al acelui șir de caractere.

Dezavantajul este că limbajul C nu oferă o alternativă standard și sigură la aceste funcții. Cu toate acestea, există un beneficiu secundar pozitiv al implementărilor multiple specifice fiecărei platforme în parte. OpenBSD oferă funcțiile strlcpy și strlcat, care funcționează în mod similar cu funcțiile strn, cu excepția faptului că trunchiază șirul cu un caracter mai devreme pentru a face ca spațiu pentru terminator zero.

În mod similar, Microsoft oferă propriile implementări sigure ale funcțiilor de manipulare a șirurilor de caractere utilizate în mod obișnuit: strcpy_s, strcat_s și sprintf_s.

Utilizarea alternativelor sigure enumerate mai sus este preferabilă la. În cazul în care acest lucru nu este posibil, pentru procesarea bufferului de șiruri de caractere se implementează verificarea manuală a limitelor și terminarea nulă.

Vulnerabilități de compilare

Vulnerabilități de compilare

În cazul în care o funcție nesigură lasă deschisă posibilitatea unei depășiri de buffer C, nu este totul pierdut. La rularea unui program, compilatorii creează adesea valori aleatorii, cunoscute sub numele de canari, și le pun pe stivă, astfel încât acestea sunt periculoase. Verificarea valorii unui canar în comparație cu valoarea sa originală poate determina dacă a avut loc o depășire a bufferului Windows. Dacă valoarea a fost modificată, programul va fi închis sau va trece la o stare de eroare în locul adresei de retur potențial modificate.

Unele sisteme de operare moderne oferă protecție suplimentară împotriva depășirilor de memorie tampon sub forma unor stive neexecutabile și a randomizării alocării spațiului de adrese (ASLR). Stive neexecutabile - Data Execution Prevention (DEP) - marchează stiva și, în unele cazuri, alte structuri, ca zone în care codul nu va fi executat. Acest lucru înseamnă că un atacator nu poate injecta cod de exploatare în stivă și să se aștepte ca acesta să se execute cu succes.

Înainte de a fixa deblocarea buffer overflow unpack pe PC ASLR. Acesta a fost conceput pentru a proteja împotriva programării orientate spre retur, ca o soluție pentru stive neexecutabile, în care fragmente de cod existente sunt înlănțuite împreună pe baza adreselor lor.

Funcționează prin randomizarea zonelor de memorie ale structurilor, astfel încât este mai dificil de determinat distanțele dintre acestea. Dacă această protecție ar fi existat la sfârșitul anilor 1980, viermele Morris ar fi putut fi evitat. Acest lucru se datorează faptului că funcționează prin umplerea parțială a unui buffer în exploit-ul de cod UNIX finger finger și apoi prin supraîncărcarea acestuia pentru a schimba adresa de întoarcere și pentru a arăta spre buffer-ul umplut.

ASLR și DEP îngreunează identificarea exactă a adresei de specificat, ceea ce face ca acea regiune de memorie să fie complet inoperabilă. Uneori, o vulnerabilitate se strecoară printre fisurile deschise la un atac de tip buffer overflow, în ciuda prezenței controalelor la nivel de dezvoltare, compilator sau sistem de operare.

Analiza de acoperire statică

Într-o situație de debordare, există două sarcini decisive. În primul rând, trebuie identificată vulnerabilitatea și trebuie identificată baza de cod pentru rezolvarea problemelor. În al doilea rând, asigurați-vă că toate versiunile de cod de vulnerabilitate la debordarea de tampon sunt înlocuite. În mod ideal, aceasta va începe cu actualizări automate ale tuturor sistemelor conectate la internet.

Nu se poate presupune că o astfel de actualizare va oferi o acoperire suficientă. Organizațiile sau persoanele fizice pot utiliza software-ul pe sisteme cu acces limitat la internet care necesită actualizări manuale. Acest lucru înseamnă că știrile despre actualizare trebuie distribuite tuturor administratorilor care ar putea utiliza software-ul, iar patch-ul trebuie să fie ușor de descărcat. Crearea și distribuirea patch-urilor ar trebui să se realizeze cât mai aproape posibil de descoperirea vulnerabilității, minimizând astfel timpul de vulnerabilitate.

Prin utilizarea unor caracteristici de manipulare sigură a tampoanelor și a unor caracteristici de securitate adecvate ale compilatorului și sistem de operare se poate construi o protecție robustă împotriva depășirilor de memorie tampon. Ținând cont de acești pași, identificarea consecventă a defectelor este un pas crucial pentru a preveni o exploatare.

Combinarea liniilor de cod sursă în căutarea unor potențiale amenințări poate fi plictisitoare. De asemenea, există întotdeauna posibilitatea ca ochiul uman să nu observe ceva important. Instrumentele de analiză statică sunt folosite pentru a asigura calitatea codului, au fost dezvoltate special pentru a detecta vulnerabilitățile de securitate în timpul dezvoltării.

Analiza de acoperire statică stabilește "semne roșii" pentru potențiale depășiri. Acestea sunt apoi procesate și corectate separat, astfel încât să nu fie căutate manual în baza de date. Aceste instrumente, combinate cu verificări periodice și cunoștințe despre cum se pot remedia depășirile de capacitate, permit ca marea majoritate a defectelor să fie identificate și remediate înainte ca dezvoltarea software-ului să fie completă.

Executarea atacului prin root

Erorile de codare sunt, de obicei, cauza unei depășiri. Erori comune în dezvoltarea aplicațiilor, care pot duce la aceasta, includ incapacitatea de a aloca tampoane suficient de mari și lipsa unui mecanism de verificare a acestor probleme. Astfel de erori sunt deosebit de problematice în limbajele C/C++, care nu dispun de protecție integrată împotriva depășirilor și sunt adesea ținta atacurilor de tip buffer overflow.

În unele cazuri, un atacator injectează cod malițios în memoria care a fost coruptă de o depășire a bufferului de stivă. În alte cazuri, pur și simplu profitând de corupția memoriei adiacente. De exemplu, un program care solicită utilizatorului o parolă pentru a-i permite accesul la sistem. În codul de mai jos, parola corectă oferă privilegii de root. Dacă parola este greșită, programul nu acordă privilegii utilizatorului.

Software-ul nu oferă privilegii de utilizator

În exemplul de mai sus, acesta va acorda privilegii de root chiar dacă utilizatorul a introdus o parolă invalidă. În acest caz, atacatorul furnizează o intrare care este mai lungă decât poate conține bufferul, creând o depășire, suprascriind memoria pasului de întreg. Prin urmare, în ciuda unei parole invalide, valoarea de trecere devine diferită de zero, iar atacatorul obține privilegii de root.

Atac zona de depozitare temporară

Buffer-ul este o zonă temporară de stocare a datelor. Atunci când un program sau un proces de sistem plasează mai multe date decât cele alocate inițial pentru stocare, datele suplimentare se revarsă. Acest lucru face ca o parte din ea să se scurgă într-un alt buffer, corupând sau suprascriind datele.

Într-un atac de tip overflow, datele suplimentare conțin instrucțiuni speciale pentru acțiuni intenționate de un hacker sau de un utilizator rău intenționat, de exemplu, declanșează un răspuns care corupe fișiere, modifică date sau dezvăluie informații personale.

Un atacator folosește un exploit de tip overflow pentru a profita de un program care așteaptă intrarea utilizatorului. Există două tipuri de depășire a bufferului: pe bază de stivă și pe bază de heap. Cele bazate pe heap sunt greu de executat și sunt cele mai puțin frecvente, în timp ce atacă aplicația prin umplerea spațiului rezervat pentru.

Stiva este spațiul de memorie utilizat pentru a stoca datele utilizatorului intrare. Acest tip de depășire este mai probabil să fie întâlnit de un atacator care exploatează aplicații.

Compilatoarele moderne oferă, de obicei, o opțiune pentru a verifica dacă există depășiri de capacitate la compilare/compilație, dar la execuție este destul de dificil de verificat această problemă fără un mecanism suplimentar de protecție pentru tratarea excepțiilor.

Efectuarea unui atac prin root

Opțiuni de program:

  1. Intrare: 12345678 (8 octeți), programul rulează fără probleme.
  2. Introduceți: 123456789 (9 octeți), apare mesajul "Eroare de segmentare", programul se termină.

Vulnerabilitatea există din cauza depășirii de capacitate dacă intrarea utilizatorului argv depășește 8 octeți. Pentru un sistem pe 32 de biți (4 octeți), umpleți memoria cu un cuvânt dublu (32 de biți). Dimensiunea caracterului este de 1 octet, deci dacă solicitați un buffer cu 5 octeți, sistemul va aloca 2 cuvinte duble (8 octeți). De aceea, dacă introduceți mai mult de 8 octeți, Buffer-ul se va supraîncărca.

Există funcții standard similare care sunt mai puțin vulnerabile din punct de vedere tehnic. De exemplu, strncpy (), strncat () și memcpy (). Problema cu aceste funcții este că, această responsabilitate este la latitudinea programatorului, nu a compilatorului, să determine dimensiunea buffer-ului.

Fiecare programator C/C++ ar trebui să cunoască problema înainte de a începe să codifice. Multe probleme generate pot fi protejate de depășire în majoritatea cazurilor.

Pericole în C/C++

http://blogs.grammatech.com/eliminating-the-danger-of-uninitialized-variables

Utilizatorii C ar trebui să evite utilizarea funcțiilor periculoase care nu verifică limitele, cu excepția cazului în care sunt siguri că limitele nu vor fi depășite. Funcțiile care ar trebui evitate în majoritatea cazurilor pentru a oferi protecție includ strcpy. Acestea ar trebui să fie înlocuite cu funcții precum strncpy. Utilizarea funcțiilor strlen ar trebui evitată dacă utilizatorul este sigur că se va găsi un caracter NIL final. familia scanf (): scanf (3), fscanf (3), sscanf (3), vscanf (3), vsscanf (3) și vfscanf (3) este periculoasă și nu ar trebui utilizată pentru a trimite date într-un șir de caractere fără un control al lungimii maxime, "format% s" este un eșec deosebit de frecvent.

Oficial, snprintf () nu este o funcție standard C în clasificarea ISO 1990. Aceste sisteme nu se protejează împotriva depășirilor de buffer, ci doar apelează direct sprintf. Se știe că versiunea actuală a lui Linux snprintf funcționează corect, adică respectă de fapt granița. Valoarea de retur a snprintf () se modifică de asemenea.

Versiunea 2 a specificației Unix (SUS) și standardul C99 diferă prin faptul că returnează snprintf (). Unele versiuni ale snprintf nu garantează că șirul se va termina cu NIL, iar dacă șirul este prea lung, nu va conține deloc NIL. Biblioteca glib are g_snprintf () cu o semantică de returnare consistentă, se termină întotdeauna cu NIL și, cel mai important, respectă întotdeauna lungimea bufferului.

Depășirea tamponului portului de comunicare

Depășirea tamponului portului de comunicare

Uneori, un port serial raportează un buffer de depășire. Această problemă poate fi cauzată de mai mulți factori. Printre acestea se numără viteza calculatorului, viteza de transmisie a datelor utilizate, dimensiunea FIFO a portului serial și dimensiunea FIFO a dispozitivului care trimite date către portul serial.

Controlul fluxului va aștepta până când un anumit număr de octeți se află în buffer înainte ca procesorul să trimită un mesaj sau un semnal către un alt dispozitiv pentru a opri transferul. La viteze de transmisie mai mari, portul serial va primi câțiva octeți din momentul în care nivelul de control al fluxului din buffer este atins și dispozitivul nu mai transmite.

Acești octeți în plus vor fi mai mulți dacă procesul cu prioritate mare controlează procesorul țintă în timp real. Deoarece procesul de depășire a buffer-ului portului de comunicație are o prioritate mai mare decât întreruperea VISA, procesorul nu va lua nici o măsură până când acesta nu se va finaliza în timp real.

Setarea implicită VISA și Windows pentru un FIFO de 16 octeți este de 14 octeți, ceea ce lasă 2 octeți în FIFO atunci când dispozitivul încearcă să trimită un mesaj de la sursă. La viteze de transmisie mai mari, este posibil ca calculatoarele mai lente să primească mai mult de 4 octeți la un moment dat, când portul serial cere procesorului să oprească transmisia.

Pentru a rezolva problema atunci când este detectată o depășire a bufferului de stivă în Windows 10, trebuie să deschideți Managerul de dispozitive. Apoi găsiți portul COM pentru care modificați setările și deschideți proprietățile acestuia. Apoi faceți clic pe fila "Avansat", va apărea un cursor, care va schimba dimensiunea depășirii clipboard-ului, astfel încât UART va activa mai repede controlul fluxului.

Valoarea implicită este suficientă în majoritatea cazurilor. Cu toate acestea, în cazul în care apare o eroare de depășire a bufferului, reduceți valoarea. Acest lucru va cauza mai multe întreruperi care vor fi trimise la procesor cu octeți încetiniți în UART.

Metode de dezvoltare sigure

Metode de dezvoltare sigure

Practicile de dezvoltare securizată includ testarea regulată pentru a detecta și corecta depășirile de capacitate. Cel mai fiabil o modalitate de a evita sau de a preveni acest lucru este utilizarea protecției automate la nivel de limbă. O altă soluție este verificarea limitelor în timpul execuției, care previne depășirea limitelor prin verificarea automată a faptului că datele scrise în buffer se încadrează în limitele acceptabile.

Serviciul de cloud Veracode identifică vulnerabilitățile de cod, cum ar fi depășirile de buffer, astfel încât dezvoltatorii să le remedieze înainte ca acestea să fie exploatate. Tehnologia de testare a securității aplicațiilor statice binare (SAST), unică în industrie și patentată de Veracode, analizează aplicațiile, inclusiv componentele open-source și ale terților, fără a fi nevoie să le acceseze.

SAST completează modelarea amenințărilor și revizuirile de cod efectuate de dezvoltatori prin detectarea erorilor și omisiunilor din cod mai rapid și la un cost mai mic prin automatizare. De obicei, se lansează la începutul ciclului de viață al dezvoltării de software, deoarece este mai ușor și mai ieftin să se rezolve problemele înainte de a intra în producție.

SAST identifică vulnerabilitățile critice, cum ar fi injecția SQL, cross-site scripting (XSS), eroare de depășire a bufferului, condiții de eroare brută și potențiale colțuri. În plus, tehnologia binară SAST oferă informații utile care prioritizează problemele în funcție de gravitate și oferă instrucțiuni detaliate privind remedierea.

Vulnerabilitatea de debordare a bufferului a fost în jur de aproape 3 decenii, dar este încă împovărătoare. Hackerii din întreaga lume continuă să o considere tactica lor implicită din cauza numărului mare de aplicații web sensibile. Dezvoltatorii și programatorii depun mult efort pentru a combate acest flagel IT, găsind tot mai multe metode.

Ideea de bază din spatele acestei din urmă abordări este de a implementa un instrument de remediere care face mai multe copii ale adresei de returnare în stivă și apoi randomizează locația tuturor copiilor, în plus față de numărul de. Toate duplicatele sunt actualizate și verificate în paralel, astfel încât orice discrepanță între ele indică o posibilă tentativă de atac și declanșează o excepție.

Articole pe această temă