marți, 8 aprilie 2014

Cum funcţionează HeartBleed, bug-ul ce afectează OpenSSL

Voi încerca, în această postare, să explic sumar care este cauza găurii de securitate din OpenSSL, "bomba" ce afectează mai mult de 50% din serverele de pe internet. Aceasta este una din cele mai grave găuri de securitate deoarece vizează o librărie ce asigură comunicarea criptată pe internet, folosită mai ales în domenii unde datele transmise online sunt de mare sensibilitate (de exemplu, domeniul bancar). Problema este că mare parte din dezvoltatori se bazează doar pe nivelul de securitate oferit de SSL, fără a lua în calcul erorile de programare din librăriile ce oferă suport pentru acest protocol (în cazul de faţă, OpenSSL).



1. Explicaţia "ca la proşti", pentru cei neiniţiaţi în domeniul calculatoarelor

Pentru cei nu prea iniţiaţi în limbajele de programare mai low-level trebuie explicat faptul că programele pentru calculator, în marea parte a timpului, execută operaţii pe memoria RAM, operaţii de genul "pune asta în memorie", "dă-mi ce se află în memorie la poziţia X", etc. Ce trebuie spus despre memoria RAM este că în ea se stochează memoria temporară a tuturor programelor ce rulează într-un moment dat pe calculator (de obicei destul de multe) ceea ce s-ar traduce prin faptul că acolo e mai mereu un talmeş-balmeş (există, totuşi, un nivel de organizare). Problema e că calculatorul e calculator: el nu ştie ce înseamnă datele de acolo, acestea având relevanţă doar pentru programul care le scrie sau citeşte. De asemenea, calculatorul nu ştie de unde începe o secţiune de date şi unde se termină, în mod asemănător cum pentru un om i-ar fi dificil să ştie unde se termină şi unde începe o frază într-o carte în care nu există semne de punctuaţie. Din cauza aceasta programele de obicei îî dau calculatorului instrucţiuni de genul "dă-mi N biţi începând de la poziţia X". De aici porneşte şi problema cu celebrul bug din OpenSSL ce a zguduit internetul zilele astea.

SSL este un protocol de criptare a datelor ce sunt trimise prin intermediul reţelelor de calculatoare. Fiind un protocol acesta respectă anumite convenţii şi oferă o listă de comenzi prestabilite. Există mai multe librării ce ştiu "a vorbi" SSL, una din acestea fiind OpenSSL. Ei bine, una din comenzile specifice SSL se rezumă la ceva de genul "uite, eu îţi trimit un mesaj,  tu trimite-mi-l înapoi". Acum imaginează-ţi că eşti telegrafist şi tocmai primeşti un mesaj prin codul MORSE, pe care îl notezi literă cu literă pe o foaie de hârtie. Ca să facem lucrurile şi mai amuzante, mesajul e într-o limbă pe care nu o cunoşti. Problema e că nu prea ştii când ai primit mesajul complet: poţi să te bazezi pe faptul că atunci când bipurile încetează mesajul s-a terminat - dar dacă celuilalt doar i-a obosit mâna şi ia o pauză? Aşa e şi atunci când primeşti date prin internet - iar pentru că calculatorul nu prea se joacă el cu "o fi gata mesajul, n-o fi?" acesta trebuie să ştie exact care e lungimea unui mesaj. De aceea, atunci când trimitem mesajul pe care dorim să-l primim înapoi prin SSL trimitem prima dată lungimea acestuia.

Acum serverul începe să primească mesajul, ţinând evidenţa cantităţii de date pe care le mai are de recepţionat. Fiecare byte recepţionat e stocat în memoria RAM pentru ca mai apoi să fie trimis înapoi expeditorului. Să zicem că noi îi spunem serverului că îi trimitem 64KB (cantitatea maximă permisă de protocol) dar de fapt îi trimitem un singur byte. El va primi byte-ul şi îl va stoca în memorie. Acum îi spunem să ne trimită mesajul înapoi. Mai sus am spus că calculatorul nu ştie când se termină o secţiune de date. Problema e că OpenSSL ştie că mesajul are 64KB şi că tot atât trebuie să ne trimită înapoi, aşa că începe cu byte-ul nostru şi nu se mai opreşte, continuând prin a ne trimite ce mai găseşte el acolo, prin memorie, până totalizează cei 64 de KB! De cele mai multe ori acolo se întâmplă să fie date sensibile precum date de acces la conturi sau cu ceva ghinion chiar cheia privată a serverului (un fel de amprentă), care ar trebui să fie absolut secretă şi care odată aflată compromite fără drept de veto securitatea acestuia.

Problema e că OpenSSL ar trebui să verifice dacă mesajul a fost incomplet şi să trimită înapoi doar tot atâţia bytes cât a primit. Genul acesta de erori se fac foarte uşor în limbajele de programare cum ar fi C sau C++ şi nu doar o singură dată au cauzat grave probleme de securitate. Dezastrul ar fi putut fi uriaş: singurul lucru care ne-a salvat a fost că atacatorul nu putea să controleze ce date primeşte, ci trebuia să se mulţumească cu ce îi trimite serverul din memoria RAM (care e un talmeş-balmeş, după cum spuneam).

2. Explicaţie pentru cei mai cunoscători
Problema apare într-un memcpy pe care OpenSSL îl execută pentru a trimite înapoi mesajul primit de la client. Librăria presupune că utilizatorul îi va trimite tot atâtea date cât a anunţat că va trimite, aşa că trimite direct ca parametru lui memcpy cantitatea de date specificată. Acest lucru cauzează citirea din memorie a numărului de bytes specificat de client, aşa că dacă se întâmplă ca mesajul să fie mai scurt decât s-a convenit, memcpy va copia - evident - ceea ce urmează în memoria RAM până când lungimea mesajului va fi satisfăcută.

E logic că rezultatele puteau fi catastrofale (ca şi cum nu ar fi deja). Din fericire în afară de cantitatea de date returnată atacatorul nu poate avea niciun control, fiind imposibil de prevăzut ce se va găsi în mesajul returnat. Din păcate de cele mai multe ori se întâmplă ca acolo să fie cookie-uri, request-uri HTTP ce conţin username-uri sau parole şi, în cele mai ghinioniste cazuri, cheia privată pe baza căreia se face criptarea. Merită menţionat că problema nu e în protocolul SSL, ci doar în implementarea din OpenSSL. Mai multe date, explicate mult mai frumos decât o pot face eu, puteţi afla de aici.

Surse:
http://security.stackexchange.com/questions/55116/how-exactly-does-the-openssl-tls-heartbeat-heartbleed-exploit-work/55117#55117
http://blog.existentialize.com/diagnosis-of-the-openssl-heartbleed-bug.html
Sursă imagine: http://heartbleed.com/