Un buffer overflow este un fenomen care apare atunci când un program de calculator scrie date în afara unui buffer alocat în memorie .
Depășirile de buffer rezultă de obicei din manipularea necorespunzătoare a datelor și memoriei primite extern, în absența unor protecții puternice din partea subsistemului de programare ( compilator sau interpret ) și a sistemului de operare . Ca urmare a unui overflow, datele situate după buffer (sau înaintea acestuia) [1] pot fi corupte .
Buffer overflow este una dintre cele mai populare moduri de a pirata sistemele informatice [2] , deoarece majoritatea limbajelor de nivel înalt folosesc tehnologia stack frame - plasarea datelor pe stiva de proces , amestecarea datelor programului cu datele de control (inclusiv adresa de început a stivei). cadru stivă și adresa de retur de la funcția executabilă).
O depășire a memoriei tampon poate cauza blocarea sau blocarea unui program, ceea ce duce la refuzul serviciului ( DoS). Anumite tipuri de overflow, cum ar fi un stack frame overflow, permit unui atacator să încarce și să execute cod de mașină arbitrar în numele programului și cu drepturile contului de pe care rulează [3] .
Sunt cunoscute exemple când depășirile de buffer sunt utilizate în mod deliberat de programele de sistem pentru a evita limitările din software-ul sau firmware-ul existent. De exemplu, sistemul de operare iS-DOS (pentru calculatoarele ZX Spectrum ) a folosit caracteristica de depășire a tamponului a TR-DOS încorporat pentru a- și lansa bootloader -ul în coduri de mașină (ceea ce este imposibil de realizat cu instrumentele standard TR-DOS).
Un program care folosește o vulnerabilitate pentru a rupe protecția unui alt program se numește exploit . Cele mai periculoase sunt exploit-urile concepute pentru a obține acces la nivelul de superutilizator sau, cu alte cuvinte, escaladarea privilegiilor . Exploatarea buffer overflow realizează acest lucru prin transmiterea unei intrări special concepute în program. Astfel de date depășesc bufferul alocat și modifică datele care urmează acelui buffer în memorie . [patru]
Imaginați-vă un program ipotetic de administrare a sistemului care rulează cu privilegii de superutilizator, de exemplu, schimbarea parolelor utilizatorului . Dacă programul nu verifică lungimea noii parole introduse, atunci orice date care depășește dimensiunea bufferului alocat pentru stocarea lor vor fi pur și simplu scrise peste ceea ce a fost după buffer. Un atacator poate introduce instrucțiuni în limbajul mașinii în această zonă de memorie , de exemplu, shellcode , efectuând orice acțiune cu privilegii de superutilizator - adăugarea și ștergerea conturilor de utilizator, schimbarea parolelor, modificarea sau ștergerea fișierelor etc. Dacă este executat în această zonă de memorie este permis și în pe viitor, programul îi va transfera controlul, sistemul va executa codul de mașină al atacatorului aflat acolo.
Programele bine scrise ar trebui să verifice lungimea datelor de intrare pentru a se asigura că nu este mai mare decât bufferul de date alocat. Cu toate acestea, programatorii uită adesea de asta. Dacă buffer-ul este situat pe stivă și stiva „crește în jos” (de exemplu, în arhitectura x86 ), atunci folosind un buffer overflow, puteți modifica adresa de retur a funcției executate , deoarece adresa de retur este situată după buffer alocat de funcția executată. Astfel, este posibil să se execute o secțiune arbitrară a codului mașină în spațiul de adrese al procesului. Este posibil să se folosească un buffer overflow pentru a deteriora adresa de retur chiar dacă stiva „crește” (caz în care adresa de retur este de obicei înaintea bufferului). [5]
Chiar și programatorilor cu experiență le este dificil să stabilească dacă o anumită depășire a memoriei tampon ar putea fi o vulnerabilitate. Acest lucru necesită cunoaștere profundă a arhitecturii computerului și a programului țintă. S-a demonstrat că chiar și depășirile mici precum scrierea unui singur octet din buffer pot reprezenta vulnerabilități. [6]
Depășirile de buffer sunt frecvente în programele scrise în limbaje de programare de nivel relativ scăzut, cum ar fi limbajul de asamblare , C și C++ , care necesită programator să controleze dimensiunea memoriei alocate. Depanarea depășirii tamponului este încă un proces prost automatizat. Sistemele formale de verificare a programelor nu sunt foarte eficiente cu limbajele de programare moderne. [7]
Multe limbaje de programare, cum ar fi Perl , Python , Java și Ada , gestionează automat alocarea memoriei, făcând erorile de depășire a memoriei tampon improbabile sau imposibile. [8] Perl oferă redimensionarea automată a matricelor pentru a evita depășirile de buffer . Cu toate acestea, sistemele de rulare și bibliotecile pentru astfel de limbi pot fi încă susceptibile la depășiri de buffer din cauza posibilelor erori interne în implementarea acestor sisteme de validare. Pe Windows sunt disponibile mai multe soluții software și firmware care împiedică executarea codului în afara unui buffer de overflow dacă are loc o astfel de depășire. Aceste soluții includ DEP în Windows XP SP2 , [9] OSsurance și Anti-Execute .
În arhitectura Harvard , codul executabil este păstrat separat de date, ceea ce face ca astfel de atacuri să fie aproape imposibile. [zece]
Luați în considerare un exemplu de program C vulnerabil :
#include <șir.h> int main ( int argc , char * argv []) { charbuf [ 100 ] ; strcpy ( buf , argv [ 1 ]); returnează 0 ; }Folosește funcția unsafe strcpy , care vă permite să scrieți mai multe date decât pot încăpea în matricea alocată pentru acestea. Dacă rulați acest program pe un sistem Windows cu un argument care este mai lung de 100 de octeți, cel mai probabil programul se va bloca și utilizatorul va primi un mesaj de eroare.
Următorul program nu este afectat de această vulnerabilitate:
#include <șir.h> int main ( int argc , char * argv []) { charbuf [ 100 ] ; strncpy ( buf , argv [ 1 ], sizeof ( buf )); returnează 0 ; }Aici, strcpy a fost înlocuit cu strncpy , unde numărul maxim de caractere de copiat este limitat de dimensiunea bufferului. [unsprezece]
Diagramele de mai jos arată cum un program vulnerabil poate deteriora structura stivei .
Ilustrație a scrierii diferitelor date într-un buffer alocat pe stivăA. - Înainte de a copia datele.
B. - Șirul „hello” a fost scris în buffer.
C. - Buffer-ul a depășit, determinând suprascrierea adresei de retur.
În arhitectura x86 , stiva crește de la adrese mai mari la cele mai mici, adică noile date sunt plasate înaintea celor care sunt deja pe stivă.
Scriind date în buffer, puteți scrie dincolo de limitele acestuia și puteți modifica datele acolo, în special, modificați adresa de retur .
Dacă programul are privilegii speciale (cum ar fi rularea ca root ), un atacator poate schimba adresa de retur cu o adresă shellcode , permițându-i să execute comenzi pe sistemul țintă cu privilegii ridicate . [12]
Tehnicile de depășire a memoriei tampon variază în funcție de arhitectură, sistemul de operare și zona de memorie. De exemplu, cazul unui buffer overflow pe heap (utilizat pentru alocarea dinamică a memoriei) este semnificativ diferit de cel de pe stiva de apeluri .
Cunoscut și sub numele de spargerea stivei . Un utilizator experimentat în tehnologie poate folosi un buffer overflow pentru a manipula programul în avantajul său în următoarele moduri:
Dacă adresa datelor utilizatorului este necunoscută, dar este stocată într- un registru, se poate folosi metoda trambulinei : adresa de retur poate fi suprascrisă cu adresa opcode-ului , care va transfera controlul în zona de memorie cu datele utilizatorului. Dacă adresa este stocată în registrul R, atunci săriți la o comandă care transferă controlul către acea adresă (de exemplu, apelul R) va determina executarea codului specificat de utilizator. Adresele codurilor operaționale adecvate sau octeții de memorie pot fi găsite în DLL sau în executabilul însuși. Cu toate acestea, adresele de obicei nu pot conține caractere nule, iar locațiile acestor coduri operaționale variază în funcție de aplicație și sistemul de operare. Proiectul Metasploit , de exemplu, a menținut o bază de date cu coduri operaționale adecvate pentru sistemele Windows (care este momentan indisponibilă). [cincisprezece]
O depășire a stivei nu trebuie confundată cu o depășire a stivei .
De asemenea, este de remarcat faptul că astfel de vulnerabilități sunt de obicei găsite folosind tehnica de testare fuzzing .
Un buffer overflow într-o zonă de date heap se numește heap overflow și este exploatat într-un mod diferit decât un buffer overflow în stivă. Memoria heap este alocată dinamic de o aplicație în timpul rulării și de obicei conține date de program. Exploatarea se face prin coruperea acestor date în moduri speciale pentru a forța aplicația să suprascrie structuri interne, cum ar fi pointerii în listele legate. O tehnică obișnuită de exploatare pentru depășirile buffer-ului heap este de a suprascrie referințe de memorie dinamică (cum ar fi metadatele funcției malloc ) și de a utiliza pointerul modificat rezultat pentru a suprascrie indicatorul de funcție a programului.
O vulnerabilitate în produsul Microsoft GDI+ în manipularea imaginilor JPEG este un exemplu al pericolului pe care îl poate reprezenta o depășire a memoriei tampon. [16]
Manipularea bufferului înainte de a-l citi sau de a-l executa poate preveni exploatarea cu succes a vulnerabilității. Ele pot reduce amenințarea unui atac de succes, dar nu o pot elimina complet. Acțiunile pot include conversia unui șir în majuscule sau litere mici, eliminarea caracterelor speciale sau filtrarea tuturor caracterelor, cu excepția caracterelor alfanumerice. Cu toate acestea, există trucuri pentru a ocoli aceste măsuri: coduri shell alfanumerice, [17] coduri polimorfe , [ 18 ] coduri auto - schimbabile și atacul de întoarcere a bibliotecii . [19] Aceleași tehnici pot fi folosite pentru a se ascunde de sistemele de detectare a intruziunilor . În unele cazuri, inclusiv cazurile de conversie a caracterelor în Unicode , vulnerabilitatea este confundată cu permiterea unui atac DoS , când de fapt este posibilă executarea de la distanță a unui cod arbitrar. [douăzeci]
Sunt folosite diverse trucuri pentru a reduce probabilitatea depășirilor de buffer.
Sistemele de detectare a intruziunilor (IDS) pot detecta și preveni încercările de a exploata de la distanță depășirile de buffer. Deoarece în majoritatea cazurilor datele destinate unei depășiri de buffer conțin șiruri lungi de instrucțiuni No Operation ( NOPsau ) NOOP, IDS pur și simplu blochează toate pachetele primite care conțin un număr mare de NOP-uri consecutive. Această metodă este în general ineficientă, deoarece astfel de matrice pot fi scrise folosind o varietate de instrucțiuni în limbaj de asamblare . Mai recent, crackerii au început să folosească coduri shell cu criptare , cod auto-modificabil , cod polimorf și cod alfanumeric , precum și atacuri de rezervă la biblioteca standard pentru a pătrunde IDS. [21]
Protecția împotriva corupției stivei este utilizată pentru a detecta cele mai frecvente erori de depășire a tamponului. Aceasta verifică dacă stiva de apeluri nu a fost modificată înainte de a reveni din funcție. Dacă a fost schimbat, atunci programul se termină cu o eroare de segmentare .
Există două sisteme, StackGuard și Stack-Smashing Protector (fostul ProPolice), ambele extensii ale compilatorului gcc . De la gcc-4.1-stage2, SSP a fost integrat în distribuția principală a compilatorului . Gentoo Linux și OpenBSD includ SSP cu distribuțiile lor gcc. [22]
Plasarea adresei de retur pe stiva de date facilitează implementarea unui buffer overflow care duce la execuția arbitrară a codului. Teoretic, s-ar putea face modificări la gcc pentru a permite ca adresa să fie plasată pe o stivă specială de returnare care este complet separată de stiva de date, similar modului în care este implementată în limbajul Forth . Cu toate acestea, aceasta nu este o soluție completă la problema depășirii tamponului, deoarece și alte date din stivă trebuie protejate.
Protejarea spațiului de cod executabil poate atenua efectele depășirilor de buffer, făcând imposibile majoritatea acțiunilor rău intenționate. Acest lucru se realizează prin randomizarea spațiului de adrese ( ASLR ) și/sau interzicerea accesului simultan la memorie pentru scriere și execuție. Stiva care nu poate fi executată previne majoritatea exploit- urilor de cod shell .
Există două corecții pentru nucleul Linux care oferă această protecție - PaX și exec-shield . Niciuna dintre acestea nu este încă inclusă în distribuția principală a nucleului. Începând cu versiunea 3.3, OpenBSD a inclus un sistem numit W^X care oferă și controlul timpului de execuție.
Rețineți că această metodă de protecție nu previne corupția stivei. Cu toate acestea, adesea împiedică executarea cu succes a „sarcină utilă” a exploitului. Programul nu va putea insera cod shell în memoria protejată la scriere, cum ar fi segmentele existente de cod executabil. De asemenea, nu va fi posibilă executarea instrucțiunilor pe memoria neexecutabilă, cum ar fi stiva sau heap .
ASLR îngreunează pentru un atacator să determine adresele funcțiilor din codul unui program cu care ar putea efectua un atac cu succes și face atacuri precum ret2libc foarte dificile, deși sunt încă posibile într-un mediu controlat, sau dacă atacatorul este corect ghiceste adresa potrivita.
Unele procesoare , cum ar fi Sparc de la Sun , Efficeon de la Transmeta și cele mai recente procesoare pe 64 de biți de la AMD și Intel, împiedică executarea codului situat în zonele de memorie marcate cu bitul NX special . AMD își numește soluția NX (din engleză No eXecute ), iar Intel își numește XD (din engleză eXecute Disabled ). [23]
Acum există mai multe soluții diferite disponibile pentru protejarea codului executabil pe sistemele Windows , atât de la Microsoft , cât și de la terți.
Microsoft și-a oferit soluția, numită DEP (din engleză. Data Execution Prevention - „data execution prevention”), incluzând-o în pachete de service pentru Windows XP și Windows Server 2003 . DEP profită de noile procesoare Intel și AMD care au fost concepute pentru a depăși limita de memorie adresabilă de 4 GB a procesoarelor pe 32 de biți. În aceste scopuri, unele structuri de servicii au fost mărite. Aceste structuri conțin acum bitul NX rezervat. DEP folosește acest bit pentru a preveni atacurile care implică schimbarea adresei unui handler de excepții (așa-numitul exploit SEH ). DEP oferă doar protecție împotriva exploit-ului SEH , nu protejează paginile de memorie cu cod executabil. [9]
În plus, Microsoft a dezvoltat un mecanism de protecție a stivei conceput pentru Windows Server. Stiva este marcată cu ajutorul așa-numiților „informanți” ( English canary ), a căror integritate este apoi verificată. Dacă „informatorul” a fost schimbat, atunci stiva este coruptă. [24]
Exista si solutii de la terti care impiedica executarea codului situat in zonele de memorie destinate datei sau implementarea mecanismului ASLR.
Problema depășirilor de buffer este comună limbajelor de programare C și C++, deoarece acestea nu ascund detaliile reprezentării la nivel scăzut a bufferelor ca containere pentru tipuri de date . Astfel, pentru a evita depășirile de buffer, trebuie menținut un nivel ridicat de control asupra creării și modificării codului care gestionează bufferele. Utilizarea bibliotecilor de tip de date abstracte care efectuează gestionarea automată centralizată a buffer-ului și care includ verificarea depășirii este o abordare inginerească pentru prevenirea depășirii tamponului. [25]
Cele două tipuri principale de date care permit depășirea tamponului în aceste limbi sunt șirurile și matricele . Astfel, utilizarea bibliotecilor pentru șiruri și structuri de date de listă care au fost dezvoltate pentru a preveni și/sau detecta depășirile de buffer evită multe vulnerabilități. Prețul unor astfel de soluții este o scădere a performanței din cauza verificărilor inutile și a altor acțiuni efectuate de codul bibliotecii, deoarece este scris „pentru toate ocaziile”, iar în fiecare caz specific, unele dintre acțiunile pe care le efectuează pot fi redundante.
Depășirea tamponului a fost înțeleasă și parțial documentată încă din 1972 în Studiul de planificare a tehnologiei securității computerelor. [26] Cea mai veche utilizare documentată rău intenționată a depășirii tamponului a avut loc în 1988. S-a bazat pe una dintre mai multe exploatări folosite de viermele Morris pentru a se autopropaga pe Internet. Programul a exploatat o vulnerabilitate în serviciul finger Unix . [27] Mai târziu, în 1995, Thomas Lopatik a redescoperit în mod independent depășirea tamponului și a enumerat constatările pe lista Bagtrak . [28] Un an mai târziu, Elias Levy a publicat o introducere pas cu pas despre utilizarea depășirilor de buffer cu stiva, Smashing the Stack for Fun and Profit, în revista Phrack . [12]
De atunci, cel puțin doi viermi de rețea cunoscuți au folosit depășiri de buffer pentru a infecta un număr mare de sisteme. În 2001, viermele Code Red a exploatat această vulnerabilitate în produsul Microsoft Internet Information Services (IIS) 5.0 [29] , iar în 2003 SQL Slammer a infectat mașinile care rulau Microsoft SQL Server 2000 . [treizeci]
În 2003, exploatarea unui buffer overflow prezent în jocurile Xbox licențiate a permis ca software-ul fără licență să ruleze pe consolă fără modificarea hardware folosind așa-numitele modchip -uri . [31] PS2 Independence Exploit a folosit, de asemenea, un buffer overflow pentru a obține același rezultat pentru PlayStation 2 . O exploatare similară pentru Wii Twilight a exploatat această vulnerabilitate în The Legend of Zelda: Twilight Princess .