TL;DR. În acest articol, vom explora schemele de protecție (hardening schemes) care funcționează din cutie în cinci distribuții populare de Linux. Pentru fiecare, am luat configurația nucleului implicit, am descărcat toate pachetele și am analizat schemele de protecție din fișierele binare încorporate. Discutăm despre distribuțiile OpenSUSE 12.4, Debian 9, CentOS, RHEL 6.10 și 7, precum și Ubuntu 14.04, 12.04 și 18.04 LTS.
Rezultatele confirmă că și cele mai elementare scheme, cum ar fi canarele de stivă și codul independent de poziție, nu sunt încă utilizate de toată lumea. Situația este și mai gravă în cazul compilatoarelor, când vine vorba de protecția împotriva vulnerabilităților de tipul coliziunii de stivă (stack clash), care au fost aduse în prim-plan în ianuarie, după publicarea . Dar nu totul este atât de sumbru. O proporție semnificativă din fișierele binare implementează metode de bază de protecție, iar numărul acestora crește de la o versiune la alta.
Verificările au arătat că cele mai multe metode de protecție sunt implementate în Ubuntu 18.04 la nivel de sistem de operare și aplicații, urmat de Debian 9. Pe de altă parte, în OpenSUSE 12.4, CentOS 7 și RHEL 7 sunt implementate, de asemenea, scheme de protecție de bază, iar protecția împotriva coliziunii de stivă este aplicată și mai pe larg, având un set de pachete implicit mult mai dens.
Introducere
Este greu să asiguri o calitate ridicată a software-ului. În ciuda numărului mare de instrumente avansate pentru analiza statică a codului și analiza dinamică în timpul execuției, precum și progresului semnificativ în dezvoltarea compilatoarelor și limbajelor de programare, software-ul modern continuă să sufere de vulnerabilități care sunt exploatate constant de atacatori. Situația este și mai gravă în ecosistemele care includ cod învechit. În astfel de cazuri, ne confruntăm nu doar cu eterna problemă a descoperirii posibilelor erori exploatabile, ci și cu limitele stricte ale compatibilității înapoi, care adesea necesită păstrarea unui cod limitat, iar și mai rău vulnerabil sau defectuos.
Aici intervine protecția sau întărirea programelor (hardening). Unele tipuri de erori nu putem să le prevenim, dar putem să le facem viața mai dificilă atacatorilor și să soluționăm parțial problema, prevenind sau împiedicând exploatării aceste erori. Această protecție este utilizată în toate sistemele de operare moderne, însă metodele diferă semnificativ în complexitate, eficiență și performanță: de la canari de stivă (stack canaries) și până la protecții complete și . În acest articol, vom examina ce metode de protecție sunt aplicate în cele mai populare distribuții Linux în configurația lor implicită, precum și vom analiza proprietățile binarelor distribuite prin sistemele de gestionare a pachetelor fiecărei distribuții.
CVE și securitate
Toți am văzut articole cu titluri precum „Cele mai vulnerabile aplicații ale anului” sau „Cele mai vulnerabile sisteme de operare”. De obicei, ele oferă statistici cu privire la numărul total de înregistrări de vulnerabilitate de tip , obținute din de la și alte surse. Ulterior, aceste aplicații sau sisteme de operare sunt clasificate în funcție de numărul de CVE. Din păcate, deși CVE sunt foarte utile pentru urmărirea problemelor și informarea furnizorilor și utilizatorilor, ele spun puțin despre securitatea reală a software-ului.
De exemplu, să luăm în considerare numărul total de CVE din ultimii patru ani pentru kernelul Linux și pentru cele cinci cele mai populare distribuții server, respectiv Ubuntu, Debian, Red Hat Enterprise Linux și OpenSUSE.

Fig. 1
Ce ne spune acest grafic? Înseamnă un număr mai mare de CVE că o distribuție este mai vulnerabilă decât alta? Răspunsul este nu. De exemplu, în acest articol veți vedea că Debian implementează mecanisme de protecție mai stricte în comparație, de exemplu, cu OpenSUSE sau RedHat Linux, și cu toate acestea Debian are mai multe CVE. Totuși, acestea nu înseamnă neapărat o securitate slăbită: chiar și prezența CVE nu indică dacă vulnerabilitatea este exploatabilă. Scorurile de gravitate oferă o perspectivă asupra cât de probabil este utilizarea vulnerabilității, dar în cele din urmă, exploatabilitatea depinde în mare măsură de protecția prezentă în sistemele afectate, precum și de resursele și capacitățile atacatorilor. Mai mult, absența raporturilor CVE nu spune nimic despre alte vulnerabilități nelistate sau necunoscute vulnerabilităților. Diferența în CVE poate fi explicată nu prin calitatea software-ului, ci prin alți factori, inclusiv resursele alocate pentru testare sau dimensiunea bazei de utilizatori. În exemplul nostru, un număr mai mare de CVE la Debian poate indica pur și simplu faptul că Debian furnizează mai multe pachete de software.
Desigur, sistemul CVE oferă informații utile care permit crearea de protecții corespunzătoare. Cu cât înțelegem mai bine motivele eșecului unui program, cu atât mai ușor este să determinăm posibilele metode de exploatare și să dezvoltăm mecanismele necesare. de detectare și reacție. În figura 2 sunt prezentate categoriile de vulnerabilități pentru toate distribuțiile în ultimii patru ani (). Se vede imediat că majoritatea CVE-urilor se încadrează în următoarele categorii: DoS (refuz de serviciu), execuție de cod, supraîncărcare, corupție de memorie, scurgere de informații (exfiltrare) și escaladarea privilegiilor. Deși multe CVE-uri sunt contabilizate de mai multe ori în diferite categorii, în general aceleași probleme persistă de la an la an. În următoarea parte a articolului vom evalua utilizarea diferitelor scheme de protecție pentru a preveni exploatarea acestor vulnerabilități.

Figura 2
Sarcini
În acest articol, ne propunem să răspundem următoarelor întrebări:
- Care este securitatea diferitelor distribuții Linux? Ce mecanisme de protecție există în kernel și aplicațiile din spațiul utilizatorului?
- Cum s-a schimbat în timp adoptarea mecanismelor de protecție pentru diverse distribuții?
- Care sunt dependențele medii ale pachetelor și bibliotecilor pentru fiecare distribuție?
- Ce protecții sunt implementate pentru fiecare binar?
Alegerea distribuțiilor
Se pare că este dificil să găsești statistici precise despre instalările distribuțiilor, deoarece, în cele mai multe cazuri, numărul de descărcări nu indică numărul de instalări reale. Cu toate acestea, opțiunile Unix constituie majoritatea sistemelor server (la serverele web 69.2%, conform W3techs și alte surse), iar cota lor crește constant. Astfel, pentru cercetarea noastră ne-am concentrat pe distribuțiile disponibile 'out of the box' pe platforma . În special, am ales următoarele sisteme de operare: Distribuția/versiunea
Build
Nucleu
OpenSUSE 12.4
4.12.14-95.3-default
Debian 9 (stretch)
#1 SMP Wed Dec 5 06:00:48 UTC 2018 (63a8d29)
4.9.0-8-amd64
CentOS 6.10
#1 SMP Debian 4.9.130-2 (2018-10-27)
2.6.32-754.10.1.el6.x86_64
3.10.0-957.5.1.el7.x86_64
#1 SMP Tue Jan 15 17:07:28 UTC 2019
CentOS 7
Red Hat Enterprise Linux Server 6.10 (Santiago)
#1 SMP Fri Feb 1 14:54:57 UTC 2019
2.6.32-754.9.1.el6.x86_64
Red Hat Enterprise Linux Server 7.6 (Maipo)
#1 SMP Wed Nov 21 15:08:21 EST 2018
3.10.0-957.1.3.el7.x86_64
3.10.0-957.1.3.el7.x86_64
#1 SMP Thu Nov 15 17:36:42 UTC 2018
Ubuntu 14.04 (Trusty Tahr)
4.4.0–140-generic
#166~14.04.1-Ubuntu SMP Sat Nov 17 01:52:43 UTC 20…
Ubuntu 16.04 (Xenial Xerus)
4.15.0–1026-gcp
#27~16.04.1-Ubuntu SMP Fri Dec 7 09:59:47 UTC 2018
Ubuntu 18.04 (Bionic Beaver)
4.15.0–1026-gcp
#27-Ubuntu SMP Thu Dec 6 18:27:01 UTC 2018
Tabelul 1
Analiză
Vom studiem configurația kernel-ului implicit, precum și proprietățile pachetelor disponibile prin managerul de pachete al fiecărui distribuitor din cutie. Astfel, ne concentrăm doar pe pachetele din oglinzile implicite ale fiecărui distribuitor, ignorând pachetele din depozitele instabile (de exemplu, oglinzile ‘testing’ din Debian) și pachetele terță parte (de exemplu, pachetele Nvidia din oglinzile standard). În plus, nu luăm în considerare compuneri personalizate de kernel sau configurații cu securitate sporită.
Analiza configurației kernel-ului
Am aplicat un script de analiză bazat pe . Analizăm parametrii de protecție din cutie ai distribuitorilor menționați și îi comparăm cu lista de la (KSPP). Pentru fiecare parametru de configurație, tabela 2 descrie setarea dorită: bifa este marcată pentru distribuitoarele care respectă recomandările KSSP (explanarea termenilor se găsește în ; în articole viitoare vom discuta despre cum au apărut multe dintre aceste metode de protecție și cum poate fi spart sistemul în absența lor).

În general, în noile kernel-uri există setări mai stricte din cutie. De exemplu, în CentOS 6.10 și RHEL 6.10 pe kernel 2.6.32 lipsesc majoritatea funcționalităților critice implementate în noile kernel-uri, cum ar fi , permisiuni stricte RWX, randomizarea adreselor sau protecția copy2usr. Este important de menționat că multe dintre opțiunile de configurație din tabel lipsesc în versiunile mai vechi ale kernel-ului și nu sunt aplicabile în realitate — în tabel, acest lucru este totuși menționat ca o absență a protecției adecvate. În mod similar, dacă un parametru de configurație lipsește în această versiune, iar pentru siguranță acest parametru trebuie dezactivat, aceasta este considerată o configurație rezonabilă.
Un alt aspect de avut în vedere în interpretarea rezultatelor este că unele configurații ale nucleului care măresc suprafața de atac pot fi folosite simultan pentru securitate. Exemple de acest tip includ uprobes și kprobes, modulele nucleului și BPF/eBPF. Recomandarea noastră este să utilizăm mecanismele menționate anterior pentru a asigura o protecție reală, deoarece acestea nu sunt triviale de utilizat, iar exploatarea lor presupune că subiecții malițioși s-au implantat deja în sistem. Dar dacă aceste opțiuni sunt activate, administratorul de sistem trebuie să monitorizeze activ abuzurile.
Examinând în continuare înregistrările din tabelul 2, vedem că nucleele moderne oferă mai multe opțiuni pentru protecția împotriva exploatării unor astfel de vulnerabilități, cum ar fi scurgerea de informații și suprascrierea stivei/memoriei heap. Totuși, observăm că chiar și cele mai recente distribuții populare nu au implementat încă protecții mai sofisticate (de exemplu, cu patch-uri ) sau protecție modernă împotriva atacurilor de reutilizare a codului (de exemplu, ). Ce este și mai grav, chiar și aceste instrumente de protecție mai avansate nu protejează împotriva întregului spectru de atacuri. Prin urmare, este extrem de important pentru administratorii de sistem să completeze configurațiile raționale cu soluții care oferă detectarea și prevenirea exploatărilor în timpul execuției.
Analiza aplicațiilor
Nu este surprinzător că diferitele distribuții au caracteristici diferite ale pachetelor, opțiuni de compilare, dependențe de biblioteci etc. Diferențele există chiar și pentru și pachete cu un număr mic de dependențe (de exemplu, coreutils în Ubuntu sau Debian). Pentru a evalua diferențele, am descărcat toate pachetele disponibile, am extras conținutul acestora și am analizat fișierele binare și dependențele. Pentru fiecare pachet, am urmărit celelalte pachete de care depinde, iar pentru fiecare binar am urmărit dependențele sale. În această secțiune, vom rezuma concluziile.
Distribuții
În total, am descărcat 361.556 de pachete pentru toate distribuțiile, extrăgând doar pachetele din oglinzile din moduri implicite. Am ignorat pachetele fără fișiere executabile ELF, cum ar fi codul sursă, fonturile etc. După filtrare, au rămas 129.569 de pachete, care conțin în total 584.457 de fișiere binare. Distribuția pachetelor și fișierelor pe distribuții este prezentată în figura 3.

Figura 3
Se poate observa că, cu cât distribuția este mai modernă, cu atât conține mai multe pachete și fișiere binare, ceea ce este logic. Totuși, pachetele Ubuntu și Debian includ mult mai multe fișiere binare (atât executabile, cât și module dinamice și biblioteci) decât CentOS, SUSE și RHEL, ceea ce influențează potențial suprafața de atac a Ubuntu și Debian (trebuie menționat că cifrele reflectă toate binarele tuturor versiunilor pachetului, adică unele fișiere sunt analizate de mai multe ori). Acest lucru este deosebit de important, având în vedere dependențele dintre pachete. Astfel, o vulnerabilitate într-un binar al unui pachet poate afecta multe părți din ecologie, așa cum o bibliotecă vulnerabilă poate afecta toate fișierele binare care o importă. Ca punct de plecare, să examinăm distribuția numărului de dependențe pe pachete în diferite sisteme de operare:
Figura 4
În aproape toate distribuțiile, 60% dintre pachete au cel puțin 10 dependențe. În plus, unele pachete au un număr semnificativ mai mare de dependențe (peste 100). Aceleași considerații se aplică și dependențelor inverse ale pachetelor: așa cum era de așteptat, mai multe pachete sunt utilizate de multe alte pachete din distribuție, prin urmare, vulnerabilitățile în aceste câteva selecte prezintă un risc ridicat. Ca exemplu, în tabelul următor sunt enumerate 20 de pachete cu cel mai mare număr de dependențe inverse în SLES, CentOS 7, Debian 9 și Ubuntu 18.04 (în fiecare celulă este indicat pachetul și numărul de dependențe inverse).

Tabelul 3
Un fapt interesant. Deși toate sistemele de operare analizate sunt construite pentru arhitectura x86_64, iar la majoritatea pachetelor arhitectura este definită ca x86_64 și x86, pachetele conțin adesea fișiere binare pentru alte arhitecturi, așa cum se arată în figura 5.

Fig. 5
În următoarea secțiune, ne vom aprofunda în caracteristicile binarelor analizate.
Statistica protecției fișierelor binare
Ca un minim absolut, este necesar să studiem un set de opțiuni de bază de protecție pentru fișierele binare existente. Unele distribuții Linux vin cu scripturi care efectuează astfel de verificări. De exemplu, în Debian/Ubuntu există un astfel de script. Iată un exemplu de funcționare:
$ hardening-check $(which docker)
/usr/bin/docker:
Executabil Independent de Poziție: da
Stiva protejată: da
Funcții Fortify Source: nu, doar funcții neprotejate găsite!
Mutări doar în citire: da
Bindare imediată: daScriptul verifică cinci :
- Executabil Independent de Poziție (PIE): indică dacă secțiunea de text a programului poate fi mutată în memorie pentru a obține randomizare, dacă ASLR este activat în kernel.
- Stiva Protejată: sunt activate canare de stivă pentru a proteja împotriva atacurilor de coliziune a stivei.
- Fortify Source: funcțiile nesigure (de exemplu, strcpy) sunt înlocuite cu omologii lor mai siguri, iar apelurile verificate în timp de execuție sunt înlocuite cu omologii lor neverificați (de exemplu, memcpy în loc de __memcpy_chk).
- Mutări doar în citire (RELRO): sunt marcate înregistrările din tabelul de mutări ca 'numai pentru citire', dacă funcționează înainte de începerea execuției.
- Bindare imediată: permite linker-ului de timp de execuție să rezolve toate mutările înainte de începerea execuției programului (acest lucru este echivalent cu RELRO complet).
Sunt suficiente mecanismele enumerate mai sus? Din păcate, nu. Există metode cunoscute pentru a ocoli toate protecțiile menționate anterior, dar cu cât protecția este mai strictă, cu atât mai sus ajunge ștacheta pentru atacator. De exemplu, sunt mai greu de aplicat atunci când PIE și bindarea imediată sunt active. În mod similar, ASLR complet necesită muncă suplimentară pentru a crea un exploit funcțional. Cu toate acestea, atacatorii sofisticați sunt deja pregătiți să întâmpine astfel de protecții: absența lor va accelera practic hacking-ul. De aceea, este extrem de important ca aceste măsuri să fie considerate necesare. minim.
Am dorit să studiem câte fișiere binare din distribuțiile analizate sunt protejate prin aceste metode, precum și prin alte trei metode:
- Bitul neexecutabil () împiedică executarea în orice regiune care nu ar trebui să fie executabilă, de exemplu în heap-ul stivei etc.
- indică calea de execuție folosită de loaderul dinamic pentru a căuta bibliotecile corespunzătoare. Primul este obligatoriu pentru orice sistem modern: absența sa permite atacatorilor să scrie arbitrari în memorie și să execute codul ca atare. În al doilea rând, configurațiile greșite ale căilor de execuție ajută la introducerea codului nesigur, care poate duce la o serie de probleme (de exemplu, , dar și pentru ).
- Protecția împotriva suprapunerii stivei oferă protecție împotriva atacurilor care determină stiva să se suprapună cu alte zone de memorie (de exemplu, cu heap-ul). Având în vedere exploatările recente care abuzează , am considerat că este relevant să includem acest mecanism în setul nostru de date.
Așadar, fără alte ceremonii, să trecem la cifre. Tabelele 4 și 5 conțin o sinteză a analizei fișierelor executabile și bibliotecilor diferitelor distribuții, respectiv.
- După cum se poate observa, protecția NX este implementată peste tot, cu câteva excepții rare. În special, se poate remarca o utilizare ceva mai scăzută în distribuțiile Ubuntu și Debian comparativ cu CentOS, RHEL și OpenSUSE.
- Canarele de stivă lipsesc în multe locuri, mai ales în distribuțiile cu nuclee vechi. Un anumit progres se observă în ultimele distribuții CentOS, RHEL, Debian și Ubuntu.
- Cu excepția Debian și Ubuntu 18.04, majoritatea distribuțiilor au o suport slab pentru PIE.
- Protecția împotriva coliziunilor stivei este slab implementată în OpenSUSE, CentOS 7 și RHEL 7 și practic lipseste la celelalte.
- Toate distribuțiile cu nuclee moderne au un anumit suport pentru RELRO, cu Ubuntu 18.04 pe primul loc, iar Debian pe locul doi.
După cum am menționat, metricile din acest tabel sunt medii pentru toate versiunile fișierului binar. Dacă ne uităm doar la cele mai recente versiuni ale fișierelor, cifrele vor fi diferite (de exemplu, vezi ). Mai mult, majoritatea distribuțiilor verifică de obicei protecția doar pentru câteva funcții în codul binar atunci când calculează statisticile, iar în analiza noastră este indicat procentul real de funcții consolidate. Așadar, dacă în binar sunt protejate 5 din 50 de funcții, îi vom atribui o evaluare de 0,1, ceea ce corespunde cu 10% funcții consolidate.

Tabelul 4. Caracteristicile protecției pentru fișierele executabile, prezentate în figura 3 (implementarea funcțiilor corespunzătoare în procente din numărul total de fișiere executabile)

Tabelul 5. Caracteristicile de securitate pentru bibliotecile prezentate în fig. 3 (implementarea funcțiilor corespunzătoare în procente din numărul total de biblioteci)
Există, așadar, progres? Cu siguranță că există: acest lucru se observă din statisticile pentru distribuțiile separate (de exemplu, ), precum și din tabelele prezentate anterior. Ca exemplu, în fig. 6 este arătată implementarea mecanismelor de securitate în trei distribuții succesive Ubuntu LTS 5 (am omis statistica privind protecția împotriva coliziunii stivei). Observăm că, de la o versiune la alta, tot mai multe fișiere suportă canaritul stivei, iar tot mai multe fișiere binare sunt livrate cu protecție completă RELRO.
Fig. 6
Din păcate, o serie de fișiere executabile din diverse distribuții nu dispun de niciuna dintre protecțiile menționate anterior. De exemplu, privind Ubuntu 18.04, putem observa binarul ngetty (înlocuirea getty), precum și shell-urile mksh și lksh, interpretul picolisp, pachetele nvidia-cuda-toolkit (pachet popular pentru aplicații cu accelerare GPU, cum ar fi cadrele de învățare automată) și klibc-utils. De asemenea, binarul mandos-client (instrument administrativ care permite repornirea automată a mașinilor cu sisteme de fișiere criptate), precum și rsh-redone-client (reimplementarea rsh și rlogin) sunt livrate fără protecția NX, deși au drepturi SUID :(. În plus, în mai multe binare suid nu există protecția de bază, cum ar fi canaritul stivei (de exemplu, fișierul binar Xorg.wrap din pachetul Xorg).
Rezumat și observații finale
În acest articol, am evidențiat câteva caracteristici de securitate ale distribuțiilor moderne de Linux. Analiza a arătat că în cea mai recentă distribuție Ubuntu LTS (18.04) s-a realizat, în medie, cea mai puternică protecție la nivel de sistem de operare și aplicații dintre distribuțiile cu nuclee relativ noi, cum ar fi Ubuntu 14.04, 12.04 și Debian 9. Totuși, distribuțiile examinate, CentOS, RHEL și OpenSUSE, din setul nostru de date, oferă implicit un set mai dens de pachete, iar în versiunile lor recente (CentOS și RHEL) au un procent mai mare de implementare a protecției împotriva coliziunii de stivă, comparativ cu concurenții pe bază de Debian (Debian și Ubuntu). Comparând versiunile CentOS și RedHat, observăm îmbunătățiri considerabile în implementarea canariilor de stivă și RELRO de la versiunile 6 la 7, dar, în medie, CentOS implementează mai multe funcții decât RHEL. În general, toate distribuțiile ar trebui să acorde o atenție specială protecției PIE, care, cu excepția Debian 9 și Ubuntu 18.04, este implementată în mai puțin de 10% din fișierele binare din setul nostru de date.
În cele din urmă, trebuie menționat: deși am realizat cercetarea manual, există multe instrumente de securitate (de exemplu, , , ), care efectuează analize și ajută la evitarea configurațiilor nesigure. Din păcate, chiar și o protecție puternică în configurații rezonabile nu garantează absența exploiturilor. De aceea, suntem ferm convinși că este esențial să asigurăm , concentrându-ne pe modelele de exploatare și prevenindu-le.
Sursa: habr.com
