Od redakcji: Tekst może zostać uznany za dość historyczny (w sensie, opisuje wydarzenia sprzed miesięcy), jednak warto na niego zwrócić uwagę w kontekście problemów z bezpieczeństwem nowych kerneli linuksowych.
Każdy dobry programista powinien wiedzieć, jak pisać programy by były bezpieczne. Mimo to, wraz z rozrostem kodu coraz trudniej uniknąć pomyłek i pewnie niejeden czytelnik już sie o tym przekonał. Błędy w aplikacjach użytkownika, zdarzają się bardzo często, jednak nie tylko programy użytkowe są narażone na podatności. Również jądro Linuksa nie jest wolne od niedociągnieć, o czym mogą świadczyć odkrycia grupy iSEC.
Jednym z popularniejszych błędów występujących w kernelu jest przeładowanie zmiennej integer, a następnie użycie jej podczas alokowania pamięci. Do tej pory uważano, że tego typu podatności są niemożliwe do praktycznego wykorzystania (to znaczy uzyskania dzięki nim przywilejów administatora). Na szczęście znalazł się pasjonat, który pokazał światu że jednak się da.
Po krótce spróbuję opisać w czym tkwi problem, tak aby osoby nie zaznajomione z tematem mogły się czegoś nowego dowiedzieć. Weźmy następujący kod programu użytkownika:
char *bufor = malloc(size + 64);
Alokuje on pamięć dla bufora o wielkości „size + 64”. Zmienna size jest kontrolowana przez nas, możemy więc ustawić jej wartość na ujemną. Przykładowo, jeśli będzie to -63, dla bufora zostanie zaalokowany jeden bajt. W następstwie tego niedopatrzenia, w dalszej części programu mogą wystąpić inne błędy – przepełnienia sterty. Program będzie sądził że bufor ma minimum 64 bajty i będzie umieszczać w nim dane które „wyciekną” poza niego. Jest to dobrze znany, opisany i przez psujki bardzo lubiany problem. Z obszernymi publikacjami na ten temat można zapoznać się na przykład w magazynie Phrack. Luki te są stosunkowo trudne w eksploitacji, ale umożliwiają uruchomienie dowolnego kodu.
W jądrze Linuksa sprawa ma się nieco inaczej. Do alokacji pamięci nie korzysta ono z funkcji libc, wykorzystuje zupełnie odmienne mechanizmy. Na poziomie kernela, wygląda to tak:
char *bufor = kmalloc(size + 64,GFP_KERNEL);
Kod ten, jak by sie można było spodziewać, zaalokuje size+64 bajtów. Jeśli size będzie ujemne, będziemy mogli nadpisać „któryś” z regionów pamięci w jądrze.
Dyskusja na temat tego, czy możliwe jest wykorzystanie tego typu błędów, rozpoczęła się wraz z opublikowaniem przez iSEC informacji o błędzie w funkcji ip_setsockopt(), gdzie czytamy:
3. Impact
Proper exploitation of this vulnerability leads to local privilege escalation giving an attacker full super-user privileges. Unsuccessful exploitation of the vulnerability may lead to a denial-of-service attack causing machine crash or instant reboot.
Znalawcy pokusili się o stwierdzenie, że odpowiednia eksploitacja może doprowadzić do eskalacji przywilejów. Ja jednak, po wielogodzinnych próbach, nie mogłem z czystym sumieniem przyznać im racji. Główny problem leży właśnie w tym, że nie wiemy co tak naprawde nadpisujemy. Kilka osób „ze sceny” również miało wątpliwości w tej sprawie. Warto tutaj przywołać obszerny artykuł autora o ksywie „infamous”, w którym znajdziemy pozorne „dowody” na to iż wykorzystanie kmalloc() overflows jest niemożliwe. Oprócz tego znajduje się też tam opis linuksowego slab allocatora i innych elementów pozwalających zrozumieć istote błędu. Publikacja ta dostępna jest tu: http://www.infsec.net/Kmalloc_Internals.html
Szczerze mówiąc, poczułem zadowolenie czytając powyższy artykuł, ponieważ popierał on również moją opinie na ten temat. Chociaż chyba powinienem się smucić, przecież właśnie pozbawiono „nas” ciekego wektora ataku 😉 Powróćmy jednak do tematu.
Teoretycznie, gdybyśmy mogli spowodować aby kernel tak „ułożył” alokowane regiony pamięci, aby dało się określić jakie dane zmodyfikujemy powodując przepełnienie bufora, eksploitacja byłaby jak najbardziej możliwa. Do niedawno nikomu nie udało się tego osiągnąć.
Wszelkie wątpiwości rozwiała jednak znana ze sceny osoba o ksywie „qobaiashi”, która zawarła w swoim artykule „The story of exploiting kmalloc() overflows” dokładny opis błędu, wraz z przykładowym podatnym modułem jądra oraz jego eksploitacją krok po kroku. Nie będę wgłębiać się w opis wykorzystanej techniki – qobaiashi znalazł poprostu sposób na nadpisanie takich danych, których modyfikacja może umożliwić uruchomienie dowolnego kodu. Zapraszam do lektury!
Archiwalny news dodany przez użytkownika: skrzywiony.
Kliknij tutaj by zobaczyć archiwalne komentarze.