-
Notifications
You must be signed in to change notification settings - Fork 1
Home
This is a simple LKM rootkit detection tool for Linux 4.4+ on x86-64.
- Detect Hidden PIDs
- Detect Hidden Files
- Detect Hidden Network Ports
- Detect Hooked Functions
- Detect Hidden Modules
- 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
- Linux Headers
- GCC Compiler (Version > 5.0.0)
- The Sleuth Kit (TSK)
- Python 3
- pytsk3 library
For Debian-based distros:
sudo apt install linux-headers-$(uname -r) build-essential sleuthkit python3 python3-pip
pip3 install pytsk3
git clone https://github.com/lckjosh/DetectionTool.git
cd DetectionTool
make
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
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.
Adapted from inodeyou: http://www.unixist.com/security/detecting-hidden-files/index.html
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. 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 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
Recommended Usage (For the stand-alone script):
python3 hidden-inode-detector.py /dev/sda1 / /
The above python command 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/sda1, where /dev/sda1 is the partition where the root directory or directory to be scanned is mounted on. When the user subsequently invokes this script, it will be compared to the baseline.
The Python script for this function is embedded in the user-space C program using Python/C API.
The script roughly takes around a few minutes to execute and can take longer depending on the number of inodes hidden by a possible rootkit.
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 approach is that since the port space of IPv4 and IPv6 is shared, if a port number is hidden in IPv4 but not IPv6 and netstat or ss shows the port number in IPv6, this function will not detect the hidden IPv4 port.
This function detects:
- hooked system calls
- hooked fops for "/", "/proc" and "/sys"
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.
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.
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
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.
| Rootkit | Detect Hidden PIDs | Detect Hidden Files | Detect Hooked Functions | Detect Hidden Modules |
|---|---|---|---|---|
| Diamorphine | ✓ | ✓ | ✓ | ✓ |
| LilyOfTheValley | ✓ | ✓ | ✓ | |
| Nuk3Gh0st | ✓ | ✓ | ✓ | ✓ |
| Reptile | ✓ | ✓ | ✓ | |
| nurupo/rootkit (4.4 only) | ✓ | ✓ | ✓ | ✓ |
| puszek-rootkit (4.4 only) | ✓ | ✓ |
| Rootkit | Detect Hidden PIDs | Detect Hidden Files | Detect Hooked Functions | Detect Hidden Modules |
|---|---|---|---|---|
| LilyOfTheValley (forked) | ✓ | ✓ | ✓ | |
| Reptile | ✓ | ✓ | ✓ |