|=------------------------=[ Bypasser Chkrootkit ]=---------------------------=| |=----------------------------------------------------------------------------=| |=---------------------=[ anonyme ]=---------------------=| --[ 1 Introduction Ce court article se concentrera sur une des facons tres simple de bypasser chkrootkit[1] et plus particulierement sur son outil chkproc. A l'heure actuelle, chkrootkit est un des outils les plus utilises pour voir si le systeme n'a pas ete corrompu. Il ne travail qu'en userland et sans utiliser les devices /dev/kmem ou /dev/mem. On peut se poser des questions sur une telle utilisation et confiance faite a cet outil. Actuellement pour la detection de rootkit sous linux, il n'existe peut de tool, voir un nombre tendant vers 0 (ce qui pousse forcement a un pseudo monopole de la part de chkrootkit qui s'est fait une belle place) --[ 2 Analyse de chkproc Pour simplifier, chkproc recupere la sortie de ps, le contenu de /proc et fait un bruteforce sur /proc en verifiant si tous les pids accedes sont bien contenu dans la sortie de ps et /proc. Attardons nous plus particulierement sur le bruteforce qui sert de reference dans chkproc. [...] strcpy(buf, "/proc/"); retps = retdir = 0; for (i = FIRST_PROCESS; i <= MAX_PROCESSES; i++) { snprintf(&buf[6], 6, "%d", i); [1] if (!chdir(buf)) { if (!dirproc[i] && !psproc[i]) [...] retdir++; [...] Comment ne pas passer dans le test [1] et ainsi rester cache ? Pleins de papers, on demontre qu'il etait possible de cacher des processus soit en hijackant sys_getdents ou les fonctions du VFS, etc. Dans ce cas la, on peut renvoyer une erreur en hijackant par exemple l'appel systeme open ou chdir dans un rootkit ou bien encore en utilisant une nouvelle librairie avec la variable LD_PRELOAD. --[ 3 Detournement Une methode plus efficace est de se servir de la fonction proc_pid_readdir qui affiche les processus dans /proc: /* FILE : /usr/src/linux/fs/proc/base.c */ int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir) { [...] for (;;) { nr_tgids = get_tgid_list(nr, next_tgid, tgid_array); [...] } get_tgid_list permet de recuperer la liste des pids en parcourant la liste chainee. /* FILE : /usr/src/linux/fs/proc/base.c */ static int get_tgid_list(int index, unsigned long version, unsigned int *tgids) { [...] for ( ; p != &init_task; p = next_task(p)) { int tgid = p->pid; [1] if (!pid_alive(p)) continue; if (--index >= 0) continue; tgids[nr_tgids] = tgid; nr_tgids++; if (nr_tgids >= PROC_MAXPIDS) break; } read_unlock(&tasklist_lock); return nr_tgids; } Comme dit plus haut cette fonction parcours entre autre liste chainee des processus. Un test interessant se trouve en [1], celui passe au processus suivant si le resultat de la fonction vaut 0. /* FILE : /usr/src/linux/include/linux/sched.h */ /** * pid_alive - check that a task structure is not stale * @p: Task structure to be checked. * * Test if a process is not yet dead (at most zombie state) * If pid_alive fails, then pointers within the task structure * can be stale and must not be dereferenced. */ static inline int pid_alive(struct task_struct *p) { return p->pids[PIDTYPE_PID].nr != 0; } struct task_struct { [...] /* PID/PID hash table linkage. */ struct pid pids[PIDTYPE_MAX]; [...] }; struct pid { /* Try to keep pid_chain in the same cacheline as nr for find_pid */ int nr; struct hlist_node pid_chain; /* list of pids with the same nr, only one of them is in the hash */ struct list_head pid_list; }; Le champ nr contient habituellement le pid du processus, on va donc le mettre a zero. --[ 4 Application owned rootkit # ps aux | grep backdoor test 8603 0.0 0.1 1320 268 pts/5 S+ 14:57 0:00 ./backdoor root 8605 0.0 0.2 1516 472 pts/0 S+ 14:57 0:00 grep backdoor owned rootkit # chkproc owned rootkit # ./main -p -h 8603 PID 8603 is now hide !! owned rootkit # ps aux | grep backdoor root 8610 0.0 0.2 1516 472 pts/0 S+ 14:58 0:00 grep backdoor owned rootkit # chkproc owned rootkit # zeppoo -c -p Kernel : 2.6 Running on i386 !! Memory : /dev/kmem ------------------------------------------------------------------------------- [+] Begin : Task LIST OF HIDDEN TASKS PID UID GID NAME ADDR 8603 1000 100 backdoor @ 0xc2403570 [+] End : Task ------------------------------------------------------------------------------- --[ 5 Conclusion Detecter les rootkits en etant en userland et donc en faisant confiance au systeme ne permet pas d'avoir une fiabilite sur les informations recueillies. Une solution est de se placer directement en kernelland ou de coupler userland et kernelland grace aux devices /dev/kmem et /dev/mem, mais il est egalement possible de bypasser cette solution mais moins facilement :x Have fun !! References : [1] http://www.chkrootkit.org [2] http://www.zeppoo.net