DDexec / EverythingExec
Kontekst
W systemie Linux, aby uruchomić program, musi on istnieć jako plik, musi być dostępny w jakiś sposób poprzez hierarchię systemu plików (tak działa execve()
). Ten plik może znajdować się na dysku lub w pamięci RAM (tmpfs, memfd), ale potrzebujesz ścieżki do niego. To sprawia, że bardzo łatwo kontrolować, co jest uruchamiane w systemie Linux, ułatwia wykrywanie zagrożeń i narzędzi atakujących lub zapobieganie próbom uruchomienia czegokolwiek przez nich (np. nie zezwalając nieuprzywilejowanym użytkownikom na umieszczanie plików wykonywalnych w dowolnym miejscu).
Ale ta technika ma na celu zmianę tego wszystkiego. Jeśli nie możesz uruchomić procesu, którego chcesz... to przejmujesz już istniejący.
Ta technika pozwala ci obejść powszechne techniki ochronne, takie jak tylko do odczytu, noexec, białe listy nazw plików, białe listy hashy...
Zależności
Ostateczny skrypt zależy od następujących narzędzi, aby działał, muszą być one dostępne w atakowanym systemie (domyślnie znajdziesz je wszędzie):
Technika
Jeśli jesteś w stanie dowolnie modyfikować pamięć procesu, możesz go przejąć. Można to wykorzystać do przejęcia już istniejącego procesu i zastąpienia go innym programem. Możemy osiągnąć to albo używając wywołania systemowego ptrace()
(co wymaga możliwości wykonywania wywołań systemowych lub dostępności gdb w systemie) albo, co ciekawsze, zapisując do /proc/$pid/mem
.
Plik /proc/$pid/mem
jest jedno do jednego odwzorowaniem całej przestrzeni adresowej procesu (np. od 0x0000000000000000
do 0x7ffffffffffff000
w x86-64). Oznacza to, że odczytanie lub zapisanie do tego pliku na przesunięciu x
jest takie samo jak odczytanie lub modyfikowanie zawartości pod adresem wirtualnym x
.
Teraz mamy cztery podstawowe problemy do rozwiązania:
Ogólnie rzecz biorąc, tylko root i właściciel pliku mogą go modyfikować.
ASLR.
Jeśli spróbujemy odczytać lub zapisać do adresu spoza zmapowanej przestrzeni adresowej programu, otrzymamy błąd wejścia/wyjścia.
Te problemy mają rozwiązania, które, mimo że nie są idealne, są dobre:
Większość interpreterów powłoki pozwala na utworzenie deskryptorów plików, które zostaną dziedziczone przez procesy potomne. Możemy utworzyć deskryptor wskazujący na plik
mem
powłoki z uprawnieniami do zapisu... więc procesy potomne korzystające z tego deskryptora będą mogły modyfikować pamięć powłoki.ASLR nie stanowi nawet problemu, możemy sprawdzić plik
maps
powłoki lub inny z procfs, aby uzyskać informacje o przestrzeni adresowej procesu.Musimy więc wykonać
lseek()
na pliku. Z poziomu powłoki nie można tego zrobić chyba że używając niesławnej komendydd
.
Szczegółowe omówienie
Kroki są stosunkowo proste i nie wymagają żadnego rodzaju specjalistycznej wiedzy, aby je zrozumieć:
Analizujemy binarnik, który chcemy uruchomić oraz loader, aby dowiedzieć się, jakie odwzorowania potrzebują. Następnie tworzymy "shell"code, który będzie wykonywał, ogólnie mówiąc, te same kroki, które jądro wykonuje przy każdym wywołaniu
execve()
:Tworzymy wspomniane odwzorowania.
Wczytujemy do nich binaria.
Ustawiamy uprawnienia.
Na koniec inicjalizujemy stos argumentami dla programu i umieszczamy wektor pomocniczy (potrzebny przez loader).
Skaczemy do loadera i pozwalamy mu zrobić resztę (załaduje biblioteki potrzebne przez program).
Pobieramy z pliku
syscall
adres, do którego proces powróci po wywołaniu systemowym, który wykonuje.Nadpisujemy to miejsce, które będzie wykonywalne, naszym shellcodem (poprzez
mem
możemy modyfikować strony, które nie są zapisywalne).Przekazujemy program, który chcemy uruchomić do stdin procesu (będzie odczytany przez wspomniany "shell"code).
W tym momencie loader ma za zadanie załadować niezbędne biblioteki dla naszego programu i przejść do niego.
Sprawdź narzędzie na https://github.com/arget13/DDexec
EverythingExec
Istnieje kilka alternatyw do dd
, jedną z nich jest tail
, obecnie domyślny program używany do lseek()
przez plik mem
(który był jedynym celem użycia dd
). Wspomniane alternatywy to:
Ustawiając zmienną SEEKER
możesz zmienić użytego poszukiwacza, np.:
Jeśli znajdziesz innego ważnego poszukiwacza, który nie został zaimplementowany w skrypcie, nadal możesz go użyć, ustawiając zmienną SEEKER_ARGS
:
Zablokuj to, EDR-y.
Odnośniki
Last updated