Caracteristicile accesului la funcțiile sistemului de kernel al OS GNU / Linux
A. M. Kanner
Societatea pe acțiuni închisă OKB CAD, Moscova, Rusia
B. P. Los, dr. Tech. științe
Universitatea de Stat de Stat din Moscova (MGIU), Institutul de Criptografie, Comunicare și Informatică al Academiei Serviciului Federal de Securitate al Rusiei, Moscova
Linux kernel astăzi, probabil, este singura componentă unitară și unică pentru fiecare distribuție Linux. Cei mai mulți furnizori majori (RedHat, Novell, Canonice și t. D.) disponibile modificări specifice surselor de kernel, dar astfel de modificări sau nu ar trebui să interfereze cu codul de referință (numit „vanilie“ miez), sau pot fi introduse în continuare în principal ramură a kernel-ului, astfel încât nucleul este unul pentru toți.
Având în vedere această caracteristică pentru familia Linux este destul de simplu (cu excepția unor probleme de compatibilitate cu versiunile anterioare ale kernel-ul) pentru a dezvolta diferite extensii posibile - module de kernel, care sunt de fapt o parte a miezului și să treacă peste / suplimenta diverse funcții sale, și anume, pentru a schimba ordinea .. kernel-ul Linux. Și astfel de module, cu unele rezerve, vor fi compatibile cu cele mai multe distribuții Linux fără a face modificări în codul lor.
Astfel, kernel-ul Linux este monolitic (de exemplu, conține caracteristici suficiente pentru funcționarea normală a sistemului, fără alte adăugiri / extensii ..), dar suporta module de kernel incarcabile (LKM - Linux Kernel Module sau descărcabile Kernel Module), care sunt executate în inel de protectie 0th, cu acces deplin la echipamentul, în care încărcarea / descărcarea acestor module pot fi efectuate în timpul funcționării sistemului (kernel) fără a restarta.
La prima vedere, o astfel de abordare poate părea problematică din punct de vedere al securității, însă este necesar să se înțeleagă că:
- Toate modulele kernel-ului pot fi încărcate / descărcate în spațiul kernel-ului Linux doar cu drepturi superuser (root);
- la bază, există mecanisme speciale pentru a preveni descărcarea critice a modulelor de kernel în momentul muncii lor (kernel-ul implicit va MODULE_FORCE_UNLOAD opțiunea = 0, adică fără posibilitatea de descărcare forțată a modulelor de kernel cu parametrul -Force - .. „rmmod -Force module.ko“ );
- Modulele în sine nu au permisiunea explicită de a efectua acțiuni care pot afecta sistemul de funcționare (de exemplu, modificarea datelor structurilor proceselor care rulează, accesarea memoriei kernelului etc.). Pentru astfel de acțiuni, sunt necesare manipulări preliminare, dar este posibil ca aceste acțiuni să fie posibile și să nu fie interzise atunci când dezactivați încuietori, cum ar fi GFP (General Fault Protection).
În acest sens, se poate argumenta că modulele de kernel care se încarcă nu pot fi ele însele un mijloc de creștere a privilegiilor în sistem și / sau de vulnerabilitate a sistemului. Scopul utilizării modulelor de kernel descărcate din punctul de vedere al unui atacator este de a-și ascunde propria prezență în sistem și nu mai mult, adică în acțiuni imediat după hacking-ul unui singur sistem.
Ce ar putea fi util pentru descărcarea modulelor kernel-ului Linux în ceea ce privește îmbunătățirea securității sistemului și / sau dezvoltarea unei protecții "cu balamale"? Astfel de module pot fi folosite pentru a intercepta funcțiile sistemului de kernel pentru a-și organiza propriul subsistem independent de OS pentru controlul accesului.
Interfața sistemului de kernel Linux
Interfața de apel sistem este un strat în kernelul OS, unde software-ul aplicației (din modul utilizator) poate accesa hardware-ul, poate lucra cu sisteme de fișiere etc. Astfel, practic orice acțiune din sistem necesită apelarea acelei în caz contrar apelul de sistem (dacă scriere / citire un fișier, permisiuni de schimbare, lansarea unui nou proces sau orice acțiuni cu alocare / deallocation), precum și schema generală de lucru cu apeluri de sistem poate fi reprezentat schematic în Fig. 1.
Fig. 1. Utilizarea funcțiilor sistemului kernel-ului Linux de către aplicațiile utilizatorilor
Interceptați apelurile de sistem
Fig. 2. Interceptarea unui apel de sistem folosind modulul kernel-ului Linux
Acum, dacă module_B este descărcat primul - nu se va întâmpla nimic rău în sistem - atunci când descărcați modul_A în tabelul de apeluri sistem, apelul inițial "deschis" va fi restabilit. Cu toate acestea, în cazul în care prima module_A descărcate, va fi restaurat apelul sistem original „deschis“, și atunci când descărcarea module_B apel de sistem „deschis“ va fi înlocuit cu orep_A (care, în general, nu mai este în memorie).
Utilizarea mecanismului LSM pentru a intercepta apelurile de sistem
Luați în considerare un exemplu de modul kernel LSM care va înlocui apelul standard de sistem mkdir cu dispozitivul de procesare a acestuia, care, în plus față de crearea directorului, va transmite un mesaj către dmesg de fiecare dată când se numește comanda mkdir:
1. Pentru ca modulul kernel să înceapă să utilizeze mecanismul LSM, în funcțiile sale de inițializare și deinitializare (modul_init și modul_exit) este necesar să se adauge apelul la următoarele două funcții:
/ * Înregistrarea interceptorilor apelurilor de sistem
* @return 0 în caz de înregistrare reușită, altfel - cod de eroare * /
int res = registru de securitate (hook_security_ops);
dacă (res) <
printk (KERN_ERR. res); return res;
>
retur 0;
>
/ * Deregisterarea interceptorilor apelurilor de sistem * /
int res = unregister_security (hook_security_ops);
dacă (res) printk (KERN_ERR. res);
2. După cum se vede din exemplul de mai sus modulul LSM-kernel detectează stivuitoare proprii prin intermediul register_security () funcții (prin unregister_security anulează înregistrarea (), ambele funcții sunt declarate în), care trec această structură caracteristică:
/ * Operațiuni de interceptare a apelurilor de sistem * /
operațiunile de securitate struct structură hooksecurityops = <.inodernkdir = inodernkdir,>;
3. În acest caz, un sistem de security_operations structura de tip enumerate apeluri, împreună cu funcții (cârlige noastre), care sunt executate imediat înainte de executarea apelului sistemului menționat (în acest caz, înainte ca apelul sistem este executat inode_mkdir inode_mkdir funcția noastră, care trebuie să fie declarate în același miez modulului ).
4. Funcția însăși, apelată înainte de efectuarea apelurilor de sistem, poate fi, de exemplu, următoarea:
/ * Cerere de interceptare pentru a crea un director * /
static int inode_mkdir (struct inode * dir, dentare struct * dentry, mod int)
printk (); retur 0;
5. În acest caz, încărcați modulul în kernel-ul Linux, iar comanda mkdir, în producția dmesg veți vedea o nouă linie cu expresia «mkdir deturnat!», Indicând că LSM-modulul intercepteaza cu succes inode_mkdir apelul sistem.
O caracteristică a utilizării mecanismului LSM este incapacitatea de a utiliza mai multe module de kernel pentru a înregistra cârlig - atunci când încercați să verificați kernel LSM-modulul este un avertisment, astfel încât atunci când se utilizează un standard compilat în cele mai multe versiuni de kernel SELinux va afișa utilizând propriul modul de securitate LSM nu va funcționa (SELinux off cu folosind parametrul kernel în tipul încărcător SELINUX = 0 nu poate aduce nici un rezultat). Prin urmare, pentru utilizarea gratuită a unui nucleu personalizat LSM modul trebuie să recompilați kernel-ul Linux, excluzând din acestea orice echipament de protecție suplimentare (cum ar fi SELinux, AppArmor, Tomoyo, etc).
Oricum, din motive de securitate, începând cu versiunea 2.6.24 de nucleu Linux și deasupra mecanismului LSM (și anume, permițându-vă să utilizați acest mecanism) a încetat să mai fie exportate de bază a sistemului de operare Linux (acest lucru se datorează faptului că cele mai multe programe malware ascunde prezența în sistem cu ajutorul mecanismului LSM).
Interceptarea apelurilor de sistem în modulele de încărcare kernel Linux (LKM)
Revenind la interceptarea obișnuită a apelurilor de sistem (și respingând problema posibilei deteriorări a tabelului apelurilor de sistem, adică fără a avea alte opțiuni pentru interceptarea corectă a apelurilor de sistem), două probleme trebuie rezolvate imediat:
Sarcina căutării tabelului de apeluri de sistem poate fi rezolvată în mai multe moduri:
// valori pentru kernel-urile Linux pe 32 de biți
#define START_MEM 0xc0000000
#define END_MEM 0xd0000000
nesemnate lung * syscall_table;
nesemnate lung ** find_syscall_table () <
nesemnate;
nesemnate lung int i = START_MEM;
în timp ce (i dacă (sctable [_NR_close] == (nesemnate lung *) sys aproape) întoarcere sctabil [0]; i + = dimensiunea (void *); // găsiți tabelul apelurilor de sistem după cum urmează: syscalltable = (unsigned long *) find_syscall_table (); / * dezactivați modul protejat prin setarea bitului WP la 0 * / write_cr0 (read_cr0 () ( / * efectuați modificări în tabelul de apeluri de sistem * / / * pornește modul de încărcare, setând bitul WP la 1 * / write_cr0 (read_cr0 () | 0? 10000); În acest caz, dispozitivul de blocare este asociat cu arhitectura procesorului utilizat, ca un exemplu considerat Intel blocare, în care 0-lea bit CR (registru de control, Registrul de control) trebuie să fie pornit la 0 pentru a dezactiva «modul protejat», și apoi la 1 pentru a permite «protejată mod "după schimbarea tabelului de apeluri de sistem. De asemenea, există alte încuietori, în funcție de arhitectura pe care este folosit sistemul de operare. Mecanismul de înlocuire a apelurilor de sistem cu funcții native poate fi organizat în următoarele moduri: / * funcție nouă, înlocuind apelul standard de sistem * / printk (<\nmkdir hijacked!\n>); / * funcția de modificare a tabelului de apeluri sistem * / #if LINUX VERSION CODE> = KERNEL_VERSION (2, 5,0) #if LINUX VERSION CODE> = KERNEL_VERSION (2, 5, 0) / * funcția de returnare a tabelului de apeluri de sistem la starea inițială * / #if LINUX VERSION CODE> = KERNEL_VERSION (2, 5, 0) #if LINUX VERSION CODE> = KERNEL_VERSION (2, 5, 0) / * funcția de inițializare a modulului kernel * / / * aplicați patch-ul în tabelul de apeluri sistem * / patch_sys_call_table (); printk ("sys_call_table patched. \ n"); / * funcția de terminare a modulului kernelului * / / * returnează starea tabelului de apeluri de sistem * / Crearea unui subsistem proprietate de control al accesului în sistemul de operare al familiei Linux nu este dificil din punct de vedere tehnic sau inaccesibil. Așa cum sa arătat în acest articol, este destul de simplu să se obțină controlul asupra funcțiilor sistemului de kernel (apeluri sistem), astfel încât sarcina principală este de a dezvolta acele reguli de testare care ar trebui să fie numite atunci când se execută un anumit apel de sistem al kernel-ului OS. [1] Chris Wright, Crispin Cowan, Stephen Smalley, James Morris, Greg Kroah-Hartman. Module de securitate Linux: general
asmlinkage int newrnkdir (struct inode * dir, dentry struct * dentry, mod int)
returnează orig mkdir (dir, dentry, mode);
static void patch_sys_call_table ()
writecr0 (readcr0 () (
scrie cr0 (readcr0 () | 0? 10000);
# endif>
static void revert_sys_call_table ()
scrie cr0 (citiți cr0 () (
scrie cr0 (citiți cr0 () | 0? 10000);
# endif>
static int init (vid)
retur 0;
ieșire statică (void)
revert_sys_call_table (); printk ("sys_call_table a revenit. \ n<); return;
Articole similare