zbeng Posted August 9, 2006 Report Posted August 9, 2006 1. Conceptul de regiuni de memorie non executabile2. Metode de oprire a executiei codului injectat3. Implementare proprie.3.1 Apeluri de sistem si interceptarea lor.3.2 Utilizarea interceptarii in scopuri non executabile.3.3 Intrarea in kernel mode fara suport lkm.3.4 Etapele aplicarii patch-ului.1.Exploiturile se bazeaza in general pe urmatorul concept: directionarea executiei programului la un set de instructiuni (shellcode) furnizate de atacator.Prin faptul ca shellcode-ul este primit sub forma de date de intrare, este plasat intr-una din urmatoarele portiuni de memorie: stack, heap, .data sau .bss.Impiedicare executiei unui shellcode se poate face deci prin marcarea logica a portiunilor de memorie mentionate mai sus ca non executabile .O implementare a conceptului a fost realizata de Solar Designer impotriva executiei codului din stack prin proiectul Openwall (openwall.com/linux).Un proiect mai nou de acelasi gen este PaX (http://pageexec.virtualave.net). PaX impiedica executia din toate zonele de memorie writable, aplica metode degenerare aleatorie a regiunilor de memorie alocate pentru protectie impotriva tehnicilor return-into-libc, ... .2.Discutarea crearii unui soft pentru marcarea regiunilor de memorie ca non executabile are sens pe sistemele fara suport hardware pentru acest lucru.Principala si singura exceptie pana acum sunt arhitecturile IA-32. PaX implementeaza memoria non executabila prin urmatorul mod: de fiecare data cand accesam opagina de memorie in linux, daca este setat bitul supervisor in PTE ( page table entry ), actiunea normala in cazul accesului unui program din ring 3 (user space) estetrimiterea semnalului SIGSEGV programului. PaX seteaza supervisor in toate zonele writable de memorie si modifica functia care se ocupa de protectia paginilor cubitul supervisor cu o functie proprie.In cazul cererii de executie a memoriei functia testeaza daca executia din zona respectiva de memorie este permisa. In caznegativ programul este terminat si are loc scrierea in loguri a incidentului.3.3.1In linux toate functiile puse la dispozitia utilizatorului se executa prin intermediul apelurilor de sistem (system calls). Un apel de sistem este executatprin apelarea intreruperii 0x80 cu numarul apelului in registrul eax si cu parametrii in ebx,ecx,edx. Intreruperea 0x80 trece in kernel mode si apeleaza functiasystem_call, care preia adresa functiei corespunzatoare apelului de sistem cerut dintr-un tabel de adrese de memorie ( sys_call_table ). RootKit-urile clasicede kernel isi fac treaba prin modificarea adreselor din tabela respectiva.O alta idee de a intercepta apelurile de sistem a fost inovata de sd si devik prin SucKIT (http://www.phrack.org/phrack/58/p58-0x07). Ideea estede a modifica adresa sys_call_table in system_call.Alterarea executiei apelurilor de sistem se poate face si prin schimbarea functiei corespunzatoare int 0x80 in IDT ( interrupt descriptor table ).(http://www.phrack.org/phrack/59/p59-0x04.txt)Ideea prezentata de acest articol este de a redirectiona toate apelurile de sistem prin inserarea la inceputul functiei system_call a unui ansamblu push - ret, determinandastfel fiecare apel de sistem sa treaca intai prin codul nostru.3.2.La apelul functiei system_call eip-ul ( instruction pointer) de provenienta se gaseste pe stack. Impiedicarea executiei codului din portiuni de memorienepermise ( stack, heap, .data, .bss) se poate face prin verificarea apartenentei eip-ului extras unei reuniuni de intervale de memorie : stack ( in linux0xbf000000-0xbfffffff), heap ( current->mm->start_brk pana la current->mm->brk ) , .data ( current->mm->start_data pana la current->mm->end_data) si .bss.0x0807814c->0x080790e4 at 0x0002f14c: .data ALLOC LOAD DATA HAS_CONTENTS0x08079110->0x08079f78 at 0x00030110: .bss ALLOC0x08079f78 : current->mm->start_brk0xxxxxxxx : current->mm->brkAvem mai sus un exemplu de asezare a zonelor de memorie writable a unui elf. Din asezare se vede ca multimea zonelor protejateeste cuprinsa in stack ( 0xbf000000-0xbfffffff) si current->mm->start_data --- current->mm->brk.Functia noastra trebuie sa testeze daca eipul salvat pe stack apartine zonei descrise ; daca da apeleaza int 0x80 cu eax 0x1 ( exit ), dacanu executa primi 6 bytes inlocuiti din system_call si apoi sare la system_call+6.3.3Sarcinile de mai sus pot fi indeplinite de un loadable kernel module. Calea pe care am ales-o este de a scrie in /dev/kmem - fisierul prin care se poateciti sau scrie in memoria kernelului. Tehnica a fost aplicata pentruprima data de Silvio Cesare ( http://www.big.net.au/~silvio/runtime-kern...em-patching.txt ).Locul in care va fi plasata functia de filtrare este spatiul alocat in memoria kernel pentru sys_olduname.Obtinerea variabilei current se obtine prin macroul definit in entry.S din sursa kernelului#define GET_CURRENT(reg) movl $-8192, reg; andl %esp, regMai ramane o problema: determinarea structurii task_struct si mm_struct (este diferita pentru aproximativ fiecareversiune de kernel) pentru a determina current->mm->start_data si current->mm->brk.In sys_getuid avem urmatoarea portiune de cod:mov $0xffffe000,%eaxand %esp,%eaxmov 0x58(%eax),%eaxandb $0xfe,0x74(%eax)primele doua instructiuni extrag current iar ultimele adreseaza current->mm->dumpable care se afla de obiceila o distanta de 15*4 bytes dupa start_data. Din codul de mai sus extragem distanta mm in task_struct (0x5b) sidistanta dumpable in mm_struct (0x74) si le introducem in codul propriu.3.4 Etape:1. determinarea adresei IDT ( interrupt descriptor table ) prin instructiunea sidt2. aflarea din IDT a adresei functiei system_call3. prepararea functiei prin extragerea din sys_setgid a distantei mm in task_struct si dumpable in mm_struct4. adaugarea primilor 6 bytes din system_call la sfarsitul functiei de filtrare5. adaugarea unui push $(system_call+6) - ret la sfarsitul functiei noastre6. introducerea functiei in sys_olduname.7. inserarea unui push $sys_olduname - ret in system_call. Quote