Terapia zamiast łat

Plomba twarda lub miękka

Podstawą powodzenia nadpisania bufora jest możliwość wykonania kodu umieszczonego w obszarze pamięci zdeklarowanym jako dane, a nie kod wykonywalny. Niektóre procesory ściśle rozdzielają obszary pamięci przeznaczone dla danych od obszarów przeznaczonych dla kodu, co skutecznie uniemożliwia ataki przez przepełnienie bufora. Procesory 32-bitowe Intela i kompatybilne z nimi układy innych producentów w obecnych wersjach nie mają takiego ograniczenia. Możliwość zablokowania wykonywalności wskazanych obszarów pamięci ma się pojawić w procesorach AMD K8 i kolejnych wersjach Intel Itanium jako flaga NX (non-executable).

Microsoft zapowiada, że do obsługi tych funkcji gotowy jest system Windows XP z Service Pack 2 (SP2).

Niezależnie od rozwiązań sprzętowych, od kilku lat podejmowano próby stworzenia rozwiązania pozwalającego na zablokowanie ochrony pamięci przy użyciu metod programowych. Pierwszą tego typu implementacją była technika Solar Designera, wykorzystana w systemie OpenWall Linux. Podobna technika została zaimplementowana także przez firmę Sun w systemie Solaris. Późniejsze prace zespołu PAX (pageexec.virtualave.net) zaowocowały stworzeniem kolejnych dwóch metod - efektywniejszej, ale dostępnej tylko dla architektury Intela techniki opartej na segmentacji pamięci oraz drugiej, mniej optymalnej, lecz dostępnej także dla architektur DEC (Alpha), Sparc i innych. Programowe metody weryfikowania pamięci w poszukiwaniu błędnie zaalokowanego kodu wykonywalnego powodują z reguły kilkuprocentowe zmniejszenie wydajności systemu. Metoda PAX oparta na segmentacji pamięci jest przy tym relatywnie najmniej obciążająca.

Wszystkie techniki ograniczania wykonywalności obszarów pamięci, zarówno wspierane sprzętowo, jak i czysto programowe, powodują problemy z aplikacjami, które z różnych przyczyn muszą na bieżąco generować kod wykonywalny. Dotyczy to np. niektórych emulatorów czy środowisk Java JIT (kompilacja w locie). Obie te metody są od dawna z powodzeniem stosowane w różnych wersjach Linuxa - także w środowiskach produkcyjnych. Są skuteczne zwłaszcza w przypadku nowych, nieznanych typów ataków, przed którymi nie da się zabezpieczyć systemów nawet poprzez częste instalowanie poprawek.

Analogiczny mechanizm ma również unixowy system OpenBSD. Użytkownicy Windows muszą niestety jeszcze poczekać.

Kuracja genetyczna

Do zabezpieczania systemu można i trzeba podejść od strony jego tworzenia. Chodzi o to, aby uniemożliwić powstawanie luk już na etapie pisania kodu lub najpóźniej podczas jego kompilacji. Istnieją narzędzia umożliwiające kompilatorowi śledzenie procesu przekazywania argumentów i adresów powrotu z funkcji. W kluczowych z punktu bezpieczeństwa fragmentach kodu kompilator umieszcza własny dodatkowy kod pozwalający zweryfikować integralność stosu lub sterty. W razie wykrycia jej naruszenia następuje awaryjne zakończenie pracy programu - jeszcze zanim umieszczony w pamięci "złośliwy" kod zostanie wykonany.

Pierwszą implementacją wbudowanego w kompilator GNU C/C++ "mechanizmu immunologicznego" był StackGuard (http://www.immunix.org ). Podobne zabezpieczenie pojawiło się w kompilatorze Microsoftu. Oba rozwiązania dość skutecznie zabezpieczają przed wieloma typami ataków, nie są jednak doskonałe. Inna implementacja mechanizmu kontrolnego przeznaczona dla kompilatorów GNU C/C++ to StackShield. Zachowuje ona adres powrotu z funkcji w miejscu nienarażonym na nadpisanie, a następnie przywraca go tuż przed przekazaniem sterowania do kolejnej funkcji.

Najdoskonalszym obecnie mechanizmem kontroli kodu na etapie kompilacji jest stworzony przez IBM pakiet ProPolice przeznaczony dla kompilatorów GNU C/C++. W porównaniu z wymienionymi wcześniej rozwiązaniami charakteryzuje się on większą skutecznością testów integralności i jest mniej obciążający dla systemu. Jest też niezależny od platformy sprzętowej i systemu operacyjnego. Nie jest konieczne wprowadzanie zmian do jądra systemu. Cechy te zostały docenione przez autorów OpenBSD, który jest obecnie w całości kompilowany za pomocą ProPolice, podobnie jak dwie dystrybucje Linuxa - Gentoo oraz Adamantix.

Grunt to profilaktyka

Stworzenie tzw. exploita, czyli złośliwego kodu mogącego wykorzystać luki w różnych wersjach systemów, to zadanie bardzo trudne, choć wykonalne. Paradoksalnie, tworzone w wielu firmach firmowe "obrazy" systemów operacyjnych dla stacji roboczych i serwerów (mające zwykle na celu m.in. podniesienie poziomu bezpieczeństwa) ułatwiają pracę autorom exploitów. Taki program uruchomiony w homogenicznym środowisku za każdym razem otrzyma do dyspozycji identycznie adresowane obszary pamięci. To wręcz idealne warunki do "hodowli" wirusów i koni trojańskich.

Sposobem na obejście tego problemu jest tzw. randomizacja układu i adresowania pamięci. Mechanizm randomizacji pamięci powoduje, że w każdej instancji danego systemu działający program będzie miał inną strukturę pamięci, co czyni zaprojektowane dla niego tzw. exploity nieskutecznymi. Atakujący jest zmuszony do zgadywania interesujących go adresów pamięci, co z dużym prawdopodobieństwem spowoduje naruszenie integralności danego programu i awaryjne zakończenie jego pracy. Implementacja randomizera dla Linuxa została stworzona w ramach projektu PAX. To rozwiązanie godne polecenia, tym bardziej że jego wpływ na wydajność jest niewielki.


TOP 200