
Oracle VM VirtualBox 7.2.20 - Alta - Local - Lectura de archivos - Unión de rutas en extensión FLAT dispersa de VMDK
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · vbox-vmdk-sparse-extent
Clase: Lectura de archivos Alcance: Local (host) UI:R
Oracle VM VirtualBox OSS 7.2.20 (f2792cf44) - Oracle
Un usuario del host que importa un directorio OVF o un OVA cuyo disco es un descriptor VMDK disperso más una extensión FLAT abre una ruta del host nombrada en esa extensión y copia esos bytes en el disco importado. Tras vmdkParseDescriptor, cada extensión cuyo pszBasename comienza con RTPATH_SLASH se usa tal cual; en caso contrario, RTPathJoinA(descriptor dir, pszBasename), que sigue ../. FLAT y HOSTED_SPARSE luego hacen vmdkFileOpen sobre esa ruta completa. streamOptimized (la exportación habitual de VirtualBox, con la firma mágica VMDK) es un único archivo y no realiza este recorrido. La importación sigue aceptando el especificador disperso.
| ID | aún sin CVE |
| Clase | Lectura de archivos (ruta de extensión FLAT/dispersa de VMDK disperso) |
| Alcance | Local (host) UI:R (la víctima importa un appliance hostil) |
| CWE | CWE-22 |
| CVSS | Alto: 7.7 CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N |
| Producto | Oracle VM VirtualBox OSS |
| Afectado | 7.2.20 (f2792cf44f663989818507e36d3db5f978523c5e) |
| Autenticación | ninguna en VirtualBox. El usuario del host abre Archivo/Importar o VBoxManage import. |
| Licencia | GNU Affero GPL v3.0 |
| Laboratorio | solo 127.0.0.1. Arnés de código fuente. VirtualBox no se arranca. |
Entregar un directorio OVF o un OVA. Colocar un descriptor VMDK disperso (# Disk DescriptorFile, createType="monolithicFlat") junto a una extensión FLAT cuyo nombre de archivo entre comillas sea ../../somewhere/secret o /path/secret. El operador que lo importa abre esa ruta del host con el UID que realiza la importación y copia los bytes en el disco de destino. streamOptimized (firma mágica VMDK 0x564D444B) no consulta el nombre FLAT incrustado. Se trata de una lectura de archivos que el UID de VBox puede abrir, no de una escritura sin sufijo en authorized_keys.
| Privilegio | Método + sink | Qué otorga | Procedencia |
|---|---|---|---|
| Recon | Importación de un VMDK disperso ASCII. Tras vmdkParseDescriptor, RTPathJoinA(descriptor dir, pszBasename) sin colapso de ../ (VMDK.cpp 3472-3503). vmdkFileOpen FLAT de esa ruta completa. La importación copia esos bytes en dest. | Bytes de archivo del host nombrados mediante ../ acaban en el disco importado. En el laboratorio, pszBasename ../../oracle/VBOX-VMDK-SPARSE-EXTENT-WITNESS se unió a /work/import/disk/../../oracle/... y se copió en /work/import/out.vmdk. | Probado en laboratorio (relative-escape=yes) |
| Recon | Mismo recorrido. pszBasename[0] == RTPATH_SLASH se usa tal cual (VMDK.cpp 3477-3485). | Ruta absoluta del host abierta y copiada. Laboratorio /work/oracle/VBOX-VMDK-SPARSE-EXTENT-WITNESS. | Probado en laboratorio (absolute-extent=yes) |
| Recon | Control FLAT disk-flat.vmdk unido bajo el directorio del descriptor. | Permanece dentro de la carpeta. Nombre completo de laboratorio /work/import/disk/disk-flat.vmdk. Bytes CONTROL-FLAT-NOT-ORACLE. | Probado en laboratorio (control-in-folder=yes) |
| Recon | streamOptimized con magia VMDK. vmdkDescriptorRead toma vmdkDescriptorReadSparse. FLAT ASCII incrustado nombraba el oracle. | No abrió el nombre incrustado. Dest permaneció como el archivo stream (STREAMOPTIMIZED-SELF-NOT-ORACLE). | Probado en laboratorio (streamoptimized-consults-extent=no) |
| Recon | Inicio del invitado y luego lectura del volumen convertido. | El invitado ve contenidos de archivos del host legibles por el UID del importador que acabaron en dest. | Inferido (misma copia; el laboratorio no arranca un invitado) |
| Disponibilidad | Dest de importación que ya existe, o un attach posterior del disco convertido. | Sobrescritura de la disponibilidad del disco dest. Los contenidos pasan a ser el archivo del host abierto. | Inferido (misma escritura de dest) |
| Cerrado | VMDK streamOptimized (exportación habitual de VirtualBox) | Rama de magia. Archivo único. El nombre FLAT incrustado no se abre. | Cerrado |
| Cerrado | Letra de unidad absoluta de Windows (C:\...) | Puede no detectar pszBasename[0] == RTPATH_SLASH. El código llama a la comprobación absoluta un hack (@todo implement proper path absolute check). | Cerrado |
| Cerrado | Escrituras sin sufijo (authorized_keys, crontab) | No es un residuo de escritura. La apertura del extent es una lectura de la ruta del host nombrada. | Cerrado |
| Cerrado | Kernel del host, invitado-a-host, otras VMs, memoria de VBoxSVC | No es este residuo. No se requiere arrancar una VM para la copia. Mismo UID que el importador. | Cerrado |
OVA es el mismo recorrido una vez que el descriptor disperso está en disco: el miembro del tar proporciona el descriptor más (opcionalmente) el FLAT dentro de la carpeta; el basename del extent proporciona la apertura escapada. Sin 3D, sin Guest Additions, sin VRDP, no es PUEL.
Me quedé en la etiqueta v7.2.20 (f2792cf44) tras la CPU de septiembre de 2026 y leí la importación de VMDK. ExtraData, carpetas compartidas, dest de NVRAM y RawFile serie ya están eliminados. El dest de ovf:href es un residuo hermano. La exportación habitual de VirtualBox es streamOptimized. interpret() sigue aceptando el especificador disperso (g_pszVMDKSparseURI). Tras vmdkParseDescriptor, el basename del extent se une sin colapsar ../ ni rechazar rutas absolutas:
/* Hack to figure out whether the specified name in the
* extent descriptor is absolute. Doesn't always work, but
* should be good enough for now. */
char *pszFullname;
/** @todo implement proper path absolute check. */
if (pExtent->pszBasename[0] == RTPATH_SLASH)
{
pszFullname = RTStrDup(pExtent->pszBasename);
/* ... */
}
else
{
char *pszDirname = RTStrDup(pImage->pszFilename);
RTPathStripFilename(pszDirname);
pszFullname = RTPathJoinA(pszDirname, pExtent->pszBasename);
/* ... */
}
pExtent->pszFullname = pszFullname;
RTPathJoinA solo equilibra las barras. No colapsa ... FLAT y HOSTED_SPARSE luego abren esa cadena:
case VMDKETYPE_FLAT:
rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszBasename, pExtent->pszFullname,
VDOpenFlagsToFileOpenFlags(uOpenFlags, false /* fCreate */));
vmdkDescriptorRead divide por magic. El magic de VMDK sparse (0x564D444B) toma vmdkDescriptorReadSparse y mantiene el archivo ya abierto como la extensión. ASCII toma vmdkDescriptorReadAscii y el join anterior.