Kernel Rootkit Tricks
The Spy Within
ByRootkits allow attackers to take complete control of a computer. We describe the tricks intruders use to gain access to the Linux kernel and provide guidelines on hardening the kernel against such attacks.
In response to a question asked in parliament, the German Federal Government responded in May 2012 that it was able to decrypt PGP messages or SSH connections, at least partially. Experts were exasperated when they heard this. No one seriously believed that PGP encryption had been cracked. More likely, data is sniffed before encryption or after decryption, or the secret service possesses the private key and, possibly, the associated password. Germany’s “Federal Trojan” can infiltrate the kernel as a rootkit for this purpose.
Classically, the term “rootkit” refers to a piece of software that gives an attacker camouflaged access to, and thus control over, a machine. Userland rootkits tend to modify applications to do this. In comparison, the much more powerful kernel rootkits change kernel data structures and code – for example, through system call hijacking.
Hijacked
Hijacking a system call is simple in theory. A system call, which is a job for the operating system kernel, is identified by a number that is used as an index in a table with function addresses. These addresses point to functions that implement the respective system calls. In the Linux kernel, this table is sys_call_table.
To hijack the system call, the attacker replaces the respective function address with the address for their typically malicious function (Figure 1).
In the scope of this function, the attacker can call parameters, change the code of the original system call, and copy or manipulate the results. To grab all keystrokes, including any passwords entered by the user, the attacker replaces the sys_read() system call with their malicious variant, which I’ve called evil_sys_read(). This function first calls the original sys_read() system call, which reads the requested data via the hardware and copies the data to the application’s memory space (userland). Before evil_sys_read() returns control to the application, it accesses the data in userland and does something evil, like redirecting it to the attacker’s server on the Internet.
Linux is Becoming More Secure
Kernel 2.4 made this kind of hijacking attack comparatively simple. With the help of a Loadable Kernel Module (LKM), the attacker could read the initial address from the sys_call_table and replace its entries. Kernel versions 2.6 and 3.0 made this attack more difficult. The sys_call_table address is no longer defined globally, so it is thus unknown to a kernel module. Additionally, the sys_call_table resides in a segment that cannot be changed.
However, several attack vectors are able to determine the address of the sys_call_table and write to the non-writeable segment. The program code in Listing 1 allows an attacker to manipulate read-only code and data in the kernel.
Listing 1: Editing Read-Only Areas
01 void disable_write_protection_cr0( void ) 02 { 03 unsigned long value; 04 05 asm volatile("mov %%cr0,%0":"=r" 06 (value)); 07 if( value & 0x00010000 ) { 08 value &= ~0x00010000; 09 asm volatile("mov %0,%%cr0"::"r" 10 (value)); 11 } 12 }
Working around the write protection is relatively easy because the attacker already has root privileges and runs code in the kernel.
Listing 1 shows a sequence for an x86 architecture that deletes the 16th bit in the control register, CR0. This bit generally enables or disables write protection. The bad news: You have no way of preventing this attack.
Address List
To determine the address of the sys_call_table, however, an intruder has to demonstrate a little more skill. With a bit of luck, they might find (e.g., on Ubuntu) a list of all kernel addresses on the computer. The distribution creates this list while building the kernel and stores it in /boot/System.map-<kernel version>.
Even if this list is not there, the attacker doesn’t need to give up just yet. A trick for the x86 platform lets intruders discover the sys_call_table address in the Linux kernel itself. To do so, you only have follow the code path that Linux processes when a syscall occurs; it is well known that a software interrupt is used to call the system call.
The address of the called function system_call() is located in the Interrupt Descriptor Table (IDT). The address of the IDT, in turn, is located in the IDT register of the CPU and thus is very easy to access. The system_call() function accesses the sys_call_table using the call machine command. This assembler command results in an easy-to-find fingerprint of three bytes after the requested four-byte address of the sys_call_table.
Figure 2 summarizes the approach: The attacker reads the IDTR (1) and obtains the address of the IDT. The entry for index 0x80 (2) within the table points to the system_call() function. Beginning at the start address for the function (3), the attacker just needs to search a few bytes in memory after the fingerprint (4) with the call to the selected system call to discover the address of the sys_call_table (5).
In the Data Segment
Other ways to access the address of the sys_call_table are demonstrated by various kernel rootkits, which are easily found on the Internet. For example, the Intoxonia rootkit searches the data segment for the address of the sys_close system call. This address is exported by the kernel and is thus known to a loadable module. Anyone who finds the address in the data segment can compute the start of the sys_call_table.
On x86, rootkits could also infest the system via the debug registers. The CPU provides a total of four debug registers that can each store an address. Once a code sequence accesses one of the stored addresses, an interrupt is triggered, and the Linux kernel jumps via the IDT to the do_debug() function.
If an attacker stores the address of the system_call function in a debug register, while replacing the address of do_debug() with the address of the rootkit (e.g., evil_do_debug()), each system call will enable the rootkit. Of course, the attack vector via debug registers only works on x86; however, these attacks are available on other Linux platforms in modified form – for example, on Android.
Security researchers have published other methods, in combination with proof of concept rootkits, that also support rootkit injection. The idea behind these rootkits has less to do with facilitating the work of secret service agents and more to do with developing preventive defensive measures.
The Good News
Two approaches help defend a system against rootkits. The first approach prevents the installation of a kernel rootkit, and the second takes actions that prevent, or at least detect, kernel injection, such as system call hijacking (Figure 3).
To prevent a kernel rootkit being installed, admins need to configure and compile their own kernel. Prominent barn doors for rootkits include /dev/kmem and /dev/mem, whose drivers need to be removed from the kernel configuration. Baddies also like to use loadable kernel modules (LKM); admins are advised to disable their support, which makes it necessary to build the required drivers into the kernel.
Additionally, you will want to disable the ksplice and kexec mechanisms. ksplice allows a kernel update without a reboot, and kexec enables a reboot that works around the BIOS. As long as the attacker cannot install their own kernel, this keeps the attacker out (for want of a fatal kernel bug), even if they have root privileges.
For the second approach, researchers have developed a number of tools that prevent a rootkit’s central activities and work like an Intrusion Detection System (IDS) by saving hash sums, directly after the install, of essential system tables, including the IDT or the sys_call_table. A tool verifies these checksums at regular intervals, and if it detects any changes to the initially registered comparison values, you can assume a rootkit is at work.
Another method monitors write access to the sys_call_table using, for example, the above-mentioned debug registers. As soon as a write attempt occurs, the alarm bells go off. In many cases, researchers also deploy hypervisor (i.e., virtualization) technologies to locate malicious software regardless of the operating system.
All of these tools have a minor disadvantage: They were created in an academic environment and published, then they disappeared into a drawer – no source code, no maintenance, no package. Currently, it seems that only the samhain IDS is ready for use to detect and prevent kernel rootkits, although you have a bit more choice in userland rootkits (see the “Userland Rootkits” box).
The Author
Jürgen Quade is a professor at the Lower Rhine University (Hochschule Niederrhein) and has been an avid supporter of Open Source since the beginnings of Linux. He co-authored the book Linux-Treiber entwickeln [Developing Linux Drivers, in German].
Issue 269/2023
Buy this issue as a PDF
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Find SysAdmin Jobs
News
-
Kubuntu Focus Announces XE Gen 2 Linux Laptop
Another Kubuntu-based laptop has arrived to be your next ultra-portable powerhouse with a Linux heart.
-
MNT Seeks Financial Backing for New Seven-Inch Linux Laptop
MNT Pocket Reform is a tiny laptop that is modular, upgradable, recyclable, reusable, and ships with Debian Linux.
-
Ubuntu Flatpak Remix Adds Flatpak Support Preinstalled
If you're looking for a version of Ubuntu that includes Flatpak support out of the box, there's one clear option.
-
Gnome 44 Release Candidate Now Available
The Gnome 44 release candidate has officially arrived and adds a few changes into the mix.
-
Flathub Vying to Become the Standard Linux App Store
If the Flathub team has any say in the matter, their product will become the default tool for installing Linux apps in 2023.
-
Debian 12 to Ship with KDE Plasma 5.27
The Debian development team has shifted to the latest version of KDE for their testing branch.
-
Planet Computers Launches ARM-based Linux Desktop PCs
The firm that originally released a line of mobile keyboards has taken a different direction and has developed a new line of out-of-the-box mini Linux desktop computers.
-
Ubuntu No Longer Shipping with Flatpak
In a move that probably won’t come as a shock to many, Ubuntu and all of its official spins will no longer ship with Flatpak installed.
-
openSUSE Leap 15.5 Beta Now Available
The final version of the Leap 15 series of openSUSE is available for beta testing and offers only new software versions.
-
Linux Kernel 6.2 Released with New Hardware Support
Find out what's new in the most recent release from Linus Torvalds and the Linux kernel team.