Czy dd if = / dev / urandom of = / dev / mem jest bezpieczny?

10

Co to dokładnie robi? Nie rozumiem, jak można uzyskać dostęp do podstawowej pamięci za pomocą tego ... wydaje się trochę dziwne. Czy to jest bezpieczne?

dd if=/dev/urandom of=/dev/mem
Koder 14
źródło
O czym jest to „bezpieczne”, o którym mówisz? Bezpieczny w odniesieniu do czego?
waltinator,
Co chcesz osiągnąć dzięki temu poleceniu?
jochen

Odpowiedzi:

23

Nie próbuj tego w domu! Może to spowodować awarię systemu, a jeśli masz pecha, może uszkodzić urządzenie peryferyjne lub uniemożliwić uruchomienie komputera.

W rzeczywistości na większości platform po prostu kończy się niepowodzeniem z błędem, ale zależy to od architektury sprzętowej. Zdecydowanie nie ma gwarancji, że jest to nieszkodliwe, chyba że uruchomisz polecenie jako użytkownik nieuprzywilejowany. W przypadku nieuprzywilejowanego użytkownika polecenie jest całkowicie nieszkodliwe, ponieważ nie można go otworzyć /dev/mem.

Kiedy uruchamiasz polecenie jako root, powinieneś wiedzieć, co robisz. Jądro czasami uniemożliwia ci zrobienie czegoś niebezpiecznego, ale nie zawsze. /dev/memjest jedną z tych potencjalnie niebezpiecznych rzeczy, w których naprawdę powinieneś wiedzieć, co robisz.

Omówię sposób zapisu do /dev/memLinuxa. Ogólna zasada byłaby taka sama w przypadku innych Uniksów, ale rzeczy takie jak opcje jądra są zupełnie inne.

Co dzieje się, gdy proces odczytuje lub zapisuje plik urządzenia, zależy od jądra. Dostęp do pliku urządzenia uruchamia pewien kod w sterowniku, który obsługuje ten plik urządzenia. Na przykład pisanie w celu /dev/memwywołania funkcji write_memwdrivers/char/mem.c . Ta funkcja przyjmuje 4 argumenty: strukturę danych reprezentującą otwarty plik, wskaźnik do danych do zapisania, liczbę bajtów do zapisania i bieżącą pozycję w pliku.

Zauważ, że dostaniesz się tak daleko tylko wtedy, gdy dzwoniący miał pozwolenie na otwarcie pliku. Pliki urządzeń normalnie wypełniają uprawnienia do plików. Normalne uprawnienia /dev/memcrw-r-----własnością root:kmem, więc jeśli spróbujesz otworzyć go do pisania bez rootowania, dostaniesz po prostu „odmowę dostępu” (EACCESS). Ale jeśli jesteś rootem (lub jeśli root zmienił uprawnienia do tego pliku), otwarcie przechodzi, a następnie możesz spróbować zapisać.

Kod w write_memfunkcji wykonuje pewne kontrole poczytalności, ale te kontrole nie wystarczą, aby uchronić się przed wszystkim złym. Pierwszą rzeczą, jaką robi, jest konwersja bieżącej pozycji pliku *pposna adres fizyczny. Jeśli to się nie powiedzie (w praktyce, ponieważ jesteś na platformie z 32-bitowymi adresami fizycznymi, ale 64-bitowymi przesunięciami plików, a przesunięcie pliku jest większe niż 2 ^ 32), zapis nie powiedzie się z EFBIG (plik zbyt duży). Następnym sprawdzeniem jest, czy zakres fizycznych adresów do zapisu jest poprawny w tej konkretnej architekturze procesora, a błąd powoduje EFAULT (zły adres).

Następnie, w Sparc i m68k, każda część zapisu na pierwszej stronie fizycznej jest po cichu pomijana.

Dotarliśmy do głównej pętli, która iteruje dane w blokach, które mogą zmieścić się na jednej stronie MMU . /dev/memuzyskuje dostęp do pamięci fizycznej, nie pamięci wirtualnej, ale instrukcje procesora do ładowania i przechowywania danych w pamięci używają adresów wirtualnych, więc kod musi ustawić mapowanie pamięci fizycznej pod pewnym adresem wirtualnym. W Linuksie, w zależności od architektury procesora i konfiguracji jądra, to mapowanie albo istnieje na stałe, albo musi być wykonane w locie; to jest zadanie xlate_dev_mem_ptr(i unxlate_dev_mem_ptrcofa cokolwiek xlate_dev_mem_ptrrobi). Następnie funkcja copy_from_userodczytuje z bufora, który został przekazany dowritewywołanie systemowe i po prostu zapisuje na adres wirtualny, na którym obecnie mapowana jest pamięć fizyczna. Kod emituje normalne instrukcje przechowywania w pamięci, a to oznacza sprzęt.

Zanim omówię, że zapis na adres fizyczny tak jest, omówię czek, który nastąpi przed tym zapisem. Wewnątrz pętli page_is_allowedbloki funkcyjne uzyskują dostęp do określonych adresów, jeśli CONFIG_STRICT_DEVMEMwłączona jest opcja konfiguracji jądra (tak jest domyślnie): devmem_is_allowedmożna uzyskać dostęp tylko do adresów dozwolonych przez /dev/mem, dla innych zapis nie powiedzie się przy użyciu EPERM (operacja niedozwolona). Opis tej opcji stanowi:

Jeśli ta opcja jest włączona, a IO_STRICT_DEVMEM = n, plik / dev / mem pozwala tylko na dostęp użytkownika do przestrzeni PCI oraz kodów BIOS i regionów danych. Jest to wystarczające dla dosemu i X oraz wszystkich zwykłych użytkowników / dev / mem.

To jest bardzo skoncentrowany na x86 opis. W rzeczywistości bardziej ogólnie CONFIG_STRICT_DEVMEMblokuje dostęp do adresów pamięci fizycznej mapowanych na pamięć RAM, ale umożliwia dostęp do adresów, które nie są mapowane na pamięć RAM. Szczegóły dozwolonych zakresów adresów fizycznych zależą od architektury procesora, ale wszystkie z nich wykluczają pamięć RAM, w której przechowywane są dane jądra i procesów lądowych użytkownika. Dodatkowa opcja CONFIG_IO_STRICT_DEVMEM(wyłączona od Ubuntu 18.04) blokuje dostęp do adresów fizycznych, które są żądane przez sterownik.

Adresy pamięci fizycznej mapowane na pamięć RAM . Czy istnieją adresy pamięci fizycznej, które nie są mapowane na pamięć RAM? Tak. Tę dyskusję obiecałem powyżej na temat tego, co to znaczy napisać na adres.

Instrukcja magazynu pamięci niekoniecznie zapisuje w pamięci RAM. Procesor rozkłada adres i decyduje, do którego urządzenia peryferyjnego wysłać sklep. (Kiedy mówię „procesor”, obejmuję kontrolery urządzeń peryferyjnych, które mogą nie pochodzić od tego samego producenta.) RAM jest tylko jednym z tych urządzeń peryferyjnych. Sposób wysyłki jest bardzo zależny od architektury procesora, ale podstawy są mniej więcej takie same na wszystkich architekturach. Procesor zasadniczo rozkłada większe bity adresu i wyszukuje je w niektórych tabelach, które są zapełniane na podstawie zakodowanych informacji, informacji uzyskanych przez sondowanie niektórych magistral oraz informacji skonfigurowanych przez oprogramowanie. Może być zaangażowanych wiele buforowania i buforowania, ale w skrócie, po tym rozkładzie,autobus, a potem to urządzenie peryferyjne musi sobie z tym poradzić. (Lub wynik wyszukiwania tabeli może być taki, że pod tym adresem nie ma urządzeń peryferyjnych, w którym to przypadku procesor wchodzi w pułapkę, w której wykonuje pewien kod w jądrze, który zwykle skutkuje SIGBUS dla procesu wywołującego.)

Magazyn na adres, który jest mapowany na pamięć RAM, nie „robi” niczego poza zastąpieniem wartości, która była wcześniej przechowywana pod tym adresem, z obietnicą, że późniejsze załadowanie pod ten sam adres zwróci ostatnią przechowywaną wartość. Ale nawet pamięć RAM ma kilka adresów, które nie zachowują się w ten sposób: ma kilka rejestrów, które mogą kontrolować takie rzeczy, jak częstotliwość odświeżania i napięcie.

Ogólnie rzecz biorąc, odczyt lub zapis do rejestru sprzętowego robi wszystko, co sprzęt jest zaprogramowany. Większość dostępów do sprzętu działa w ten sposób: oprogramowanie (zwykle kod jądra) uzyskuje dostęp do określonego adresu fizycznego, dociera do magistrali łączącej procesor z urządzeniem peryferyjnym, a urządzenie peryferyjne robi swoje. Niektóre procesory (w szczególności x86) mają również osobne instrukcje CPU, które powodują odczyt / zapis na urządzeniach peryferyjnych, które różnią się od obciążenia pamięci i przechowywania, ale nawet na x86 wiele urządzeń peryferyjnych jest osiąganych poprzez ładowanie / przechowywanie.

Polecenie dd if=/dev/urandom of=/dev/memzapisuje losowe dane na dowolnym urządzeniu peryferyjnym odwzorowanym pod adresem 0 (i na kolejne adresy, o ile zapisy się powiodą). W praktyce spodziewam się, że w wielu architekturach adres fizyczny 0 nie ma przypisanego do niego urządzenia peryferyjnego lub ma pamięć RAM, dlatego pierwsza próba zapisu kończy się niepowodzeniem. Ale jeśli urządzenie peryferyjne jest zmapowane pod adresem 0 lub jeśli zmienisz polecenie zapisu na inny adres, uruchomisz coś nieprzewidywalnego w urządzeniu peryferyjnym. Przy losowych danych pod rosnącymi adresami jest mało prawdopodobne, aby zrobić coś interesującego, ale w zasadzie mógłby wyłączyć komputer (prawdopodobnie jest to adres, który faktycznie to robi), zastąpić niektóre ustawienia BIOS, które uniemożliwiają uruchomienie, a nawet uderzenie wadliwy peryferia w sposób, który je uszkadza

alias Russian_roulette='dd if=/dev/urandom of=/dev/mem seek=$((4096*RANDOM+4096*32768*RANDOM))'
Gilles „SO- przestań być zły”
źródło
Dziękuję Ci! Właśnie tego szukałem! Byłem po prostu zdezorientowany, czy / dev / mem zezwala na dostęp do pamięci na rzecz urządzeń peryferyjnych i sprzętowych!
Coder14
Urządzenia peryferyjnego nie można zmapować pod adresem fizycznym 0 na komputerze x86; taka konfiguracja nigdy się nie uruchomi.
Joshua
To nie jest prawda
Yvain
1
Oczywiście możesz uszkodzić jądro, ale nie bios
Yvain
1
@Yvain Co nie jest prawdą? W rzeczywistości nie można uszkodzić jądra, jeśli CONFIG_STRICT_DEVMEMjest włączone.
Gilles „SO- przestań być zły”
12

Jest bezpieczny, jeśli poprawnie skonfigurowałeś jądro (bezpieczne, ponieważ nie będzie działać)

Pamięć strony podręcznika (4) :

/ dev / mem to plik urządzenia znakowego, który jest obrazem głównej pamięci komputera. Można go na przykład użyć do zbadania (a nawet załatania) systemu.

Teoretycznie dd if=/dev/urandom of=/dev/mempowinien więc zastąpić całą przestrzeń adresową zainstalowanej pamięci fizycznej, a ponieważ jądro i inne programy działają z pamięci, powinno to skutecznie zawiesić system. W praktyce jest limit. Z tej samej strony podręcznika:

Od Linuksa 2.6.26 i zależnie od architektury opcja konfiguracji jądra CONFIG_STRICT_DEVMEM ogranicza obszary, do których można uzyskać dostęp poprzez ten plik.

Wypróbowanie tego na maszynie wirtualnej Ubuntu 18.04 zwraca błąd, dd: writing to '/dev/mem': Operation not permittednawet z sudouprawnieniami roota i pomimo nich crw-r-----. Z Ubuntu Wiki :

/ dev / mem protection

Niektóre aplikacje (Xorg) potrzebują bezpośredniego dostępu do pamięci fizycznej z przestrzeni użytkownika. Istnieje specjalny plik / dev / mem, aby zapewnić ten dostęp. W przeszłości możliwe było przeglądanie i zmiana pamięci jądra z tego pliku, jeśli osoba atakująca miała dostęp do konta root. Wprowadzono opcję jądra CONFIG_STRICT_DEVMEM, aby zablokować dostęp do pamięci innej niż urządzenie (pierwotnie nazwany CONFIG_NONPROMISC_DEVMEM).

Technicznie rzecz biorąc, nie, nie jest to bezpieczne (ponieważ spowodowałoby to awarię systemu), a jeśli opcja jądra CONFIG_STRICT_DEVMEMjest wyłączona, jest to dziura w zabezpieczeniach, ale z tego, co widzę do tej pory, polecenie nie uruchomiłoby się, gdyby ta opcja była włączona. Zgodnie z duplikatem między witrynami , ponowne uruchomienie naprawi wszelkie problemy z tym związane, ale oczywiście dane w pamięci RAM w tym czasie zostałyby utracone i nie zostałyby zrzucone na dysk (jeśli taki miałby być).

Sugerowana metoda na duplikacie została wcześniej użyta, busybox devmemwięc jeśli zdecydujesz się na bałagan z pamięcią RAM, może być jakiś sposób.

Sergiy Kolodyazhnyy
źródło
6
„Jest bezpieczny” Nie, z pewnością nie jest. Nawet przy pomocy CONFIG_STRICT_DEVMEMmożna uzyskać dostęp do obszarów pamięci, w których mapowane jest urządzenie peryferyjne, co jest celem całego procesu /dev/mem. Jeśli napiszesz losowe rzeczy na urządzeniach peryferyjnych, wszystko może się zdarzyć. Otrzymasz „operacja niedozwolona”, jeśli spróbujesz uzyskać dostęp do adresu, który nie jest mapowany, a polecenie rozpocznie się od adresu 0. To, czy adres 0 odwzorowuje coś złego, zależy od architektury sprzętowej. Z tego, co wiem, może nigdy nie być przypisane do niczego na komputerze, ale ogólnie nie jest bezpieczne.
Gilles „SO- przestań być zły”
1
@Gilles Na x86 (nie jestem pewien co do x86-64), pierwsze 1 KiB pamięci RAM (adresy od 0x0 do 0x3ff) zawiera wektory przerwań; adres o wartości czterech bajtów na wektor. Jeśli uda ci się zastąpić te przypadkowymi śmieciami, wszelkiego rodzaju ciekawe rzeczy mogą się wkrótce wydarzyć. Najprawdopodobniej skończysz z podwójną lub potrójną usterką i system się zawiesi, ale nie ma żadnych gwarancji ...
CVn
@aCVn Z pewnością istnieje coś mapowanego ( head -c 1024 </dev/mem | od -tx1), ale nie wiem, czy są one używane, gdy procesor nie jest w trybie rzeczywistym (tryb 8088). Nie sądzę, że można ich używać w trybie 64-bitowym: w końcu wektory przerwań 8088 mają tylko 32 bity na adres. A tak przy okazji, jest to dostępne z CONFIG_STRICT_DEVMEMsetem, więc myślę, że Linux go nie używa.
Gilles „SO- przestań być zły”
@Gilles: Strona 0 na x86 jest zarezerwowana dla v86, bootloadera itp .; to jest tam tabela wektorów przerwań trybu rzeczywistego. W trybie chronionym IVT jest gdzieś indziej (rejestr maszyny mówi gdzie).
Joshua