MOKSHA-2026-0009: QEMU Serial Host Filesystem Write via VM.platform hvm_serial

Advisory IDMOKSHA-2026-0009
Semantic IDPLAT-6
Published2026-04-24
CVSS 3.18.5 High
CVSS 3.1 VectorAV:N/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:L
CVSS 4.08.5 High
CVSS 4.0 VectorAV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:H/SC:N/SI:H/SA:H
XAPI ObjectVM
XAPI Fieldplatform:hvm_serial
Entry Rolevm-admin
ResearcherJakob Wolffhechel, Moksha

Affected Products

VendorProductVersions
Citrix / Cloud Software GroupXenServer / Citrix Hypervisorall versions (shared XAPI codebase)
VatesXCP-ng8.3.0

Summary

A user with the vm-admin role can write arbitrary host filesystem files by setting VM.platform:hvm_serial to file:<path>. XAPI reads this key from the raw database record (bypassing the filtered_flags allowlist) and passes it to QEMU as the -serial argument. QEMU creates the target file on the host filesystem even if the VM fails to fully start. This is an independent host filesystem write primitive that chains into all 34 FIST debug points in XAPI without requiring BOC-1. The file is created with root ownership because QEMU runs as root in dom0.

Vulnerability Description

VM.platform is a Map(String, String) field controlling hypervisor-level VM configuration. The hvm_serial key specifies the QEMU serial device. XAPI reads this key at xapi_xenops.ml:456 from the raw vm.API.vM_platform database value - not from the filtered platformdata that passes through sanity_check.

The filtered_flags Bypass

XAPI applies a filtered_flags allowlist to VM.platform before passing values to xenopsd. This allowlist blocks unrecognized keys from reaching the QEMU command line. However, hvm_serial is read independently from the raw database record at line 456, completely bypassing the allowlist:

1. hvm_serial is NOT in filtered_flags
2. filtered_flags prevents it from reaching platformdata
3. BUT xapi_xenops.ml:456 reads it from raw vm.API.vM_platform
4. The value is passed directly to xenopsd as the serial field in HVM info
5. xenopsd passes it verbatim to QEMU: -serial file:<path>

File Creation Without VM Boot

QEMU creates the file specified in the -serial file:<path> argument during its own initialization, before the VM domain is fully created. If domain creation fails for any reason, the file still exists. This means:

Live Verification

[root@xcpng ~]# /usr/lib64/xen/bin/qemu-system-i386 -accel xen \
    -serial "file:/tmp/test-serial-qemu-check" -M none
[root@xcpng ~]# ls -la /tmp/test-serial-qemu-check
-rw-r--r-- 1 root root 0 Feb  7 14:01 /tmp/test-serial-qemu-check

Confirmed on the live production host: QEMU creates the file as root.

Root Causes

  1. filtered_flags bypass. hvm_serial is read from the raw database record, not from the filtered platformdata. The allowlist provides zero protection for this key.

  2. Missing per-key RBAC. VM.platform has zero map_keys_roles entries. All keys are writable by vm-admin.

  3. No write-time validation. All VM.platform validation occurs at VM start time (sanity_check), not at write time. The malicious value persists in the database.

  4. QEMU file creation at init. QEMU creates the serial output file during its own initialization before checking whether the VM domain is viable. This guarantees file creation regardless of VM start success.

Affected Systems

Directly Affected

Indirectly Affected

Exploitation Scenarios

Scenario Impact Pre-conditions Status
FIST point activation Create /tmp/fist_disable_memory_checks, /tmp/fist_disable_ha, etc. - disables XAPI internal safety checks None Confirmed (live host file creation verified)
Arbitrary file creation Create files in any root-writable directory None Confirmed (QEMU runs as root in dom0)
PLAT-6 + FIST chain Disable HA fencing → split-brain vulnerability; disable memory checks → overcommitment Chains with 34 FIST debug points Modeled (live file creation confirmed, FIST effects documented)
No-boot exploitation File created even when VM start fails Deliberately misconfigured VM Confirmed (QEMU creates file during init, before domain creation)

FIST Debug Points (Partial List)

File Effect
/tmp/fist_disable_memory_checks Disables XAPI memory validation
/tmp/fist_disable_ha Disables HA fencing
/tmp/fist_pause_storage_migrate Pauses storage migration (DoS)
/tmp/fist_disable_sched_gran_affinity_check Disables CPU scheduling granularity checks

There are 34 FIST points in XAPI, each controllable via this primitive.

Detection

Remediation

Short-Term Mitigations

Long-Term Fix

Validate hvm_serial. Restrict the hvm_serial value to known safe serial device types. Reject file: prefix entirely - there is no legitimate use case for QEMU serial output to host filesystem in production.

Add map_keys_roles. Add per-key RBAC for hvm_serial requiring _R_POOL_ADMIN in datamodel.ml.

Fix the filtered_flags bypass. Read hvm_serial from filtered platformdata, not from the raw database value. This ensures the allowlist is applied consistently.

Add write-time validation. Validate platform key values when they are set, not just at VM start.

Upstream patches exist. They are held privately pending coordinated disclosure.

Disclosure

Disclosure:

References

Credits

Discovered and reported by Jakob Wolffhechel, Moksha.

Jakob Wolffhechel · Moksha · Copenhagen
jakob@wolffhechel.dk · +45 3170 7337
Published 2026-04-24 08:00 CEST · cna.moksha.dk · shittrix.moksha.dk