Detecting Kernel rootkits

This option is currently supported only for Linux, kernel versions 2.2.x, 2.4.x, and 2.6.x on ix86 machines, and for FreeBSD (tested on FreeBSD 4.6.2).

WarningWarning
 

It is incorrect to assume that disabling support for loadable kernel modules protects against runtime kernel modifications. It is possible to modify the kernel via /dev/kmem as well.

To use this facility, you need to compile with the option:

./configure --with-kcheck=/path/to/System.map (Linux), or

./configure --with-kcheck (FreeBSD).

On Linux, System.map is a file (sometimes with the kernel version appended to its name) that is generated when the kernel is compiled, and is usually installed in the same directory as your kernel (e.g. /boot), or in the root directory. To find it, you can use: locate System.map

NoteNOTE
 

On Linux, after installing a new kernel, you need to configure five (5) addresses (see configuration example below), otherwise the kernel check will not work anymore (samhain needs to know the new position of some objects within the kernel). As explained below, you can easily obtain the required values by grepping them from the System.map of your new kernel, which should normally be installed into the /boot directory, together with the kernel.

NoteNOTE
 

If you also use the option ./configure --enable-khide to use a kernel module to hide the presence of samhain, the first detected modification of the sys_getdents syscall (to list directories) will only cause a warning (rather than an error), as it is presumed to be caused by the samhain_hide LKM).

You should NOT initialize the database with the samhain_hide LKM loaded (doing so might result in the non-detection of a real rootkit if it also only modifies the sys_getdents syscall).

Configuration

This facility is configured in the Kernel section of the configuration file.
  [Kernel]  
  # activate (0 for switching off) 
  KernelCheckActive=1
  # interval between checks (in seconds, default 300)
  KernelCheckInterval=20 
  # also check the interrupt descriptor table (default TRUE)
  KernelCheckIDT=TRUE 
  # this is the severity (see section the Section called Severity levels in the chapter called Configuration of logging facilities) 
  SeverityKernel=crit 
  #
  # Only needed for Linux, after installing a new kernel. You need the address
  # (first item in the grepped line), prefixed with '0x' to indicate
  # hexadecimal format.
  #
  # this is the address of system_call (grep system_call System.map) 
  KernelSystemCall = 0xc0106cf8
  #
  # this is the address of sys_call_table (grep ' sys_call_table' System.map) 
  KernelSyscallTable = 0xc01efb98
  #
  # this is the address of proc_root (grep ' proc_root$' System.map) 
  KernelProcRoot = 0xc01efb98
  #
  # this is the address of proc_root_inode_operations 
  # (grep proc_root_inode_operations System.map) 
  KernelProcRootIops = 0xc01efb98
  #
  # this is the address of proc_root_lookup
  # (grep proc_root_lookup System.map) 
  KernelProcRootLookup = 0xc01efb98

  

What is a kernel rootkit ?

A rootkit is a set of programs installed to "keep a backdoor open" after an intruder has obtained root access to a system. Usually such rootkits are very easy to install, and provide facilities to hide the intrusion (e.g. erase all traces from audit logs, install a modified 'ps' that will not list certain programs, etc.).

While "normal" rootkits can be detected with checksums on programs, like samhain does (the modified 'ps' would have a different checksum than the original one), this method can be subverted by rootkits that modify the kernel at runtime, either with a loadable kernel module (LKM), i.e. a module that is loaded into the kernel at runtime, or by writing to /dev/kmem (this allows to 'patch' a kernel on-the-fly even if the kernel has no LKM support).

Kernel rootkits can modify the action of kernel syscalls. From a users viewpoint, these syscalls are the lowest level of system functions, and provide access to filesystems, network connections, and other goodies. By modifying kernel syscalls, kernel rootkits can hide files, directories, processes, or network connections without modifying any system binaries. Obviously, checksums are useless in this situation.

Implemented integrity checks

When a system call (e.g. open() to open a file) is made by an application, the flow of control looks like this:

  1. An interrupt is triggered, and execution continues at the interrupt handler defined for that interrupt. On Linux, interrupt 80 is used.

    A rootkit could replace the kernels interrupt handler by an own function.

    Samhain checks the Interrupt Descriptor Table for modifications.

  2. The interrupt handler (named system_call() on Linux) looks up the address of the requested syscall in the syscall table, and executes a jump to the respective address.

    A rootkit may (a) modify the interrupt handler to use a (rootkit-supplied) different syscall table, or (b) modify the entries in the syscall table to point to the rootkits replacement functions.

    Samhain checks (a) the interrupt handler, and (b) the syscall table for modifications.

  3. The syscall function is executed, and control returns to the application.

    A rootkit may overwrite the syscall function to place a jump to its own replacement function at the start of the syscall function.

    Samhain checks the first few bytes of each syscall function for modifications.

In addition to these checks, Samhain will check the /proc inode to detect the adore-ng rootkit, which does not modify any syscall execution, but rather the VFS (Virtual File System) layer of the kernel.

On FreeBSD, currently only the syscall table (2b) and the system call (3) are checked.

Error messages

Error messages start with 'POLICY KERNEL'. There are four types of them: (a) 'IDT' signifies modified interrupts: old and new address, segment, privilege level, and type are listed, (b) SYSCALL: modified syscall table/syscall code interrupt handler, and (c) SYS_GATE: modified interrupt handler for syscalls. (d) PROC: modified /proc system

If an empty slot in the interrupt descriptor table (old address zero) has been modified, this indicates that a new interrupt has been installed. This cannot modify the behaviour of user applications (which would not use that interrupt), but could be used by a dedicated (rootkit-supplied) application to perform some action (e.g. elevate privileges).

Likewise, if an empty slot in the syscall table (syscall name sys_ni_syscall/_nosys) has been modified, this cannot modify the behaviour of user applications, but again could be used by a dedicated (rootkit-supplied) application to perform some action.

NoteNOTE
 

As of version 1.8.4, kernel info is stored in the baseline database by (mis-)using fields that normally describe some properties of files. You may therefore find that error messages have info appended that looks like properties you would normally expect for a file (e.g. mtime, ctime, link_path ...). This is required for server-side database update (if you use samhain as client/server system).