
FreeBSD Kernelland-Trickery / Gain root access via syscall
Date: 2012-07-16 13:46:50This time I will focus on FreeBSD kernel developement. The recent stable version of FreeBSD is 9.0, but for this example we will use a version 8.1 with i386 architecture.
After several years of coding in userland I found changing the operating system internals quite enjoyable. In contrast to many programs found in the wild, kernelcode is usually clean and properly written, at least in BSDland. For this first issue I will focus on a simple syscall module which assigns a user uid 0 just by performing a syscall.
First of all, *nix operating systems heavily depend on syscalls. They are what every other operation is based on. Think of opening a file, which starts several syscalls. Most interestingly: open(), read(), close(). Unix knows several hundred system calls, which I will turn to in a later blog entry. For now just accept (which is a syscall as well) that those are quite important and useful.
For building a FreeBSD module your best bet is:This directory holds everything for a first module. For building a syscall module you will need the following Makefile:
Reading the module, an overview:
- 1. We include the required headerfiles here. Those are important. In bigger projects you might want to store them in a separate file is there are quite a lot of them.
- 2. This is the only function called by our module when the syscall is executed: We print a “hello curesec”.
- 3. The function for loading and unloading the module. Please note that a offset for the syscall is printed.
- 4. The module is defined as SYSCALL_MODULE. We are interested in the first two positions which define the module’s name in the kernel structure and the offset at which the module is called. As already mentioned, Unix has quite some syscalls. When a new one is loaded into the kernel, the next free is taken. On FreeBSD this is usually syscall number 210.
OK, let’s compile the module and execute it:
Switch to console window or execute dmesg to the output of the module. Now for diving a bit deeper into it, lets have a look at the hello() function. It is executed with:
digging through struct proc, I arrive at “struct ucread”, lets have a short overview:
We can see that through the struct thread of the called function, struct ucred, for user credentials, is accessible. Lets try to change the user credentials by changing the hello function. For this we need to reference the structure in our function and change it to the uid we want to assign ourselves. As this is a Unix the most interesting user is “root”.
Have a look at the following lines:
We insert these into our module, compile it, and load it into the kernel. You might have recognized a new function “uprintf” which prints the message on the current console window.
There a various ways for calling the module. For now I choose the path of the test call application delivered in the example’s directory. In short terms it searches for the number of the systemcall with modstat and calls it without any argument. This is all we need.
Please note that you need to change the name of syscall to the name of your module if it is no longer called “syscall”. As root user you can now load this module with the command:
If you execute kldstat you should see your newly loaded module.
Log in as a non-privileged user and call the module with the caller binary. Check for the current privileges to confirm you are running as systemuser.
For production usage this module will need some sort of protection, otherwise any user on your system knowing about this feature could become instant root, which is not desired. For this purpose you can use the functions copyin and copyout which are for userland and kernelland communication. I will talk about them in another blogentry.
Please keep in mind that techniques like these are also used by attackers to maintain root access to your system.