abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · vbox-vmdk-sparse-extent
类别: 文件读取 触达范围: 本地(主机)UI:R
Oracle VM VirtualBox OSS 7.2.20 (f2792cf44) - Oracle
当主机用户导入一个 OVF 目录或 OVA,且其磁盘由一个稀疏 VMDK 描述符加一个 FLAT extent 组成时,程序会打开该 extent 中指定的主机路径,并将这些字节复制到导入的磁盘中。在 vmdkParseDescriptor 之后,每个 pszBasename 以 RTPATH_SLASH 开头的 extent 会被直接使用;否则会调用 RTPathJoinA(descriptor dir, pszBasename),该调用会跟随 ../。随后 FLAT 和 HOSTED_SPARSE 会通过 vmdkFileOpen 打开该完整路径。streamOptimized(VirtualBox 常规导出格式,VMDK 魔数)是单个文件,不会走这条路径。导入过程仍然接受稀疏说明符。
| ID | 暂无 CVE |
| 类别 | 文件读取(稀疏 VMDK FLAT/稀疏 extent 路径) |
| 触达范围 | 本地(主机) UI:R(受害者导入恶意设备) |
| CWE | CWE-22 |
| CVSS | 高危:7.7 CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N |
| 产品 | Oracle VM VirtualBox OSS |
| 受影响版本 | 7.2.20 (f2792cf44f663989818507e36d3db5f978523c5e) |
| 认证 | VirtualBox 上无需认证。主机用户打开 File/Import 或执行 VBoxManage import。 |
| 许可证 | GNU Affero GPL v3.0 |
| 实验环境 | 仅 127.0.0.1。源码测试框架。VirtualBox 未启动。 |
提供一个 OVF 目录或 OVA。将一个稀疏 VMDK 描述符(# Disk DescriptorFile、createType="monolithicFlat")与一个 FLAT extent 放在一起,该 extent 的带引号基名为 ../../somewhere/secret 或 /path/secret。导入该设备的操作者会以导入用户的 UID 打开该主机路径,并将字节复制到目标磁盘中。streamOptimized(VMDK 魔数 0x564D444B)不会参考内嵌的 FLAT 名称。这是对 VBox UID 可打开文件的读取,而不是对 authorized_keys 的无后缀写入。
| 权限 | 方法 + 汇聚点 | 授予的能力 | 来源 |
|---|---|---|---|
| 侦察 | 导入一个 ASCII 稀疏 VMDK。在 vmdkParseDescriptor 之后,执行 RTPathJoinA(descriptor dir, pszBasename),且不折叠 ../(VMDK.cpp 3472-3503)。对该完整路径执行 FLAT vmdkFileOpen。导入会将这些字节复制到 dest。 | 由 ../ 指定的主机文件字节落入导入的磁盘中。实验中将 pszBasename 设为 ../../oracle/VBOX-VMDK-SPARSE-EXTENT-WITNESS,与 /work/import/disk/../../oracle/... 拼接,并复制到 /work/import/out.vmdk。 | 实验证实(relative-escape=yes) |
| 侦察 | 同样的路径遍历。pszBasename[0] == RTPATH_SLASH 被原样使用(VMDK.cpp 3477-3485)。 | 打开并复制绝对主机路径。实验中为 /work/oracle/VBOX-VMDK-SPARSE-EXTENT-WITNESS。 | 实验证实(absolute-extent=yes) |
| 侦察 | 对照 FLAT disk-flat.vmdk 拼接在描述符目录下。 | 保持在文件夹内。实验中的完整路径为 /work/import/disk/disk-flat.vmdk。字节为 CONTROL-FLAT-NOT-ORACLE。 | 实验证实(control-in-folder=yes) |
| 侦察 | 带 VMDK magic 的 streamOptimized。vmdkDescriptorRead 调用 vmdkDescriptorReadSparse。内嵌的 ASCII FLAT 指定了 oracle。 | 未打开内嵌名称。dest 仍为流文件(STREAMOPTIMIZED-SELF-NOT-ORACLE)。 | 实验证实(streamoptimized-consults-extent=no) |
| 侦察 | 客户机启动,然后读取转换后的卷。 | 客户机看到导入器 UID 可读的主机文件内容,这些内容已落入 dest。 | 推断(同一复制过程;实验未启动客户机) |
| 可用性 | 导入的 dest 已存在,或之后挂载转换后的磁盘。 | 覆盖 dest 磁盘的可用性。内容变为被打开的主机文件。 | 推断(同一 dest 写入) |
| 已排除 | streamOptimized VMDK(VirtualBox 常规导出) | Magic 分支。单文件。内嵌 FLAT 名称不会被打开。 | 已排除 |
| 已排除 | Windows 盘符绝对路径(C:\...) | 可能漏掉 pszBasename[0] == RTPATH_SLASH。代码将绝对路径检查称为 hack(@todo implement proper path absolute check)。 | 已排除 |
| 已排除 | 无后缀写入(authorized_keys、crontab) | 不是写入残留。extent 打开是对指定主机路径的读取。 | 已排除 |
| 已排除 | 主机内核、客户机到主机、其他虚拟机、VBoxSVC 内存 | 不是此残留。复制不需要启动虚拟机。与导入器相同的 UID。 | 已排除 |
一旦稀疏描述符位于磁盘上,OVA 就是同样的路径遍历:tar 成员提供描述符以及(可选的)文件夹内 FLAT;extent 的 basename 提供逃逸的打开操作。不涉及 3D、不涉及 Guest Additions、不涉及 VRDP、不涉及 PUEL。
在 2026 年 9 月的 CPU 之后,我停留在标签 v7.2.20(f2792cf44)上,并阅读了 VMDK 导入相关代码。ExtraData、共享文件夹、NVRAM dest 和串口 RawFile 都已被剥离。ovf:href dest 是同级残留。VirtualBox 常规导出是 streamOptimized。interpret() 仍然接受稀疏说明符(g_pszVMDKSparseURI)。在 vmdkParseDescriptor 之后,extent 的 basename 被拼接,既不折叠 ../,也不拒绝绝对路径:
/* 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 仅平衡斜杠。它不会折叠 ..。随后 FLAT 和 HOSTED_SPARSE 会打开该字符串:
case VMDKETYPE_FLAT:
rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszBasename, pExtent->pszFullname,
VDOpenFlagsToFileOpenFlags(uOpenFlags, false /* fCreate */));
vmdkDescriptorRead 按 magic 进行分支。VMDK 稀疏 magic(0x564D444B)走 vmdkDescriptorReadSparse,并将已打开的文件保留为 extent。ASCII 走 vmdkDescriptorReadAscii 以及上面的 join。
已记录的弯路:将 streamOptimized 当作这种残留情况处理(magic 不会查询内嵌的 FLAT 名称);将 Windows 盘符绝对路径当作 Unix 斜杠检查;将 dest-disk 覆盖当作无后缀的 authorized_keys 写入;在 join 的源 harness 已足够时启动 VM。表演性:宿主内核、guest 到 host。判定依据是描述符文件夹之外的一条见证行,外加一个保持原位的对照 disk-flat.vmdk。
python3 vbox-vmdk-sparse-extent-Abraxas-Labs.py
或
cd lab
./run.sh
vmdkParseDescriptor extent join、vmdkFileOpen 以及 streamOptimized magic 分支的源 harness。Docker compose,回环地址 127.0.0.1:19250,无 VirtualBox 二进制文件。描述符目录 /work/import/disk。Oracle /work/oracle。目标 /work/import/out.vmdk。
SUCCESS vbox-vmdk-sparse-extent relative-escape=yes absolute-extent=yes control-in-folder=yes VBOX-VMDK-SPARSE-EXTENT-WITNESS
在 pszBasename 中折叠或拒绝 ..。拒绝绝对路径的 pszBasename。或者仅在描述符目录下打开 extent(RTPATHABS_F_STOP_AT_BASE / realpath-under-base)。不要将 pszBasename[0] == RTPATH_SLASH 视为完整的绝对路径检查。
f2792cf44f663989818507e36d3db5f978523c5e)VMDK.cpp vmdkDescriptorReadSparse 3312,vmdkDescriptorReadAscii extent 拼接 3472-3547,vmdkDescriptorRead 3576-3589ApplianceImpl.cpp g_pszVMDKSparseURI 61, 78ApplianceImplImport.cpp i_importOneDiskImage 4343-4408GNU Affero GPL v3.0。参见 LICENSE。仅限回环实验环境。不提供任何担保。