Rootkit

Versiunea actuală a paginii nu a fost încă examinată de colaboratori experimentați și poate diferi semnificativ de versiunea revizuită pe 20 august 2020; verificările necesită 12 modificări .

Rootkit ( eng.  rootkit , adică „ set root ”) este un set de instrumente software (de exemplu, fișiere executabile, scripturi, fișiere de configurare ) care oferă:

Termenul Rootkit a provenit istoric din lumea UNIX , iar acest termen se referă la un set de utilități sau un modul special de kernel pe care un atacator îl instalează pe un sistem informatic pe care l-a spart imediat după ce a obținut drepturile de superutilizator. Acest set, de regulă, include o varietate de utilități pentru acoperirea urmelor unei intruziuni în sistem, făcând invizibile sniffer -urile , scanerele, keylogger -urile , troienii , înlocuind principalele utilități UNIX (în cazul unui rootkit non-nuclear). Rootkit-ul permite unui atacator să obțină un punct de sprijin într-un sistem compromis și să ascundă urmele activităților sale, ascunzând fișierele, procesele și însăși prezența unui rootkit în sistem.

Un rootkit poate fi instalat într-un sistem în diferite moduri: prin descărcare printr- un exploit , după obținerea accesului shell (în acest caz, un instrument precum wget sau clientul FTP original poate fi folosit pentru a descărca rootkit-ul de pe un dispozitiv la distanță), în codul sursă sau resursele produsului software.

Clasificarea rootkit-urilor

Metode de bază de implementare

Pe Microsoft Windows

Există diverse tehnologii rootkit, cele mai comune sunt capturarea tabelelor de apeluri (IAT, IDT, SSDT, GDT ), interceptarea funcțiilor (de exemplu, modificarea octeților inițiali), modificarea directă a obiectelor de sistem (DKOM), metodele de utilizare a driverelor.

Capturarea tabelelor de apeluri

Tabelul de apeluri este o matrice în care fiecare element stochează adresa procedurii corespunzătoare. Astfel de tabele există atât în ​​modul kernel (IDT, CPU MSR- uri, GDT, SSDT, tabel de expediere IRP), cât și în modul utilizator (IAT).

Import Address Table (IAT) este tabelul de apeluri al modulului principal în modul utilizator. Majoritatea fișierelor executabile au unul sau mai multe IAT-uri încorporate care conțin adresele rutinelor de bibliotecă importate din DLL [2] .

Pe o mașină multiprocesor, există mai multe instanțe de tabele de apeluri (de exemplu IDT, GDT , MSR ). Deoarece fiecare procesor are propriile registre de sistem (în special, GDTR - registrul tabelului de descriptor global (GDT), IDTR - registrul de descriptor al tabelului de întreruperi (IDT) și IA32_SYSENTER_EIP - conține adresa virtuală a punctului de intrare în modul kernel (MSR)) , are și structuri de sistem proprii [3] .

Când o intrare în tabelul de apeluri este modificată, execuția programelor este controlată și, dacă este necesar, redirecționată către funcțiile necesare. Procedura interceptată poate [4] :

  • blocați apelurile efectuate de anumite aplicații (de exemplu, antivirus )
  • înlocuiți procedura inițială
  • monitorizați sistemul prin interceptarea parametrilor de intrare
  • parametrii de ieșire ale filtrului

Ideea generală a capturii este următoarea:

  • Identificați tabelul de apeluri, obțineți adresa acestuia
  • Salvați înregistrarea existentă în tabel
  • Înlocuiți intrarea cu o nouă adresă
  • Restaurați intrarea inițială

Dacă funcția de interceptare presupune apelarea procedurii inițiale, atunci blocarea și monitorizarea sunt efectuate înainte de apel, filtrarea parametrilor după.

IAT este un tabel de apeluri situat în structura de fișiere a aplicației. IAT stochează adresa procedurilor exportate de un anumit DLL . Fiecare DLL la care se conectează o aplicație în momentul pornirii are propriul său IAT. Pentru a captura IAT, trebuie să faceți următoarele:

  • Obțineți acces la spațiul de adresă al procesorului
  • Localizați IAT în imaginea memoriei procesorului
  • Modificați IAT necesar

Pentru a manipula IAT-ul, este necesar accesul la spațiul de adrese al aplicației căreia îi aparține tabelul. O modalitate este să injectați un DLL. Printre metodele de injectare a unui DLL în spațiul de adrese al unui proces, se poate specifica [5] :

  • Modificarea valorii de registry AppInit_DLL
  • Setați apelul API WindowsHookEx().
  • Folosind fire la distanță
Interceptare prin modificarea codului funcției

Principiul de funcționare se bazează pe faptul că primii octeți ai funcțiilor interceptate sunt înlocuiți cu codul interceptor. Trebuie subliniat că la instalarea interceptorului, codul funcției interceptate nu este analizat: primii N octeți sunt modificați și nu primele N instrucțiuni de mașină. Consecința acestui fapt este [6] :

  1. codul interceptor poate fi setat doar la începutul unei funcții;
  2. pentru fiecare apel către funcția interceptată, interceptorul trebuie să-și restabilească codul de mașină înainte de apel și să reintercepteze după ce apelul este finalizat.

Algoritmul rootkit:

  1. În corpul interceptorului este creată o matrice, în care sunt scriși primii N octeți ai fiecărei funcții interceptate (de obicei dimensiunea codului modificat nu depășește 20 de octeți)
  2. Matricea este completată cu codul mașinii de referință al funcțiilor interceptate.
  3. La începutul fiecărei funcții interceptate, este scris un cod care transferă controlul către interceptor.

Algoritmul de operare a interceptorului:

  1. Secvența de acțiuni definită de atacator.
  2. Recuperarea primilor N octeți ai funcției interceptate.
  3. Apelarea funcției interceptate.
  4. Remodificarea codului de mașină al funcției interceptate: suprascrierea codului care transferă controlul către interceptor în primii octeți.
  5. Analiza și, dacă este necesar, modificarea rezultatelor funcției inițiale.
  6. Efectuarea operației ret, revenirea controlului la programul care a apelat funcția.

Pentru a intercepta, este suficient să modificați primii cinci octeți ai funcției, în locul cărora este scrisă operația jmp, transferând controlul către interceptorul rootkit-ului.

Trebuie remarcat faptul că cele mai simple sisteme de protecție împotriva atacurilor de acest tip verifică primul octet al funcțiilor apelate pentru prezența codului operațional al mașinii jmp în ele. Ca o contramăsură, dezvoltatorii de rootkit folosesc tehnici pentru a „masca” codul scris la începutul funcției de interceptor (folosind comenzi precum PUSH / RET, plasând mai mulți operatori NOP sau cod gunoi precum PUSH AX / POP AX, precum și elemente de polimorfism ).

Metoda de modificare a primilor octeți de funcții are o serie de dezavantaje, legate în principal de necesitatea restabilirii codului de mașină al funcțiilor interceptate înainte de a le apela și reinterceptarea după apel. Aceste operațiuni reduc performanța sistemului și pot cauza blocarea aplicațiilor cu mai multe fire .

DKOM (Manipulare directă a obiectelor Kernel)

Sistemele de operare ale familiei Windows NT utilizează modele de obiecte standard. Diverse componente ale sistemului de execuție definesc unul sau mai multe tipuri de obiecte. Fiecare componentă exportă în modul kernel un set de funcții și proprietăți acceptate, numite interfață COM, pentru manipularea acelui tip de obiect. Nicio componentă nu poate accesa direct un alt obiect component. Obiectele tipice în modul kernel sunt [7] :

  • obiect tip dispozitiv (un tip de obiect în mod privilegiat definit de managerul I/O, folosit pentru a reprezenta un dispozitiv fizic, logic sau virtual)
  • obiect fişier
  • legături simbolice
  • chei de registry
  • fire și procese
  • obiect dispecer (o clasă de tip de obiect în mod privilegiat utilizată pentru a controla procesele de dispecerare și sincronizare)

Acest design oferă flexibilitate și portabilitate, de exemplu, versiunile viitoare ale sistemului de operare pot conține componente ale nucleului care definesc obiecte similare, dar au o structură internă complet diferită. Dacă astfel de componente vor exporta funcții cu nume și parametri păstrați, modificarea nu va avea niciun efect [3] .

Manipularea directă a obiectelor nucleului este o tehnologie destul de puternică, greu de descoperit. Cu toate acestea, există o serie de dezavantaje, cum ar fi instabilitatea metodei, dependența de versiune, complexitatea implementării din cauza lipsei unei descrieri documentate a structurilor și proprietăților obiectelor. În ciuda acestor limitări, această metodă vă permite să ascundeți procesele, driverele de dispozitiv, porturile și să ridicați privilegiile firelor de execuție (deci procese).

EPROCESS este o structură care servește ca reprezentare internă a unui proces (obiect de proces). Windows folosește o listă circulară dublu legată de structuri EPROCESS pentru a urmări progresul execuției. Legăturile care leagă obiectele EPROCESS sunt conținute în câmpul ActiveProcessLink, a cărui structură este LIST_ENTRY [8] :

typedef struct _LIST_ENTRY { struct _LIST_ENTRY * Flink ; struct _LIST_ENTRY * Clipește ; } LIST_ENTRY , * PLIST_ENTRY ;

Cel mai simplu algoritm de ascundere a procesului:

  1. Obținerea unui pointer către procesul căruia îi aparține firul curent apelând PsGetCurrentProcess()
  2. Obținerea PID-ului unui proces
  3. Dacă PID-ul nu se potrivește cu cel dorit, se face o tranziție printr-o listă dublu legată (câmp ActiveProcessLinks, tip LIST_ENTRY)
  4. Modificarea câmpurilor ActiveProcessLinks. În special, legătura către următorul bloc EPROCESS al blocului A este setată la blocul C, la fel și legătura către blocul anterior din blocul C. Legăturile blocului B sunt închise în înregistrarea lor. Astfel, sunt create două liste, dintre care una este formată dintr-un element

Excluderea unui proces din lista de procese nu afectează execuția acestuia. În Windows, codul este programat pentru execuție la nivel de fir, procesele definesc contextul în care rulează firele. Ascunderea unui proces se face extern în instrumente care se bazează pe obiecte de proces EPROCESS, cum ar fi Managerul de activități. Dispeceratul nucleului folosește o schemă contabilă diferită care se bazează pe alte structuri de date (în primul rând obiectul ETHREAD). Această metodă vă permite să ascundeți procesele fără a pierde funcționalitatea [9] .

Drivere

Modelul de driver Microsoft acceptă o arhitectură stratificată, astfel încât o solicitare I/O (cerere I/O, schimb de date între aplicații și drivere) poate fi deservită de o serie de drivere conectate , fiecare dintre ele își îndeplinește propria sarcină. Un lanț de drivere care deservesc un dispozitiv fizic se numește stivă. Această abordare modulară permite includerea de noi drivere în stivă pentru a crește funcționalitatea. În acest caz, doar o secțiune separată a lanțului este schimbată sau adăugată. De asemenea, unele periferice folosesc aceleași controlere (și, prin urmare, magistralele I/O). Modularitatea vă permite să optimizați utilizarea acelorași blocuri de cod, în loc să scrieți un driver separat pentru fiecare dispozitiv.

În modelul WDM sunt definite trei tipuri de drivere: driver de autobuz, drivere de funcții și drivere de filtru. Driverele de filtrare sunt de obicei amplasate între alte module și captează IRP -urile care trec prin ele . Înainte de a trimite IRP la driverul adiacent, filtrul poate examina conținutul sau îl poate modifica pentru a influența în continuare comportamentul sistemului. De exemplu, atunci când luați o imagine de disc de la un server critic pentru timp de nefuncționare, un driver de filtru poate fi utilizat pentru a modifica fluxul de date pentru a ascunde unele fișiere.

Pachetul IRP (pachetul de solicitare I/O) este o structură de date a nucleului Windows care oferă schimb de date între aplicații și driver, precum și între driver și driver. Când se primește o solicitare de la o aplicație, managerul I/O generează un IRP adecvat, care localizează și redirecționează către obiectul de sus din stiva de drivere. Dacă driverul de top a putut procesa singur IRP-ul de intrare, acesta completează cererea și returnează IRP-ul managerului I/O. În caz contrar, driverul efectuează o procesare parțială, localizează obiectul de bază pe stivă și cere managerului I/O să transmită IRP următorului driver.

La crearea unui IRP, managerul I/O își rezervă zona de memorie după antet. Memoria alocată este folosită pentru a scrie o matrice de structuri IO_STACK_LOCATION alocate pentru fiecare driver de stivă:

Dimensiunea memoriei corespunde numărului de drivere din stivă. Matricea este numerotată de la 1, corespunzătoare driverului stivei de jos. Structura conține informații despre funcția de control al driverului apelată de managerul I/O (câmpurile MajorFunction și MinorFunction), parametrii trecuți funcției (câmpul Parameters, conținutul variază în funcție de funcție), un pointer către obiectul driver (DeviceObject), un pointer către funcția de finalizare (câmpul CompletionRoutine, această funcție se află în driverul de nivel superior).

Funcția de control a șoferului, la prima primire a unui IRP, restabilește parametrii din poziția corespunzătoare a stivei de I/O apelând IoGetCurrentIrpStackLocation(). În continuare, sunt efectuate acțiunile prescrise, după care, în cazul redirecționării IRP-ului către driverul de stivă inferior, are loc următoarele:

  • setarea poziției stivei I/O în IRP
  • înregistrare funcție de terminare (opțional)
  • trimiterea unui IRP către driverul din aval
  • cod de stare de returnare (NTSTATUS)

Există două moduri standard de a seta poziția stivei pentru următorul driver [10] :

  • Poziția curentă este trimisă fără modificări, funcție:
VOID IoSkipCurrentIrpStackLocation ( IN PIRP Irp );

Funcția decrește indicatorul către matricea IO_STACK_LOCATION cu unul. Astfel, la redirecționarea IRP-ului, pointerul va fi restaurat (mărește automat cu unul), ca urmare, se va folosi aceeași secțiune a stivei. Când utilizați această metodă, va exista o zonă nefolosită la capătul stivei.

  • Dacă este necesar să treceți conținutul poziției curente a stivei, cu excepția indicatorului către funcția de completare (câmpul CompletionRoutine), utilizați:
VOID IoCopyCurrentIrpStackLocationToNext ( IN PIRP Irp );

Redirecționarea unui IRP către următorul driver se face folosind funcția:

NTSTATUS IoCallDriver ( IN PDEVICE_OBJECT DeviceObject , IN OUT PIRP Irp );

Primul argument este un pointer către obiectul driver subiacent. Metoda de obținere a unei astfel de adrese este determinată de funcția de control specifică, nu există o metodă standard.

Fiecare cerere trebuie să fie terminată fie de ultimul driver din stivă (nu este posibilă o redirecționare suplimentară a IRP-ului), fie de către unul dintre driverele din amonte.

Managerul I/O inițiază procesul de finalizare pentru un anumit IRP atunci când oricare dintre driverele de procesare IRP apelează funcția de finalizare IoCompleteRoutine(). Când este apelat, managerul I/O umple stiva I/O a driverului curent cu zerouri, apoi apelează driverul de nivel superior cu funcția de terminare setată la acest IRP. Doar blocul de stare I/O din IRP este disponibil pentru a determina modul în care cererea este gestionată de driverul de nivel inferior al funcției de finalizare a driverului de nivel superior.

De fapt, driverul de filtru instalat în acest fel vă permite să procesați nu numai pachetele IRP primite (de exemplu, blocarea citirii unui anumit sector de disc), ci și să gestionați rezultatele procesării driverelor din aval prin inițializarea funcției de terminare [11] .

O altă metodă de implementare a rootkit-urilor este modificarea MBR -ului și pornirea la kernel-ul sistemului de operare - bootkit -uri (de exemplu, BackDoor.MaosBoot).

Acest tip de cod rău intenționat în mediul Windows este cunoscut încă de la începutul anilor 1990 sub numele de viruși stealth .

Pe UNIX și Linux

  • implementate prin înlocuirea principalelor utilități de sistem (foarte ușor de detectat prin controalele de integritate, în plus, sunt ușor blocate de instrumente obligatorii de control al accesului precum SELinux sau AppArmor );
  • implementat ca modul de nucleu și bazat pe corecțiile VFS sau interceptarea tabelelor de apeluri de sistem (sys_call_table);
  • bazat pe modificarea memoriei fizice a nucleului.

Caracteristici suplimentare

În plus față de el însuși, un rootkit, de regulă, poate masca prezența în sistem a oricăror directoare și fișiere descrise în configurația sa de pe disc, chei din registru . Din acest motiv, bibliotecile rootkit „montate” au apărut în mod natural. Multe rootkit-uri își instalează propriile drivere și servicii în sistem (desigur, sunt și „invizibile”).

Rootkit-uri pentru și împotriva DRM

Rootkit-urile, de fapt, sunt cele mai multe programe de protecție împotriva copierii (și mijloacele de a ocoli aceste protecții - de exemplu, emulatori de unități CD și DVD ) .

În 2005, Sony BMG Corporation a încorporat protecția bazată pe rootkit în CD-urile sale audio , care s-au instalat fără știrea utilizatorului.

Anti-rootkit-uri

Acestea sunt utilitare sau module rezidente care detectează prezența rootkit-urilor în sistem și (în diferite grade) le elimină. Există multe instrumente concurente pentru aceasta - atât plătite, cât și gratuite, dar toate folosesc principii similare.

Metode de detectare a rootkit-urilor

Există un algoritm cunoscut pentru capturarea rootkit-urilor MEP. Esența sa constă în faptul că aceeași informație este înregistrată în mai multe moduri - folosind API și „direct”, după care datele primite sunt comparate în căutarea discrepanțelor. Tabelele de import și tabelele de  apeluri native API sunt scanate cel mai frecvent , precum și structural întregul sistem de fișiere.

Arsenalul de bază al instrumentelor de captare rootkit se bazează pe următoarele metode.

  1. căutare de semnături. A fost folosit încă de pe vremea primelor antivirusuri și este o căutare în fișierul scanat pentru un lanț unic de octeți (semnătură) inerent unui program rău intenționat.
  2. Analizor euristic sau comportamental. Această tehnologie se bazează pe găsirea abaterilor în setările sistemului, fișierele de configurare Linux sau registrul Windows, comportamentul suspect al proceselor și modulelor și așa mai departe.
  3. Controlul integrității. Acest tip de căutare se bazează pe compararea sumei de control (MD5 și altele asemenea) sau a semnăturii digitale a diferitelor fișiere de sistem cu o bază care conține suma de control a fișierelor originale. În cazul unei nepotriviri, programul ajunge la concluzia că fișierul a fost modificat sau înlocuit complet.

Note

  1. Kolesnichenko, 2006 , p. 29.
  2. Kolesnichenko, 2006 , Rescrierea adresei funcției.
  3. 1 2 Solomon, Russinovici, Ionescu, 2012 .
  4. Blunden, 2009 .
  5. Blunden, 2009 , Injectarea unui DLL.
  6. Zaitsev, 2006 , Interceptarea prin modificarea primilor octeți ai unei funcții.
  7. Gestionarea obiectelor Kernel  : [ ing. ] // MSDN .
  8. Blunden, 2009 , Capitolul 7 Alterarea obiectelor Kernel.
  9. Blunden, 2009 , Capitolul 7. Alterarea obiectelor Kernel.
  10. Diferite moduri de procesare IRP - Referință rapidă  : [ ing. ] // MSDN .
  11. Zaitsev, 2006 , Spionul tastaturii bazat pe un driver de filtru.

Literatură

  • Zaitsev O. Rootkit-uri, SpyWare_AdWare, Keyloggers și BackDoors. Detectare și protecție / Oleg Zaitsev. - Sankt Petersburg: BHV-Petersburg, 2006.
  • Blunden B. The Rootkit Arsenal / Bill Blunden. — Plano, Texas: Wordware Publishing, Inc., 2009.
  • Kolesnichenko D. ROOTKITS sub Windows / Denis Kolesnichenko. - Sankt Petersburg: Știință și tehnologie, 2006.
  • Solomon D., Russinovich M., Ionescu A. Windows Internals / David Solomon, Mark Russinovich, Alex Ionescu. — Microsoft Press, 2012.

Link -uri