NFsec Logo

Ukrywamy nazwę procesu i jego parametry za pomocą programu zapper

Dzisiaj w Bezpieczeństwo, Pen Test Brak komentarzy.  (artykuł nr 940, ilość słów: 2302)

Z

apper to mały program, który za pomocą ptrace() manipuluje stosem wektora pomocniczego (ang. auxiliary vector) formatu ELF (ang. Executable and Linkable Format), który posiada format tablicy w postaci par: „klucz – wartość” i jest przekazywany z jądra systemu do procesu w przestrzeni użytkownika, gdy program jest uruchamiany za pomocą funkcji systemowej execve(). Celem wspomnianego wektora (nazywanego również tablicą pomocniczą) jest dostarczenie programowi dodatkowych informacji konfiguracyjnych i środowiskowych, które są niezbędne lub przydatne dla programu w trakcie jego działania (np. jak argumenty linii poleceń czy zmienne środowiskowe – argc, argv, envp).

Zapper przechwytuje moment, gdy jądro systemu (ang. kernel) przekazuje opcje uruchamianego programu (za pomocą wspomnianego execve()): przenosi oryginalne opcje programu do nowej lokalizacji w pamięci, a następnie niszczy starą. Z perspektywy jądra (i pakietu narzędzi systemowych korzystających z procps) opcje programu przestają istnieją istnieć. Następnie zapper „naprawia” wskaźniki w tablicy pomocniczej programu i oddaje wykonanie z powrotem uruchamianemu programowi (za pomocą PTRACE_CONT). Wykonywany program przez zapper jest później śledzony tylko pod kątem wszelkich, dalszych wywołań typu fork() lub execve() (aby powtórzyć sztuczkę od nowa dla wątków i procesów potomnych). Według autora ma to prawie zerowy (tylko 00.1% narzutu) wpływ na wydajność działania programu dzięki zastosowaniu kilku przydatnych funkcji ptrace: śledzimy tylko dwa rodzaje wywołań systemowych (fork() i execve()).

Inne zalety programu zapper to: brak wymagań uprawnień administracyjnych; działanie na statycznych plikach binarnych (np. rust i golang); możliwość uruchomienia procesu pod dowolnym (niezajętym) identyfikatorem. Przykład działania:

agresor@darkstar:~$ wget -O zapper https://github.com/.../zapper-linux-$(uname -m)
agresor@darkstar:~$ chmod +x zapper
agresor@darkstar:~$ ps x
    PID TTY      STAT   TIME COMMAND
  31384 pts/0    R+     0:00 ps x
3620444 ?        Ss     0:00 /usr/lib/systemd/systemd --user
3620445 ?        S      0:00 (sd-pam)
3620475 ?        S      0:02 sshd: agresor@pts/0
3620476 pts/0    Ss     0:00 -bash

Uruchomimy teraz proces sleep 3600, jako fałszywy wątek jądra [kworker/u10:0] nadając mu PID: 31337:

 
agresor@darkstar:~$ (./zapper -n31337 -a '[kworker/u10:0]' sleep 3600 &>/dev/null &)
agresor@darkstar:~$ ps x
    PID TTY      STAT   TIME COMMAND
  31386 pts/0    S      0:00 [kworker/u10:0] -n31337 -a [kworker/u10:0] sleep 3600
  31387 pts/0    D      0:01 [kworker/u10:0] -n31337 -a [kworker/u10:0] sleep 3600
  31388 pts/0    R      0:01 [kworker/u10:0] -n31337 -a [kworker/u10:0] sleep 3600
  31390 pts/0    R      0:01 [kworker/u10:0] -n31337 -a [kworker/u10:0] sleep 3600
  31393 pts/0    D      0:01 [kworker/u10:0] -n31337 -a [kworker/u10:0] sleep 3600
  31395 pts/0    R      0:01 [kworker/u10:0] -n31337 -a [kworker/u10:0] sleep 3600
  31398 pts/0    D      0:01 [kworker/u10:0] -n31337 -a [kworker/u10:0] sleep 3600
  31400 pts/0    R      0:01 [kworker/u10:0] -n31337 -a [kworker/u10:0] sleep 3600
  31401 pts/0    D      0:01 [kworker/u10:0] -n31337 -a [kworker/u10:0] sleep 3600
 545989 pts/0    R+     0:00 ps x
3620444 ?        Ss     0:00 /usr/lib/systemd/systemd --user
3620445 ?        S      0:00 (sd-pam)
3620475 ?        S      0:02 sshd: agresor@pts/0
3620476 pts/0    Ss     0:00 -bash

Na pierwszy rzut oka wygląda, jakby coś nam nie do końca zadziałało z ukryciem procesu. Dzieje się tak, ponieważ system Linux przydziela nowy PID (identyfikator procesu) każdemu nowemu wątkowi w kolejności sekwencyjnej, aż do największego możliwego numeru wynoszącego: 4194304 (2^22) – kernel.pid_max. Następnie zaczyna ponownie od numeru 300 (lub 1, w zależności od środowiska). Dlatego zapper musi iterować przez wszystkie (2^22) możliwe wartości numeryczne (w ciągu kilku sekund), aż napotka docelowy PID-1. W tym celu rozwidla / forkuje się na 8+ procesów i je zabija, aby przekręcić licznik systemu. Po chwili wzmożonej pracy CPU otrzymujemy finalny wynik:

agresor@darkstar:~$ ps x
    PID TTY      STAT   TIME COMMAND
  31337 pts/0    S      0:00 [kworker/u10:0]
  31355 pts/0    R+     0:00 ps x
3620444 ?        Ss     0:00 /usr/lib/systemd/systemd --user
3620445 ?        S      0:00 (sd-pam)
3620475 ?        S      0:02 sshd: agresor@pts/0
3620476 pts/0    Ss     0:00 -bash

Możemy też uruchomić proces, którego wątki również zostaną zamaskowane (-f):

agresor@darkstar:~$ ./zapper -f -a '[kworker/u11:1]' screen -S agresor
agresor@darkstar:~$ ps x
    PID TTY      STAT   TIME COMMAND
  31337 pts/0    S      0:00 [kworker/u10:0]
  31399 pts/0    S+     0:00 [kworker/u11:1]
  31400 pts/0    S+     0:00 [kworker/u11:1]
  31401 ?        Ss     0:00 [kworker/u11:1]
  31402 pts/1    Ss     0:00 [kworker/u11:1]
  31409 pts/1    R+     0:00 [kworker/u11:1]
3620444 ?        Ss     0:00 /usr/lib/systemd/systemd --user
3620445 ?        S      0:00 (sd-pam)
3620475 ?        S      0:04 sshd: agresor@pts/0
3620476 pts/0    Ss     0:00 -bash

Oczywiście wyłączenie ptrace w systemie uniemożliwia poprawne działanie programu:

root@darkstar:~# sysctl -w kernel.yama.ptrace_scope=3
agresor@darkstar:~$ ps x
    PID TTY      STAT   TIME COMMAND
  31455 pts/0    S+     0:00 [kworker/u11:1] -f -a [kworker/u11:1] screen -S agresor
  31456 pts/0    S+     0:00 screen          -S agresor
  31457 ?        Ss     0:00 SCREEN          -S agresor
  31458 pts/1    Ss     0:00 /bin/bash
  31470 pts/1    R+     0:00 ps x
3620444 ?        Ss     0:00 /usr/lib/systemd/systemd --user
3620445 ?        S      0:00 (sd-pam)
3620475 ?        S      0:05 sshd: agresor@pts/0
3620476 pts/0    Ss     0:00 -bash

Program również nie jest odporny na szukania niespójności w systemie plików procfs. Dodatkowo warto wiedzieć, że procfs także udostępnia informacje o wektorze pomocniczym (tak jak glibc za pomocą zmiennej LD_SHOW_AUXV=1 przy uruchamianiu programu) w ścieżce: /proc/$PID/auxv. Jeśli teraz za pomocą programu zapper został ukryty proces o ID: 1659 w systemie:

agresor@darkstar:~$ ps x
    PID TTY      STAT   TIME COMMAND
   1613 ?        Ss     0:00 /usr/lib/systemd/systemd --user
   1614 ?        S      0:00 (sd-pam)
   1643 ?        S      0:00 sshd: agresor@pts/0
   1644 pts/0    Ss     0:00 -bash
   1659 pts/0    S      0:00 redis --port 3129
   1662 pts/0    R+     0:00 ps x

Możemy sprawdzić i odczytać zawartość jego wektora pomocniczego, w celu uzyskania prawdziwej nazwy pliku:

agresor@darkstar:~$ od -t d8 /proc/1659/auxv
0000000                   33      140725435932672
0000020                   51                 1776
0000040                   16            395049983
0000060                    6                 4096
0000100                   17                  100
0000120                    3      102138210017344
0000140                    4                   56
0000160                    5                   13
0000200                    7      134200643850240
0000220                    8                    0
0000240                    9      102138210021568
0000260                   11                 1000
0000300                   12                 1000
0000320                   13                 1000
0000340                   14                 1000
0000360                   23                    0
0000400                   25      140725435594489
0000420                   26                    2
0000440                   31      140725435600878
0000460                   15      140725435594505
0000500                   27                   28
0000520                   28                   32
0000540                    0                    0
0000560

Znaczenie poszczególnych wartości numerycznych dla drugiej kolumny znajdziemy w pliku /usr/include/linux/auxvec.h. Nas interesuje wartość wskaźnika trzeciej kolumny, dla której kluczem jest: 31 (AT_EXECFN – ang. File name of executable), czyli wartość: 140725435600878. Możemy ją odczytać prosto z procesu za pomocą GNU Debuggera (gdb):

agresor@darkstar:~$ gdb -p 1659 --batch -ex "x/s 140725435600878" 2>/dev/null
0x7ffd31998fee:	"./malware"
agresor@darkstar:~$ gdb -p 1659 --batch -ex "info auxv" 2>/dev/null | grep AT_EXECFN
31   AT_EXECFN            File name of executable        0x7ffd31998fee "./malware"

Jak widzimy to nie proces redis, tylko malware kryje się pod tym ID.

Więcej informacji: zapper, How does Linux start a process, About ELF Auxiliary Vectors, getauxval() and the auxiliary vector

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

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

Brak nowszych postów

Komentowanie tego wpisu jest zablokowane.