VirtualBox 7.2.20 leftovers: SAS overlay, hostile sav, OVF, copy-from
Six unpublished leftovers on Oracle VM VirtualBox OSS 7.2.20 after the Sep 2026 CPU. The prize is guest-to-host into the VM process: SAS1068 MPT CONFIG SCSI-SPI pages overlay SasPages host pointers. Five High parsers and confused-deputies sit next to it. Source-harness labs. No CVE yet.
- Name
- VirtualBox 7.2.20 leftovers: SAS overlay, hostile sav, OVF, copy-from
- Type
- 0-day analysis
- CVE
- n/a
- CVE Risk
- high
- Disclosure Status
- public
- Vendor
- Oracle
- Affected
- Oracle VM VirtualBox OSS 7.2.20 (f2792cf44f663989818507e36d3db5f978523c5e, svn r175154). Pin is after the Sep 2026 CPU (7.2.18, CVE-2026-87267..87285).
- Published
- 16 Oct 2026
- Updated
- 19 Oct 2026
- Tags
- 0-day, virtualbox, oracle, guest-to-host, type-confusion, heap-overflow, ovf, tftp, vmdk
The SPI page that is not a SPI page
I am @abraxas_null. I wanted a guest-to-host on Oracle VM VirtualBox. The prize class is still the same one that paid for years of VMSVGA and PCnet write-ups: a guest that can read and write the hypervisor process. Full host-user still needs a second leftover in the Windows restricted token. I did not find that second leftover unpublished on this pin. I did find the first one.
On an attached SAS1068, MPT CONFIG still serves SCSI-SPI pages. Those twelve bytes sit on the same union as SAS host pointers. Port 0 READ leaks pManufacturingPage7. Port 0 WRITE plants pointer and size. Manufacturing page 7 then copies through the planted address. That is arbitrary read/write in VirtualBoxVM. No Guest Additions. No 3D. No VRDP. The guest SCSI driver does not have to be mptsas. Ring3 programming the doorbell is enough.
I labbed it as a packed-struct overlay. Witness VBOX-LSILOGIC-SAS-CONFIG-WITNESS. Pack: abraxas/vbox-lsilogic-sas-config.
Five High leftovers sat next to it: a hostile .sav that wraps HGCM cParms, Guest Control copy-from that concatenates .., OVF href that walks off the machine folder, an imported NAT TFTP prefix that libslirp treats as enabled because the string is empty rather than NULL, and a sparse VMDK FLAT extent whose basename is used absolute or joined with ../. All six source harnesses printed SUCCESS. I mailed Oracle in one thread.
Why this tree
VirtualBox is still the desktop hypervisor a lot of people actually run. The OSS tree on GitHub is GPL-3.0 / CDDL. VRDP, USB smartcard, VM encryption, NVMe, and USB webcam moved into OSS in 7.2.x. The Extension Pack stays PUEL and I left it alone.
Wiz zeroday.cloud 2026 does not list VirtualBox. This is Oracle VRP, CPU cadence, not a container bounty.
I pinned tag v7.2.20, peel f2792cf44f663989818507e36d3db5f978523c5e, svn r175154, Version.kmk 7.2.20, released 2026-09-22. The Sep 2026 CPU (CVE-2026-87267 through 87285) landed in 7.2.18. Hunting trunk would have mixed nba::yoh e325e3bf (2026-10-13) into a pin that does not have it.
The public changelog line for 7.2.20 is VERR_SUP_VP_FOUND_EXEC_MEMORY on Windows hosts. The interesting work was already in 7.2.18. I wanted leftovers of those patches, not a re-file.
What I skipped
PCnet CVE-2026-77178 is closed: loopback leaves four bytes for FCS, network RX drops giants. Shared folders now lexical-abs then RTPathStartsWith. Clipboard and DnD reject RTPATH_PROP_DOTDOT_REFS. VMSVGA DX11 view-UAF (d691aa68) and the LsiLogic MptReplyUnion error-stack leak (122c6880) are on the pin. Exodus Zombie COTables / OffensiveCon MOB-lifetime is the skip-list class. Live HGCM caps cParms at 32 (CVE-2013-5892 live sink). VRDP CVE-2026-87277 is DoS. USB-over-VRDP IN copies are length-capped. SCSI MMC GET CONFIGURATION, VUSBSniffer, VirtioNet empty OUT TX, HGCM save lock, graphics instruction reader: all in tree.
Windows hardening: token virtualization off (951906c), HKCU class skip (5251d7a), PatchComBugs (2c5a7bf), VBoxSDS image-path (db777a6). Project Zero IDispatch 544666386 is public. I hunted a different walk and did not keep one at 55% or better.
AHCI, NVMe, USB, audio, EFI, TPM, ACPI, HGSMI, clipboard, shared folders, live NIC heap, VRDP RCE leftover of 87277: NONE unmatched Crit/High on this pin.
The leftover that is actually guest-to-host
SAS init allocates MptConfigurationPagesSupported and fills u.SasPages with host pointers: manufacturing page 7, SAS I/O unit pages, PHY array, device list. SPI page init never runs. CONFIG dispatch still has cases for MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_PORT (0x03) and SCSI_SPI_DEVICE (0x04). Only EXTENDED is SAS-gated. The page-attribute getter is a comment:
/*uint8_t u8PageAttribute = MPT_CONFIGURATION_PAGE_ATTRIBUTE_GET(pConfigurationReq->u8PageType);*/Readonly Port 0 is still writable. The SPI Port 0 struct is packed 12 bytes. On LP64 that is exactly pManufacturingPage7 (8) plus cbManufacturingPage7 (4). CONFIG READ DMA-writes those twelve bytes to guest GPA. CONFIG WRITE copies guest bytes into the overlay. The manufacturing page 7 getter is SAS-gated, dereferences the pointer with no NULL check, and uses the (now planted) size as the copy length.
Default Windows LSI is LSI53C1030 SPI. That path uses real SpiPages. The leftover needs ControllerType=SAS1068, PCI 1000:0054, PDM LsiLogicSas. OSS, selectable, common on Linux guests.
Lab CONTROL: overlay-read of a real SPI Port 0 is dummy page data, not a heap pointer. SAS confused READ leaked the calloc address. WRITE planted a second buffer. Manufacturing page 7 READ copied the witness into the guest array.
Replay: clone vbox-lsilogic-sas-config and ./lab/run.sh.
SUCCESS vbox-lsilogic-sas-config leak=yes plant=yes arb-read=yes VBOX-LSILOGIC-SAS-CONFIG-WITNESSCWE-843 / CWE-822. 8.8 CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H.
What an attacker can do (1)
Guest ring3 on SAS1068 issues MPT CONFIG SCSI-SPI Port 0 READ. The VM process DMA-writes a host heap pointer and size into guest RAM. That is ASLR input.
The same page WRITE plants pointer and size. CONFIG READ of manufacturing page 7 copies host bytes from the planted address into guest GPA. CONFIG WRITE copies guest bytes there. Arbitrary read/write in VirtualBoxVM / VBoxHeadless.
A wild pointer crashes the VM process. Crash without a chosen address is not the leftover. The arb R/W walk is.
Closed: host kernel, other VMs, VBoxSVC, host files, Windows restricted token, default SPI 53C1030, SAS EXTENDED pages on a SPI controller (VERR_NOT_FOUND). 122c6880 closed a different LsiLogic leak. Pairing this with VMSVGA DX11 UAF is closed on this pin. Full host-user still needs a new hardening leftover. IDispatch is public.
Hostile saved state
Live vmmdevR3HgcmCallAlloc still rejects cParms > 32. SSM load does not. vmmdevR3HgcmLoadState reads cParms from the sav and calls the allocator whose size is uint32_t:
const uint32_t cbCmd = sizeof(VBOXHGCMCMD)
+ cParms * (sizeof(VBOXHGCMGUESTPARM) + sizeof(VBOXHGCMSVCPARM));cParms = 0x4000000 makes the 64-bit product wrap. cbCmd becomes sizeof(VBOXHGCMCMD) (about 120). RTMemAllocZ of that size succeeds against the heap budget. The load loop writes paGuestParms[i] for i < cParms. The first store is already at or past the block.
A guest cannot plant this through a legitimate Save. A host user who restores a snapshot or opens a .sav can. Same class as a QEMU migration smash. Leftover of CVE-2013-5892, which closed the live entry and did not apply the cap to SSM.
Lab: cParms=2 keeps a 0xA5 canary. Wrap smashes it on i=0. Live path rejects 33.
Pack: abraxas/vbox-hgcm-ssm-cparms.
SUCCESS vbox-hgcm-ssm-cparms wrap=yes oob=yes live-cap=32 canary-smashed=yes VBOX-HGCM-SSM-CPARMS-WITNESSCWE-190 / CWE-787. 7.8 CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H.
What an attacker can do (2)
Host user restores a hostile .sav. VBoxVM heap receives structured HGCM parm records off a 120-byte block. Integrity is a write primitive in the already-host-user VM process. Availability is a crash if the smash is sloppy. Recon: none. Closed: host kernel, other VMs, VBoxSVC, live guest HGCM, Windows hardening token. This is not guest-to-host.
Copy-from that believes the guest
Guest Control is host-to-guest by design. VBoxManage guestcontrol copyfrom of a directory is the reverse confused-deputy. The guest answers HOST_MSG_DIR_READ with names. AddDirFromGuest skips exact . and .. and keeps ../oracle/secret. CopyFrom::Run concatenates dest + sep + name, then GuestPath::Translate. Translate changes slash style and collapses double slashes. It does not reject ... The comment even says it does not perform file system lookups.
BuildDestinationPath documents "Dotdot directories are forbidden for security reasons" and rejects RTPATH_PROP_DOTDOT_REFS. That call is on the dest root the host already chose. Per-entry names never go through it.
Lab: dest /work/vbox-copy, guest name ../oracle/VBOX-GUESTCTRL-COPYFROM-DOTDOT-WITNESS. Translate kept ... The witness file landed outside dest. The dest-root check on /work/dst-root/../outside failed closed. Same check on the per-entry join would have failed closed too. CopyFrom does not call it.
Needs Guest Additions (or a replacement HGCM client) plus a host-initiated directory copy-from. Single-file copy dest is fully host-specified. Copy-to-guest dest is on the guest.
SUCCESS vbox-guestctrl-copyfrom-dotdot skip-dot=yes translate-keeps-dotdot=yes escaped-write=yes dest-root-check-unused-per-entry=yes VBOX-GUESTCTRL-COPYFROM-DOTDOT-WITNESSCWE-22. 8.2 CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N.
What an attacker can do (3)
Hostile guest answering a host copy-from writes files as the VBox UID outside the dest the host named. Content is guest-chosen. Typical integrity hit is ~/.ssh/authorized_keys if that UID can write it. Availability is overwrite. Recon: none beyond the dest. Closed: host kernel, other VMs, VBoxSVC, files the UID cannot write, hardening token, single-file copy.
OVF that does not stay in the folder
Appliance::interpret() copies File/@href into the dest filename. i_ensureUniqueImageFilePath calls RTPathAbsExCxx(machineFolder, aName) with no RTPATHABS_F_STOP_AT_BASE, then inserts _%i before the suffix if the path exists. It does not basename-strip. Import then AbsEx again and CREATE_REPLACE-writes after creating parent directories.
IPRT tests already document the join: /VirtualBox/Machines + ../VirtualBox.xml walks off the base. An absolute href wins outright.
Suffix is required. No extension throws. This is a disk-image write (.vmdk / .iso / other MediumFormat), not a suffix-free authorized_keys drop. NVRAM dest is forced under the machine folder. VM name .. is mangled. Shared folders, ExtraData, and serial RawFile are sanitized on vbox:Machine. The disk dest path is not.
SUCCESS vbox-ovf-href-dest-escape relative-escape=yes unique-keeps-dotdot=yes absolute-href=yes control-in-folder=yes VBOX-OVF-HREF-DEST-ESCAPE-WITNESSCWE-22. 7.8 CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N.
What an attacker can do (4)
Host user imports a hostile OVF/OVA. VBoxSVC writes attacker disk-image bytes outside the new VM folder, as the importing UID, and will overwrite a colliding suffix. OVA is clean: tar member supplies bytes, href supplies dest. Closed: suffix-free host dots, NVRAM dest, guest-to-host, host kernel. No VM start required.
The TFTP prefix import forgot to strip
Shared folders from an OVA were already called a security hazard and this pin clears them. NAT enable-tftp and TFTP prefix ride along in vbox:Machine XML and are never sanitized. New VMs write EnableTFTP false. An imported appliance can start with TFTP on and prefix / or empty.
7.2 switched NAT to bundled libslirp 4.9.3. libslirp disables TFTP only when tftp_prefix is NULL. DrvNAT g_strdup("") so the empty string stays a live pointer. tftp_handle_rrq builds prefix + / + guest name. ../ in the guest filename is still rejected (CVE-2022-21394 class). An empty or / prefix is not a traversal inside the prefix. It is the prefix.
Lab: sanitizer bodies contain no TFTP fields. NULL prefix opens nothing. Empty prefix RRQ of oracle/WITNESS opened /oracle/WITNESS. Path-only "" + "/" + "etc/passwd" builds /etc/passwd (not opened). Guest ../ rejected. Intended PXE prefix /work/tftp stayed confined.
SUCCESS vbox-ovf-nat-tftp-prefix sanitize-miss=yes null-disable=yes empty-prefix-read=yes slash-prefix-read=yes dotdot-reject=yes VBOX-OVF-NAT-TFTP-PREFIX-WITNESSCWE-22. 7.7 CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N.
What an attacker can do (5)
Attacker ships a vbox:Machine OVA, user imports and starts it, guest TFTP-reads any file the VBox UID can open: home keys, VirtualBox.xml, /etc/passwd. Integrity on TFTP is closed (RRQ-only). Closed: default new VMs, NAT Network <VirtualBox home>/TFTP, generic OVF without vbox:Machine, host kernel, other VMs.
Same slirp leftover if a host turns EnableTFTP on and leaves the prefix empty. That is configuration. The unmatched walk I kept is the imported appliance.
The sparse VMDK that is not streamOptimized
Usual VirtualBox export is streamOptimized: one file, VMDK magic, grains inside. Import still accepts the older sparse specifier. After vmdkParseDescriptor, each extent with pszBasename starting with / is used as-is. Otherwise RTPathJoinA(descriptor dir, pszBasename). That join balances slashes. It does not collapse ... FLAT and HOSTED_SPARSE then vmdkFileOpen the full path and copy those bytes into the converted disk.
Lab CONTROL: disk-flat.vmdk joined under /work/import/disk and dest was dummy payload, not the oracle. RELATIVE ../../oracle/VBOX-VMDK-SPARSE-EXTENT-WITNESS stayed uncollapsed and copied the witness into /work/import/out.vmdk. ABSOLUTE /work/oracle/... skipped the join. streamOptimized control kept VMDK magic, named the oracle in the embedded ASCII, and never opened it.
SUCCESS vbox-vmdk-sparse-extent relative-escape=yes absolute-extent=yes control-in-folder=yes VBOX-VMDK-SPARSE-EXTENT-WITNESSCWE-22. 7.7 CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N.
What an attacker can do (6)
Host user imports a hostile OVF/OVA whose disk is a sparse descriptor plus FLAT extent. Import opens the named host path as the VBox UID and copies those bytes into the new disk. Guest Start then reads the volume. Typical recon: home keys, VirtualBox.xml, /etc/passwd. Integrity on this walk is closed (read of the extent). Closed: streamOptimized VMDK, Windows drive-letter absolute (the slash check is a documented hack), host kernel, other VMs. Suffix-free host writes are leftover 4, not this.
Labs
All six are source harnesses. Packed structs and path joins matching the pin. No nested VM, no vboxdrv, no payloads. Oracle is a witness string. Replay is cd lab && ./run.sh on loopback from each GitHub pack. Root *-Abraxas-Labs.py is a chdir wrapper. AGPLv3.
Wrong turns, in order: treating default Windows LSI53C1030 as the SAS overlay (it is not); treating the HGCM wrap as live guest-to-host (live cap is 32); treating Translate as the DOTDOT reject (BuildDestinationPath is, and it is dest-root only); assuming i_ensureUniqueImageFilePath basename-strips (it inserts _%i and keeps ..); assuming libslirp TFTP-off is the empty string (it is NULL); assuming every VMDK import is streamOptimized (sparse still opens pszBasename).
Chains I did not keep
The silent 2026 chain was LsiLogic leak plus VMSVGA write. Both of those links are patched on 7.2.20. This SAS overlay is a new LsiLogic sink. I do not pair it with a remaining unpublished 3D write. VM-process arb R/W is already guest-to-host into VirtualBoxVM.
HGCM wrap plus public IDispatch is every hardening link public. Guest Control copy-from is a single surface, not a shared-folder chain. Shared-folder TOCTOU (P0 1037) is closed.
The sandbox escape people mean - guest to host user to host kernel - still wants a new unpublished hardening leftover. I did not find one. The SAS overlay is the first link. That is already the class Oracle rates as Core local S:C.
Packs
- vbox-lsilogic-sas-config - Local (guest) type confusion / arb R/W
- vbox-hgcm-ssm-cparms - Local (host) heap overflow
- vbox-guestctrl-copyfrom-dotdot - Local (guest) file write
- vbox-ovf-href-dest-escape - Local (host) file write
- vbox-ovf-nat-tftp-prefix - Local (guest) file read
- vbox-vmdk-sparse-extent - Local (host) file read
No CVE numbers yet. Pin VirtualBox v7.2.20.
Contact: github.com/abraxas · x.com/abraxas_null (@abraxas_null) · abraxaslabs.tech · abraxas.null@proton.me