Wykrywanie pozorowania procesów jądra za pomocą linii poleceń
Napisał: Patryk Krawaczyński
06/06/2020 w Bezpieczeństwo Brak komentarzy. (artykuł nr 737, ilość słów: 2947)
W
nawiązaniu do “Ściągawki z informatyki śledczej w wykrywaniu włamań za pomocą linii poleceń Linuksa” dzisiaj zajmiemy się maskowaniem procesów, które czasem jest wykorzystywane przez złośliwe oprogramowanie. Z pomocą ponownie przyjdzie nam magia linii poleceń Linuksa, która umożliwi nam zdemaskowanie prawdziwego intruza w systemie. Na początku odpowiedzmy sobie na pytanie: czym jest maskarada procesów jądra Linux? Otóż w systemie Linux jądro posiada wiele własnych wątków utworzonych w celu ułatwienia wykonywania zadań systemowych. Wątki te mogą służyć do planowania obsługi zadań (ang. scheduling), operacji I/O na urządzeniach blokowych, wykonać transakcje księgowania dla systemów plików, okresowej synchronizacji zmodyfikowanych stron pamięci itd.
Jeśli użyjemy standardowego polecenia służącego do wyświetlania procesów, takiego jak: ps auxw
, wątki te pojawią się w formie opakowanej w [nawiasy kwadratowe]. Zwykłe procesy systemowe zwykle nie pojawiają się w tej formie. Nawiasy kwadratowe oznaczają, że proces nie ma argumentów wiersza poleceń, co zwykle oznacza, że został uruchomiony jako wątek. Na przykład poniższa lista pokazuje wątki jądra w porównaniu do normalnych procesów:
root@darkstar:~# ps xuaw USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND # WĄTKI JĄDRA root 145 0.0 0.0 0 0 ? S May28 0:00 [watchdogd] root 146 0.0 0.0 0 0 ? S May28 0:00 [kswapd0] root 147 0.0 0.0 0 0 ? I< May28 0:00 [kworker/u9:0] root 148 0.0 0.0 0 0 ? S May28 0:00 [ecryptfs-kthrea] # NORMALNE PROCESY root 777 0.0 0.1 31920 3236 ? Ss May28 0:01 /usr/sbin/cron -f root 383 0.0 0.0 97704 1804 ? Ss May28 0:00 /sbin/lvmetad -f root 1049 0.0 0.3 72296 6360 ? Ss May28 0:00 /usr/sbin/sshd -D
Złośliwe oprogramowanie w systemie Linux wykorzystuje różne techniki, aby ukryć się przed wykryciem. Jedną z metod, których używa to próba podszywania się pod wątek jądra – poprzez wyświetlanie procesu w otoczeniu [nawiasów] wokół jego nazwy na liście procesów. W ten sposób administratorzy mogą łatwo przeoczyć złośliwy proces. Jeśli spojrzysz teraz na poniższy wykaz procesów to czy jesteś w stanie jednoznacznie wskazać, który ukrywa się jako wątek jądra?
root 2 0.0 0.0 0 0 ? S May28 0:00 [kthreadd] root 6 0.0 0.0 0 0 ? I< May28 0:00 [kworker/0:0H-kb] root 9 0.0 0.0 0 0 ? S May28 0:07 [ksoftirqd/0] root 18 0.0 0.0 0 0 ? S May28 0:07 [ksoftirqd/1] root 20 0.0 0.0 0 0 ? I< May28 0:00 [kworker/1:0H-kb] root 24 0.0 0.0 0 0 ? S May28 0:05 [ksoftirqd/2] root 26 0.0 0.0 0 0 ? I< May28 0:00 [kworker/2:0H-kb] root 30 0.0 0.0 0 0 ? S May28 0:07 [ksoftirqd/3] root 32 0.0 0.0 0 0 ? I< May28 0:00 [kworker/3:0H-kb] root 33 0.0 0.0 0 0 ? S May28 0:00 [kdevtmpfs] root 36 0.0 0.0 0 0 ? S May28 0:00 [kauditd] root 37 0.0 0.0 0 0 ? S May28 0:01 [khungtaskd] root 40 0.0 0.0 0 0 ? S May28 0:00 [kcompactd0] root 41 0.0 0.0 0 0 ? SN May28 0:00 [ksmd] root 42 0.0 0.0 0 0 ? SN May28 0:00 [khugepaged] root 136 0.0 0.0 0 0 ? I< May28 0:00 [kintegrityd] root 137 0.0 0.0 0 0 ? I< May28 0:00 [kblockd] root 146 0.0 0.0 0 0 ? S May28 0:00 [kswapd0] root 147 0.0 0.0 0 0 ? I< May28 0:00 [kworker/u9:0] root 151 0.0 0.0 0 0 ? I< May28 0:00 [kthrotld] root 165 0.0 0.0 0 0 ? I< May28 0:00 [kstrp] root 23438 0.0 0.0 8068 812 ? S 20:45 0:00 [kworkerd] root 254 0.0 0.0 0 0 ? I< May28 0:00 [kworker/3:1H-kb] root 256 0.0 0.0 0 0 ? I< May28 0:00 [kworker/0:1H-kb] root 304 0.0 0.0 0 0 ? I< May28 0:00 [kworker/1:1H-kb] root 314 0.0 0.0 0 0 ? I< May28 0:00 [kworker/2:1H-kb] root 10835 0.0 0.0 0 0 ? I 11:12 0:09 [kworker/0:2-eve] root 12935 0.0 0.0 0 0 ? I 13:57 0:06 [kworker/3:1-eve] root 13205 0.0 0.0 0 0 ? I 14:02 0:10 [kworker/1:2-eve] root 13385 0.0 0.0 0 0 ? I 14:31 0:00 [kworker/1:1-eve] root 13490 0.0 0.0 0 0 ? I 14:40 0:00 [kworker/0:1-eve] root 13952 0.0 0.0 0 0 ? I 15:30 0:05 [kworker/2:1-eve] root 14536 0.0 0.0 0 0 ? I 18:01 0:00 [kworker/u8:1-ev] root 14708 0.0 0.0 0 0 ? I 18:38 0:00 [kworker/3:2-eve] root 23219 0.0 0.0 0 0 ? I 20:12 0:00 [kworker/u8:2-ev] root 23298 0.0 0.0 0 0 ? I 20:27 0:00 [kworker/u8:0-ev] root 23364 0.0 0.0 0 0 ? I 20:44 0:00 [kworker/u8:3-ev]
Teraz, gdy wiesz już, jak wygląda jedna z metod maskowania wątków jądra Linux przygotujmy test, abyś mógł się dowiedzieć, jak go znaleźć za pomocą śledzctwa z poziomu linii poleceń. W naszej symulacji użyjemy polecenia sleep
, ponieważ nie spowoduje ono żadnych problemów w systemie:
export PATH=.:$PATH cp /bin/sleep /tmp/[kworkerd] cd /tmp "[kworkerd]" 3600 &
Osobliwe eksportowanie ścieżki dostępu (export PATH=.:$PATH
) powoduje, że możemy wykonać plik w katalogu lokalnym bez konieczności umieszczenia przed nim frazy: "./"
. To sprawia, że tak uruchomiony proces wygląda bardziej “naturalnie”. Kolejnym krokiem jest skopiowanie polecenia sleep
do katalogu /tmp i uruchomienie go pod fałszywą nazwą [kworked]. Jako argument wpisujemy 3600 sekund, aby proces zakończył się po godzinie działania testów. Spójrzmy teraz jak wygląda proces [kworkerd], gdy wydamy polecenie ps
:
root@darkstar:~# ps auxw | grep '\[k' # PRAWDZIWY WĄTEK JĄDRA root 23416 0.0 0.0 0 0 ? I 20:52 0:00 [kworker/u8:1-ev] # FAŁSZYWY PROCES root 23438 0.0 0.0 8068 740 pts/0 S 20:54 0:00 [kworkerd] 3600
Przeprowadźmy teraz wykrywanie zamaskowanych procesów jądra za pomocą map procesów. Metoda ta polega na sprawdzeniu, czy dany proces zawiera wpisy w /proc/$PID/map
. W tym miejscu utrzymywana jest lista aktualnie zmapowanych regionów pamięci i uprawnienia dostępu do nich. Dla prawdziwych wątków jądra mapy powinny być puste. Jeśli spojrzymy na tę lokalizację w poszukiwaniu procesu o nazwie w [nawiasach] kwadratowych, ale pokaże on jakiekolwiek wpisy – oznacza to, że nie jest to prawdziwy wątek jądra. Prostym poleceniem, którego należy użyć to: cat /proc/23438/maps
, gdzie liczba 23438 jest identyfikatorem procesu [kworkerd], który wygląda podejrzanie:
root@darkstar:~# cat /proc/23438/maps 557186324000-55718632b000 r-xp 00000000 08:02 131850 /tmp/[kworkerd] 55718652b000-55718652c000 r--p 00007000 08:02 131850 /tmp/[kworkerd] 55718652c000-55718652d000 rw-p 00008000 08:02 131850 /tmp/[kworkerd] 5571882f8000-557188319000 rw-p 00000000 00:00 0 [heap] 7fc55c737000-7fc55caac000 r--p 00000000 08:05 393301 /usr/lib/locale/locale-archive 7fc55caac000-7fc55cc93000 r-xp 00000000 08:02 32 /lib/x86_64-linux/libc-2.27.so 7fc55cc93000-7fc55ce93000 ---p 001e7000 08:02 32 /lib/x86_64-linux/libc-2.27.so 7fc55ce93000-7fc55ce97000 r--p 001e7000 08:02 32 /lib/x86_64-linux/libc-2.27.so 7fc55ce97000-7fc55ce99000 rw-p 001eb000 08:02 32 /lib/x86_64-linux/libc-2.27.so 7fc55ce99000-7fc55ce9d000 rw-p 00000000 00:00 0 7fc55ce9d000-7fc55cec4000 r-xp 00000000 08:02 23 /lib/x86_64-linux/ld-2.27.so 7fc55d0b7000-7fc55d0b9000 rw-p 00000000 00:00 0 7fc55d0c4000-7fc55d0c5000 r--p 00027000 08:02 23 /lib/x86_64-linux/ld-2.27.so 7fc55d0c5000-7fc55d0c6000 rw-p 00028000 08:02 23 /lib/x86_64-linux/ld-2.27.so 7fc55d0c6000-7fc55d0c7000 rw-p 00000000 00:00 0 7fffaa2f0000-7fffaa311000 rw-p 00000000 00:00 0 [stack] 7fffaa38c000-7fffaa38f000 r--p 00000000 00:00 0 [vvar] 7fffaa38f000-7fffaa390000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]
Jeśli chcemy szybko przejrzeć wszystkie systemowe identyfikatory $PID
i zobaczyć, które z nich są nazwane w nawiasach, ale mają pliki map – możemy zastosować polecenie:
root@darkstar:~# ps auxw | awk '{print $11,$2}' | grep ^\\[ | awk '{print $2}' \ | xargs -I % sh -c 'echo PID: %; cat /proc/%/maps' 2> /dev/null PID: 10835 PID: 12935 PID: 13205 PID: 13385 PID: 13952 PID: 14708 PID: 23589 PID: 23609 PID: 23630 PID: 23438 557186324000-55718632b000 r-xp 00000000 08:02 131850 /tmp/[kworkerd] 55718652b000-55718652c000 r--p 00007000 08:02 131850 /tmp/[kworkerd] 55718652c000-55718652d000 rw-p 00008000 08:02 131850 /tmp/[kworkerd] 5571882f8000-557188319000 rw-p 00000000 00:00 0 [heap] 7fc55c737000-7fc55caac000 r--p 00000000 08:05 393301 /usr/lib/locale/locale-archive 7fc55caac000-7fc55cc93000 r-xp 00000000 08:02 32 /lib/x86_64-linux/libc-2.27.so 7fc55cc93000-7fc55ce93000 ---p 001e7000 08:02 32 /lib/x86_64-linux/libc-2.27.so 7fc55ce93000-7fc55ce97000 r--p 001e7000 08:02 32 /lib/x86_64-linux/libc-2.27.so 7fc55ce97000-7fc55ce99000 rw-p 001eb000 08:02 32 /lib/x86_64-linux/libc-2.27.so 7fc55ce99000-7fc55ce9d000 rw-p 00000000 00:00 0 7fc55ce9d000-7fc55cec4000 r-xp 00000000 08:02 23 /lib/x86_64-linux/ld-2.27.so 7fc55d0b7000-7fc55d0b9000 rw-p 00000000 00:00 0 7fc55d0c4000-7fc55d0c5000 r--p 00027000 08:02 23 /lib/x86_64-linux/ld-2.27.so 7fc55d0c5000-7fc55d0c6000 rw-p 00028000 08:02 23 /lib/x86_64-linux/ld-2.27.so 7fc55d0c6000-7fc55d0c7000 rw-p 00000000 00:00 0 7fffaa2f0000-7fffaa311000 rw-p 00000000 00:00 0 [stack] 7fffaa38c000-7fffaa38f000 r--p 00000000 00:00 0 [vvar] 7fffaa38f000-7fffaa390000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall] PID: 23873 PID: 29254
Jeśli na liście zobaczymy wpisy w plikach map, gdzie plik binarny zawiera linki do siebie lub bibliotek, których używa – powinniśmy zbadać te przypadki – szczególnie, gdy widzimy podejrzane biblioteki, odniesienia do ukrytych katalogów itp. Na przykład ściezka /tmp/[kworkerd] jest bardzo podejrzanym procesem wartym zbadania. Innym sposobem na zdemaskowanie procesów udających wątki jest sprawdzenie, czy nie posiadają one plików binarnych dołączonych do uruchomionego procesu (/proc/$PID/exe
):
root@darkstar:~# ps auxww | awk '{print $11,$2}' | grep ^\\[ | awk '{print $2}' \ | xargs -I % sh -c 'echo PID: %; sha1sum /proc/%/exe' 2> /dev/null PID: 10835 PID: 12935 PID: 13205 PID: 13385 PID: 13952 PID: 23609 PID: 23630 PID: 23873 PID: 24533 PID: 24624 PID: 24663 PID: 23438 bebcce23072c4d831ce8e2822a0858d6aa813067 /proc/23438/exe PID: 25080
W przypadku, gdy polecenie zwórci nam skrót, to jest to normalny proces, który próbuje się ukryć i nie jest wątkiem jądra. Prawdziwe wątki jądra nie będą miały linku do pliku binarnego, który je uruchomił. Plik taki możemy szybko skopiować do innej lokalizacji i wykonać jego analizę na specjalnym, wyizolowanym systemie:
cp /proc/23438/exe /mnt/offline/podejrzany_bin
Wartość jego skrótu możemy użyć do sprawdzenia baz danych znanego złośliwego oprogramowania w celu możliwej identyfikacji.
Więcej informacji: Detecting Linux kernel process masquerading with command line forensics