Skip to content
Fai Yew edited this page Aug 7, 2020 · 50 revisions

Detection Tool

This is a simple LKM rootkit detection tool for Linux 4.4+ on x86-64.

Features

Tested Environments

  • Ubuntu 16.04
    • 4.15.0-45-generic
    • 4.4.0-22-generic (for rootkits only compatible with 4.4)
  • Ubuntu 18.04
    • 5.3.0-28-generic
  • CentOS 8.0
    • 4.18.0-80.el8.x86_64

Installation

Dependencies

  • Linux Headers
  • GCC Compiler (Version > 5.0.0)
  • The Sleuth Kit (TSK)
  • Python 3
  • pytsk3 library

For Debian-based distros (Ubuntu 16.04 & Ubuntu 18.04):

sudo apt install linux-headers-$(uname -r) build-essential git sleuthkit python3 python3-pip python3-dev
pip3 install pytsk3

For RHEL-based distros (CentOS 8):

sudo dnf -y install epel-release
sudo dnf config-manager --set-enabled PowerTools
sudo dnf -y install make automake elfutils-libelf-devel gcc gcc-c++ git kernel-devel-$(uname -r) python3-devel
wget --no-check-certificate "https://forensics.cert.org/cert-forensics-tools-release-el8.rpm"
sudo dnf -y install cert-forensics-tools-release-el8.rpm
sudo dnf -y install sleuthkit
sudo pip3 install pytsk3

Cloning and Compilation

git clone https://github.com/lckjosh/DetectionTool.git
cd DetectionTool
make

Usage

NOTE: RUN sudo ./client -f upon initial insertion of module to form initial baseline for detecting hidden inodes.

sudo insmod detectiontool.ko
./client [option]

Options:
[-p] detect hidden PIDs (run with sudo)
[-f partition-to-scan ] detect hidden files (run with sudo)
[-n] detect hidden network ports
[-s] detect hooked functions
[-m] detect hidden modules

Examples:
./client -p
./client -f /dev/sda1 
./client -n 
./client -s
./client -m 

Explanation of Functions

Detect Hidden PIDs

Adapted from unhide.

This function compares the outputs of the following system calls with PIDs as input parameters:

  • kill()
  • getpriority()
  • getpgid()
  • getsid()
  • sched_getaffinity()
  • sched_getparam()
  • sched_getscheduler()
  • sched_rr_get_interval()

along with

  • /proc entries
  • ps command.

Discrepancies in the outputs can provide information on which PIDs were hidden and which system calls were hooked. For example, if certain system calls return an error when given a specific PID and others return without an error, we can identify which functions were hooked and which PIDs were hidden.

Detect Hidden Files

Adapted from inodeyou

Enhanced Version used by this LKM: https://github.com/fy-fy-fy/inodeyou-again

inodeyou compares the view of the filesystem inodes via the read() syscall and from the getdents() and stat() syscalls. For example, if an inode is discovered by the read() syscall and not from getdents() and stat(), it is likely that either the read(), getdents(), or stat() syscall is hooked by the rootkit, so that some entries are hidden by the ls command.

However, this may still return many false positives when scanning recursive directories. A false positive occurs when the script incorrectly states that a rootkit is hiding a particular inode when it is not.

The original inodeyou functionality is extended by using integrity-based checks by comparing a compromised system with a clean system, which eliminates most of the false positives. Using an integrity-based check also shortens the time it takes for the script to find positives. The Sleuth Kit (TSK) commands are also used to reduce further false positives by ignoring recently deleted inodes. Other false positives entries can be placed into a text file false-positives.txt which will be ignored by the script.

Dependencies required for this script are:

  • Python 3 (Version 3.5)
  • The Sleuth Kit (TSK)
  • python3-dev
  • pytsk3 module

The inodeyou python script python3 hidden-inode-detector.py /dev/sda1 / / searches hidden inodes from the root / directory recursively, whereby the root is mounted on the /dev/sda1 partition. When the LKM is loaded, the user should form the initial baseline by running sudo ./client -f /dev/sdaX, where /dev/sdaX is the partition where the root directory or directory to be scanned is mounted on. inodeyou assumes that /dev/sdaX partition has a ext2/ext3/ext4 file system as other file system types such as xfs are not supported by The Sleuth Kit.

//TODO: Add image

When the user subsequently invokes this script after generating the initial baseline with sudo ./client -f /dev/sda1, results will be compared to the baseline. The figure below shows an example of when the LilyOfTheValley rootkit is loaded into the system before the second run of the command, detecting the inodes hidden by the rootkit

//TODO: Add image

The Python script for this function is embedded in the user-space C program using Python/C API. The command ./client -f /dev/sdaX in turn executes the python script with python3 hidden-inode-detector.py /dev/sdaX / /

The script roughly takes around a few minutes to execute and can take longer depending on the computing power of the system, number of recently deleted inodes since a previous scan on the filesystem, and the number of inodes hidden by a rootkit.

Detect Hidden Network Ports

Adapted from unhide.

This function tries to detect hidden network ports by finding TCP and UDP ports that are currently used but not shown in the output of either netstat or ss. It does this by iterating through the possible port numbers on a system, creating sockets with those port numbers and trying to call bind() and listen() (only for TCP ports) on those sockets. If bind() or listen() returns EADDRINUSE and the port is not shown in the output of either netstat or ss, we can deduce that the port is hidden.

A limitation of this function is that since IPv4 and IPv6 port space is shared by default, in the rare scenario that a process binds an IPv6 socket with a specific port number without specifying the IPV6_V6ONLY option, trying to bind an IPv4 socket with the same port number would return EADDRINUSE even though the port is not shown as an IPv4 port in either netstat or ss, resulting in a false positive.

A theoretical way to remove this false positive is to find a method to create an IPv4 socket to listen only on IPv4 ports and disregard ipv6 ports, similarly to how we create ipv6 sockets to listen only to IPv6 ports with the IPV6_V6ONLY socket option to disregard the IPv6 socket picking up on used IPv4 ports. However, we did not manage to do so and have yet to find a straightforward way to achieve this mechanism for IPv4 sockets.

Detect Hooked Functions

This function detects:

  • hooked system calls
  • hooked fops for /, /proc and /sys
  • hooked network protocol functions

Hooked system calls

This tool is able to detect hooked system calls in the system call table that are hooked via replacing the address of the original function in the table with the address of a hooked function, or by using inline hooking with the unconditional jmp code of

\x48\xb8\x00\x00\x00\x00\x00\x00\x00\x00\xff\xe0

where the 8 bytes in the middle are replaced with the address of the hooked function.

When the function is run, the addresses of each system call in the system call table is checked if it is within the core kernel text. If the address is not, it it likely that the system call has been replaced. If the address of the system call is within the core kernel text, the first 12 bytes are read and bytes 0,1,10 and 11 are compared with the above unconditional jmp code to detect for inline hooking.

Advantages of using the core kernel text method instead of creating a dump of all system calls when the module is inserted and comparing their addresses when this function is called include:

  • better performance
  • hooked system calls can be identified correctly even if this LKM is inserted after a rootkit LKM

Even though this method will not identify hooked system calls where addresses have been changed to another address still within the core kernel text (e.g. the address of another system call), it is not feasible for a rootkit to do so while still maintaining original functionality of the system call and maintaining stealth and we could not find any open source LKM rootkit that does so.

Hooked fops

The fops of /, /proc and /sys are sometimes hooked instead of system calls to avoid hook detection in the system call table. This tool scans for hooks in the fops of these directories as well using the same methods as above.

Hooked network protocol functions

The tool also scans for the network function seq_ops.show in /proc/net/tcp, /proc/net/tcp6, /proc/net/udp, /proc/net/udp6 and checks if it is hooked using the methods above.

Detect Hidden Modules

Adapted from tyton.

This function checks if find_module is able to find the module referenced for each kobj entry. If it is unable to, the module is trying to hide. This function only works if the rootkit has the ability to both hide AND unhide itself.

Testing Results

Ubuntu 16.04

Rootkit Detect Hidden PIDs Detect Hidden Files Detect Hooked Functions Detect Hidden Modules Detect Hidden Network Ports
Diamorphine N.A.
LilyOfTheValley N.A.
Nuk3Gh0st
Reptile N.A.
nurupo/rootkit (4.4 only) N.A.
puszek-rootkit (4.4 only) N.A. (broken) N.A. (done with syscall hooking)

Ubuntu 18.04

Rootkit Detect Hidden PIDs Detect Hidden Files Detect Hooked Functions Detect Hidden Modules Detect Hidden Network Ports
LilyOfTheValley (forked) N.A.
Reptile N.A.

CentOS 8.0

Rootkit Detect Hidden PIDs Detect Hidden Files Detect Hooked Functions Detect Hidden Modules Detect Hidden Network Ports
LilyOfTheValley (forked) N.A.
Reptile N.A.

Clone this wiki locally