A collection of QEMU/KVM vulnerability reproductions spanning information leaks, memory corruption, assertion failures, denial-of-service bugs, and full VM-escape chains.
Each subdirectory contains a self-contained reproduction environment: build scripts, a minimal Linux guest (Linux 5.4.40 + busybox), and a skeleton exploit.
| Directory | CVE | QEMU Version | Component | Type |
|---|---|---|---|---|
CVE-2015-3456 (VENOM)/ |
CVE-2015-3456 | 2.2.0-rc1 | hw/block/fdc.c |
FIFO buffer overflow |
CVE-2015-5165/ |
CVE-2015-5165 | 2.3.0 | hw/net/rtl8139.c |
Information leak |
CVE-2015-7504/ |
CVE-2015-7504 | 2.4.0 | hw/net/pcnet.c |
Heap buffer overflow |
CVE-2016-4952/ |
CVE-2016-4952 | 2.6.2 | hw/scsi/vmw_pvscsi.c |
Host DoS (ring setup / infinite loop PoC) |
CVE-2019-6788/ |
CVE-2019-6788 | 3.1.0 | slirp/tcp_subr.c |
Heap buffer overflow |
CVE-2019-14378/ |
CVE-2019-14378 | 4.0.0 | slirp/ip_input.c |
Heap buffer overflow |
CVE-2020-8608/ |
CVE-2020-8608 | 4.2.1 | slirp/tcp_subr.c |
Heap buffer overflow |
CVE-2020-14364/ |
CVE-2020-14364 | 4.2.1 | hw/usb/core.c |
Out-of-bounds write |
CVE-2020-25084/ |
CVE-2020-25084 | 4.2.1 | hw/usb/hcd-xhci.c + hw/usb/core.c |
Assertion failure (xHCI) |
Scavenger/ |
N/A (no CVE) | 4.2.1 | hw/block/nvme.c |
Uninitialized free (NVMe CMB) |
fdctrl_handle_drive_specification_command() in hw/block/fdc.c does not call
fdctrl_reset_fifo() when a DRIVE_SPECIFICATION_COMMAND byte has bit-7 clear and
data_len <= 7, leaving data_pos to advance past the 512-byte FIFO boundary on
subsequent writes. Any adjacent heap data (e.g., a QEMUBH.cb function pointer)
can be overwritten.
Patched in QEMU 2.3.0 (commit e6f4d0b) by adding the missing else branch that
calls fdctrl_reset_fifo(). Confirmed working — clean QEMU v2.2.0-rc1 crashes
with SIGSEGV at aio_bh_poll with rax = 0x4242424242424242.
rtl8139_cplus_transmit_one() in hw/net/rtl8139.c insufficiently validates
Ethernet/IP/TCP header lengths before performing checksum and TSO offload.
A malformed IPv4 frame with IP Total Length < IHL can make the transmit path
reuse uninitialised host heap bytes and leak them back to the guest through the
emulated NIC loopback path.
This repository reproduction programs the RTL8139 directly from the guest, enables C+ transmit offload plus internal loopback, sends a malformed frame, and then recovers non-zero tail bytes / host-like pointers from guest-visible RX buffers. The bug is fixed by the XSA-140 patch series, which adds the missing short-packet and header-length checks before offload continues.
pcnet_transmit() in hw/net/pcnet.c appends a 4-byte CRC/FCS to the loopback
transmit buffer. If the guest sends exactly 4096 bytes, the FCS write lands
4 bytes past the end of s->buffer, corrupting adjacent host device state
(s->irq) and typically crashing the QEMU process.
This reproduction configures the emulated AMD PCnet card from guest userspace,
places it into internal loopback mode, and sends an exact 4096-byte frame with
an attacker-chosen final FCS so the overwrite is easy to recognise under GDB.
The upstream fix reserves 4 bytes for the appended FCS (sizeof(s->buffer)-4).
This repository's implemented PoC exercises the PVSCSI ring-setup path in
hw/scsi/vmw_pvscsi.c: sending PVSCSI_CMD_SETUP_RINGS with
reqRingNumPages=0 makes pvscsi_ring_init_data() call
pvscsi_log2(0xffffffff), after which the host-side loop never terminates.
Attack vector: MMIO (PCI BAR0) via the PVSCSI adapter. The PoC is treated as a host DoS verification under GDB, not as a guest-to-host code-execution exploit.
tcp_emu() in slirp/tcp_subr.c handles FTP PORT and IRC DCC traffic by parsing
with sscanf then writing a rewritten string back into the same mbuf with snprintf,
without checking that the result fits within the mbuf boundary. A guest can trigger
the overflow by connecting to SLiRP's FTP emulation (port 21) or IRC (port 6667)
and sending a crafted PORT or DCC command.
Related: CVE-2019-6778 (same family, SLiRP tcp_emu FTP PORT).
ip_reass() in slirp/ip_input.c reassembles fragmented IP datagrams. The
allocated mbuf may be smaller than the total reassembled size when certain fragment
offset combinations are presented, causing the subsequent memcpy of fragment data
to overflow the mbuf.
Attack vector: raw IP socket inside the VM sending crafted fragmented UDP datagrams
to the SLiRP gateway (10.0.2.2).
Patched by adding a next > IP_MAXPACKET check before m_inc().
Closely related to CVE-2019-6788. tcp_emu()'s IRC DCC SEND handler in
slirp/tcp_subr.c rewrites the DCC packet's embedded IP address to the SLiRP
external address. When the rewritten address is longer than the original, the
snprintf call overflows the backing mbuf.
Attack vector: guest connects to 10.0.2.2:6667 and sends a DCC SEND with a 256-byte filename and a short internal IP that SLiRP expands to a longer external address.
do_token_setup() in hw/usb/core.c stores setup_len (derived from the
guest-controlled SETUP packet) into s->setup_len before checking whether it
exceeds sizeof(s->data_buf) (4096 bytes). Subsequent DATA stage packets copy up
to s->setup_len bytes into data_buf, providing a controlled out-of-bounds write.
Call chain: ehci_work_bh → ehci_advance_state → ehci_execute →
usb_handle_packet → do_token_setup. Requires --enable-spice at configure
time (enables the EHCI controller).
Patched by moving the s->setup_len assignment after the bounds check.
xhci_fire_ctl_transfer() in hw/usb/hcd-xhci.c sends a control transfer with
a DATA TRB whose direction field (TRB_TR_DIR) is set to OUT while the request is
an IN transfer (GET_DESCRIPTOR, bmRequestType=0x80). The direction mismatch causes
xhci_xfer_create_sgl() to call xhci_die() and destroy the SGL (nsg→0).
xhci_setup_packet() ignores the error; usb_packet_map() returns 0 with
iov.size=0; usb_handle_packet() → usb_packet_copy() fires
assert(p->actual_length + bytes <= iov->size).
Call chain: xhci_doorbell_write → xhci_kick_ep → xhci_kick_epctx →
xhci_fire_ctl_transfer → usb_handle_packet → usb_packet_copy → SIGABRT.
Affects QEMU 4.2.1 and 5.0.0; the QEMU 4.2.1 binary from CVE-2020-14364/ can
be reused.
(Black Hat Asia 2021 — not CVE-2020-25084)
nvme_dma_read_prp() in hw/block/nvme.c declares a QEMUSGList on the
stack without zero-initialising it, then passes it to nvme_map_prp(). On the
error path, nvme_map_prp() jumps to unmap: and calls qemu_sglist_destroy()
on the uninitialised stack struct, freeing stack garbage as heap pointers.
Full exploit chain (see Scavenger/README.md):
heap spray → virtio-gpu UAF primitive → chunk reclaim → physmap leak →
QEMU base derivation → timer hijack → system() on host.
Distinction from CVE-2020-25084: Scavenger targets
hw/block/nvme.c(NVMe CMB) and achieves full VM escape. CVE-2020-25084 targetshw/usb/hcd-xhci.c(xHCI USB) and causes only a DoS (SIGABRT). No CVE has been assigned to Scavenger.
Most directories follow a layout like:
CVE-XXXX-YYYY/
├── build.sh # downloads + builds QEMU & Linux kernel (shared source at ../linux-5.4.40)
├── exploit.sh # one-click verification (build check + GDB/timeout harness)
├── launch.sh # boots the VM interactively
├── attach.sh # boots the VM under GDB and auto-runs /exp via rdinit=/a.sh
├── kernel.config # optional scenario-specific kernel config fragment
├── .gitignore
├── README.md
└── rootfs/
├── exp.c # exploit source (guest-side C)
├── init # initramfs init script
├── a.sh # non-interactive exploit runner (rdinit target)
├── pack.sh # repacks rootfs.cpio from rootfs/
├── etc/ # passwd, group, hostname, …
├── root/ # welcome banner
└── bin/ # busybox symlinks (built by build.sh)
Generated artefacts (gitignored):
CVE-XXXX-YYYY/
├── qemu-system-x86_64 # vulnerable QEMU binary (copied by build.sh)
├── pc-bios/ # QEMU ROM files (copied by build.sh)
├── bzImage # guest kernel (compiled from ../linux-5.4.40)
├── rootfs.cpio # initramfs archive (packed by rootfs/pack.sh)
└── linux-build/ # out-of-tree kernel build directory
Shared infrastructure at repo root:
linux-5.4.40/ # shared kernel source tree (used by all CVEs)
default.config # common kernel config options (base for all builds)
cd CVE-XXXX-YYYY/
./exploit.shexploit.sh checks whether qemu-system-x86_64 is present (running build.sh
automatically if not), prints the expected success/crash/hang indicator, then
launches the documented verification harness. For CVE-2019-14378, which causes
a hang rather than a crash, a 90-second timeout is applied and exit code 124
confirms the bug.
cd CVE-XXXX-YYYY/
chmod +x build.sh launch.sh attach.sh rootfs/pack.sh rootfs/a.sh
./build.shbuild.sh builds the vulnerable QEMU tag (either in /tmp/ or in a local
source tree, depending on the directory) and copies qemu-system-x86_64 plus
pc-bios/ into the scenario directory. It builds the Linux guest kernel from
the shared source tree at ../linux-5.4.40, applying ../default.config
first and then the directory's own ./kernel.config fragment (where present),
and writes the out-of-tree build to ./linux-build/.
./launch.sh # opens a shell inside the VM; run /exp to trigger the bug./attach.sh # launches GDB; the VM boots with rdinit=/a.sh, which runs /exp
# automatically; GDB stops on the documented crash/hang point- Guest kernel: Linux 5.4.40, shared source at
linux-5.4.40/in the repo root. Common options are indefault.config; each CVE adds a./kernel.configfragment on top (e.g.CONFIG_E1000=yfor SLiRP CVEs,CONFIG_DEVMEM=yfor MMIO CVEs). CVE-2015-3456 (VENOM) uses only the default config. - Busybox: each directory uses a static
busyboxbinary inrootfs/bin/. If the binary is missing,build.shcopies it from the host system (/usr/bin/busyboxor/bin/busybox). Install withsudo apt-get install busybox-staticif needed. - QEMU 2.x quirks: requires Python 2 (
--python=python2) and building withmake IASL= subdir-x86_64-softmmuto avoid incompatibility with moderniasl. - Library path:
launch.shandattach.shsetLD_LIBRARY_PATHto$(conda info --base)/lib, providinglibtinfow.so.6and other runtime libraries from the active conda environment.libslirp.so.0is used directly from the system (ldconfig). CVE-2020-8608 keeps two special locally-built libslirp variants (libslirp.so.0.1.0debug-patched andlibslirp-asan.so.0.1.0ASAN-instrumented) that are not available from any package.