A loadable kernel module (LKM) is the easiest way to create a rootkit, although it is also the most noisy and easiest to defend against. Once root (or system level privileges) is gained on a machine, a rootkit is the best way to maintain root access to that machine.
Here I will try to explain the basics of what a LKM actually is and how to create and test a very basic one for Linux.
An LKM is a plugin to the kernel. It allows you to run code with the same permissions as the kernel, which isn't possible for normal userland applications. Device drivers are LKM's as they need permission to access the computers hardware, so either with or without knowing it, you already have some experience with LKM's. Throughout this post I will be using LKM and module interchangeably.
Creating A Hello World LKM
Here is the code for the LKM that we will be creating:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
Lines 4 and 5 and just some information about the module. Line 6 is needed otherwise when we load the module we get the following error message in the systems log:
hello: module license 'unspecified' taints kernel.
The module will still load but as we are learning to write a rootkit, we want as little 'noise' as possible.
hello_init on lines 8 - 12 runs when the module is loaded, here we are just printing "Hello World!\n" to the system log. The function
hello_exit on lines 14 - 18 runs when the module is unloaded, here we are just printing "Unloading hello.\n" to the system log. They are defined as such on lines 20 and 21.
Compiling The LKM
To compile it we need a
Makefile, the makefile below will do:
1 2 3 4 5 6 7
With both of these files in the same directory we can now compile our first LKM:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
As we can see, the
make command has created a number of files (
Module.symvers). The file we are interested in is
hello.ko on line 13, this is our module.
Loading/Unloading The LVM
I am using a 32 bit Debian based Linux system (Kali) for my development environment but this should work on any modern Linux system (Do not try this on a production machine! Working with the kernel always has the possiblity to crash the kernel and bring the whole system down! You have been warned!), older systems might require some changes.
Here is how we load and unload the module; and check that everything has worked:
1 2 3 4 5 6 7 8 9 10 11 12
So first I have shown you the Linux kernel version I am using with the
uname command on line 1, this is just so if it doesn't work for you, you can check if they are the same version. The
insmod command is used to load the module on line 3 and we check the system log to make sure it has printed the string "Hello World!\n" using the
dmesg command on line 4. The
lsmod command is used on line 6 to check if the module is actually loaded. The
rmmod command is used on line 8 to unload the module and the system log is checked again on line 9 to check that our printk has run correctly. Lastly we check with
lsmod again to make sure the module has been unloaded correctly.
So we have a working LKM.
It is very easy to make mistakes with any programming but the majority of mistakes in a normal application will not bring a system down. While its always important to build and test code in a development environment, its even more important when coding an application that runs in kernelland as any tiny mistake can, and most likely will, bring the system down.
Happy Hacking :-)