arrow-left

All pages
gitbookPowered by GitBook
1 of 1

Loading...

The Ultimate Aim of Kernel Exploitation - Process Credentials

hashtag
Overview

Userspace exploitation often has the end goal of code execution. In the case of kernel exploitation, we already have code execution; our aim is to escalate privileges, so that when we spawn a shell (or do anything else) using execve("/bin/sh", NULL, NULL) we are dropped as root.

To understand this, we have a talk a little about how privileges and credentials work in Linux.

hashtag
The cred struct

The contains all the permissions a task holds. The ones that we care about are typically these:

These fields are all unsigned int fields, and they represent what you would expect - the UID, GID, and a few other less common IDs for other operations (such as the FSUID, which is checked when accessing a file on the file system). As you can expect, overwriting one or more of these fields is likely a pretty desirable goal.

circle-info

Note the __randomize_layout here at the end! This is a compiler flag that tells it to mix the layout up on each load, making it harder to target the structure!

hashtag
task_struct

The kernel needs to store information about each running task, and to do this it uses the structure. Each kernel task has its own instance.

The task_struct instances are stored in a linked list, with a global kernel variable init_task pointing to the first one. Each task_struct then points to the next.

Along with linking data, the task_struct also (more importantly) stores real_cred and cred, which are both pointers to a cred struct. The difference between the two is explained :

In effect, real_cred is the initial credential of the process, and is used by processes acting on the process. cred is the current credential, used to define what the process is allowed to do. We have to keep track of both as some processes care about the initial cred and some about the updated.

An example of caring about the real_cred instead of cred is in of /proc/$PID/status, which displays the real_cred as the owner of a process, even if privileges are elevated (note that is a macro to grab real_cred, confusingly). Conversely, setuid executables will modify cred and not real_cred.

So, which set of credentials do we want to target with an arbitrary write? It will depend on what set is relevant for the purpose, but since you usually want to do be creating new processes (through system or execve), the cred is used. In some cases, real_cred will work too, because it seems as if the pointers (though note that this excerpt is not from process creation but , which is , so it could differ for new process creation).

hashtag
prepare_kernel_cred() and commit_creds()

As an alternative to overwriting cred structs in the unpredictable kernel heap, we can call prepare_kernel_cred() to generate a new valid cred struct and commit_creds() to overwrite the real_cred and cred of the current task_struct.

hashtag
prepare_kernel_cred()

The function can be found , but there's not much to say - it creates a new cred struct called new then . It returns new.

If NULL is passed as the argument, it will , which . This is very important, as it means that calling prepare_kernel_cred(0) results in a new set of root creds!

circle-exclamation

This last part is different on newer kernel versions - check out section!

hashtag
commit_creds()

This function is found , but ultimately it will update task->real_cred and task->cred to the new credentials:

hashtag
Resources and References

cred structarrow-up-right
task_struct arrow-up-right
herearrow-up-right
the implementationarrow-up-right
__task_structarrow-up-right
initially point to the same structarrow-up-right
copy_processarrow-up-right
called by the fork syscallarrow-up-right
herearrow-up-right
destroys the oldarrow-up-right
return a new set of credentials that match the init_task credentialsarrow-up-right
default to root credentialsarrow-up-right
Debugging the Kernel Module
herearrow-up-right
Xarkes' Baby Kernel 2 writeuparrow-up-right
TeamItaly's FamilyRecipes writeuparrow-up-right
struct cred {
	/* ... */
	
	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 */
	kuid_t		fsuid;		/* UID for VFS ops */
	kgid_t		fsgid;		/* GID for VFS ops */
	
	/* ... */
} __randomize_layout;
struct task_struct {
    	/* ... */
    
	/*
	 * Pointers to the (original) parent process, youngest child, younger sibling,
	 * older sibling, respectively.  (p->father can be replaced with
	 * p->real_parent->pid)
	 */

	/* Real parent process: */
	struct task_struct __rcu	*real_parent;

	/* Recipient of SIGCHLD, wait4() reports: */
	struct task_struct __rcu	*parent;

	/*
	 * Children/sibling form the list of natural children:
	 */
	struct list_head		children;
	struct list_head		sibling;
	struct task_struct		*group_leader;

	/* ... */    

	/* Objective and real subjective task credentials (COW): */
	const struct cred __rcu		*real_cred;

	/* Effective (overridable) subjective task credentials (COW): */
	const struct cred __rcu		*cred;

    	/* ... */
};
/*
 * The security context of a task
 *
 * The parts of the context break down into two categories:
 *
 *  (1) The objective context of a task.  These parts are used when some other
 *	task is attempting to affect this one.
 *
 *  (2) The subjective context.  These details are used when the task is acting
 *	upon another object, be that a file, a task, a key or whatever.
 *
 * Note that some members of this structure belong to both categories - the
 * LSM security pointer for instance.
 *
 * A task has two security pointers.  task->real_cred points to the objective
 * context that defines that task's actual details.  The objective part of this
 * context is used whenever that task is acted upon.
 *
 * task->cred points to the subjective context that defines the details of how
 * that task is going to act upon another object.  This may be overridden
 * temporarily to point to another security context, but normally points to the
 * same context as task->real_cred.
 */
rcu_assign_pointer(task->real_cred, new);
rcu_assign_pointer(task->cred, new);