Bezpieczne systemy operacyjne - SELinux

Michał Wieczorek

Przepełnienie bufora

Atak wykorzystuje tablice, przy zapisie do których nie jest sprawdzany zakres. Pisząc poza ich zakresem można zamazać inne zmienne lub rekord aktywacji procedury. Aby zrozumieć jak dokładnie działa atak, trzeba się przyjrzeć organizacji pamięci podczas wywoływania procedur.

Organizacja stosu

Stos służy do przechowywania argumentów, zmiennych lokalnych oraz rekordu aktywacji procedury.

W architekturze intela stos rośnie w dół (gdy odkładamy nań jakiś element wartość rejestru SP zmniejsza się). Adresy kolejnych komórek tablic rosną w przeciwną stronę.
Wywołanie procedury zazwyczaj polega na:

Atak

Sam atak polega na podaniu ciągu danych, na tyle długiego, aby nadpisał adres powrotu, oraz na ustaleniu wartości jaka powinna się w nim znaleźć.

Jako, że stosy procesów mają zwykle ten sam adres wirtualny przy każdym wywołaniu programu (no prawie patrz PaX) atakujący można podać we wprowadzonym buforze kawałek skompilowanego kodu, i podmienić adres powrotu na wskaźnik do niego. Przy tworzeniu kodu będzie pamiętał o kilku rzeczach:

Może też podmienić go na adres jakiejś zewnętrznej funkcji (np.: execve) i umieścić na stosie argumenty dla niej

Przykład

Katalog z przykładem

Programy korzystają ze standardowego wejścia, żeby działał jeden z drugim można stworzyć łącze fifo i przekierować do niego odpowiednie ich deskryptory. Długość danych do przepełnienia bufora można sprawdzić wpisując coraz dłuższy tekst na wejście leaky (wystarczająca długość zaprezentyje sie przy pomocy Segmentation fault). Adres skoku trudniej zgadnąć, proponuje zabaczyć gdzie kończy się stos dla funkcji main (prgram get_sp) i podawać powrotu coraz mniejszy (stos jak pamiętamy rośnie "w dół").

Niebezpieczne konstrukcje

Najprostszym sposobem, na znalezienie miejsc podatnych na atak jest grepowanie kodu. Atakujący poszukują wywołań funkcji:

strcat(), strcpy(), sprintf(), vsprintf()

Nie sprawdzają one zakresów tablic do których piszą.

gets()

Czyta całą linię ze standardowego wejścia.

scanf(), fscanf()

Formatowanie %s oznacza wczytanie ciągu nie pustych znaków.

getc(), fgetc(), getchar()

Funkcje te czytają po jednym znaku. Niebezpieczne są w pętlach, które nie sprawdzają rozmiaru bufora, czytając wszystko do końca linii, napisu lub pliku.

Bibiografia