
Oracle VM VirtualBox 7.2.20 — высокий уровень — локальная — чтение файлов — объединение путей разреженного FLAT-экстента VMDK
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-экстент, открывает путь хоста, указанный в этом экстенте, и копирует эти байты в импортируемый диск. После vmdkParseDescriptor каждый экстент, у которого pszBasename начинается с RTPATH_SLASH, используется как есть; в противном случае применяется RTPathJoinA(descriptor dir, pszBasename), который следует по ../. Затем FLAT и HOSTED_SPARSE вызывают vmdkFileOpen для этого полного пути. streamOptimized (обычный экспорт VirtualBox, магия VMDK) — это один файл, и он не проходит этот путь. Импорт по-прежнему принимает спецификатор разрежённого диска.
| ID | CVE пока нет |
| Класс | Чтение файлов (путь FLAT/разрежённого экстента разрежённого VMDK) |
| Достижимость | Локальная (хост) 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-экстентом, чьё имя в кавычках — ../../somewhere/secret или /path/secret. Оператор, импортирующий его, открывает этот путь хоста от имени UID, выполняющего импорт, и копирует байты в целевой диск. streamOptimized (магия VMDK 0x564D444B) не обращается к встроенному имени FLAT. Это чтение файлов, которые может открыть UID VBox, а не запись 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) |
| Разведка | streamOptimized с магией VMDK. vmdkDescriptorRead вызывает vmdkDescriptorReadSparse. Встроенный ASCII FLAT указывал на oracle. | Встроенное имя не было открыто. Dest остался потоковым файлом (STREAMOPTIMIZED-SELF-NOT-ORACLE). | Доказано в лаборатории (streamoptimized-consults-extent=no) |
| Разведка | Запуск гостя, затем чтение сконвертированного тома. | Гость видит содержимое хост-файла, читаемого под UID импортёра, которое попало в dest. | Предположительно (то же копирование; лаборатория не загружает гостя) |
| Доступность | Импорт в уже существующий dest или последующее подключение сконвертированного диска. | Перезапись доступности dest-диска. Содержимое становится открытым хост-файлом. | Предположительно (та же запись в dest) |
| Закрыто | streamOptimized VMDK (обычный экспорт VirtualBox) | Ветка магии. Один файл. Встроенное имя FLAT не открывается. | Закрыто |
| Закрыто | Абсолютный путь с буквой диска Windows (C:\...) | Может не сработать проверка pszBasename[0] == RTPATH_SLASH. В коде проверка абсолютного пути названа хаком (@todo implement proper path absolute check). | Закрыто |
| Закрыто | Записи без суффикса (authorized_keys, crontab) | Не является остатком записи. Открытие extent — это чтение указанного хост-пути. | Закрыто |
| Закрыто | Ядро хоста, гость-в-хост, другие ВМ, память VBoxSVC | Не этот остаток. Для копирования не требуется запуск ВМ. Тот же UID, что у импортёра. | Закрыто |
OVA — это тот же обход, как только разреженный дескриптор оказывается на диске: элемент tar предоставляет дескриптор плюс (необязательный) FLAT внутри папки; basename extent обеспечивает экранированное открытие. Без 3D, без Guest Additions, без VRDP, не PUEL.
Я остановился на теге v7.2.20 (f2792cf44) после CPU за сентябрь 2026 и изучил импорт VMDK. ExtraData, общие папки, dest NVRAM и последовательный RawFile уже вырезаны. Dest ovf:href — это соседний остаток. Обычный экспорт VirtualBox — streamOptimized. interpret() по-прежнему принимает разреженный спецификатор (g_pszVMDKSparseURI). После vmdkParseDescriptor basename extent присоединяется без свёртки ../ и без отклонения абсолютных путей:
/* 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. Sparse magic VMDK (0x564D444B) передаётся в vmdkDescriptorReadSparse и сохраняет уже открытый файл как extent. ASCII передаётся в vmdkDescriptorReadAscii и объединение выше.
Уже зафиксированные неверные пути: трактовка streamOptimized как этого остатка (magic не обращается к встроенному имени FLAT); трактовка абсолютного пути с буквой диска Windows как проверки слэша Unix; трактовка перезаписи dest-disk как записи authorized_keys без суффикса; загрузка VM, когда достаточно исходного harness объединения. Театр: хост-ядро, гость-к-хосту. Оракул — строка-свидетель вне папки дескриптора плюс контрольный disk-flat.vmdk, который остался на месте.
python3 vbox-vmdk-sparse-extent-Abraxas-Labs.py
или
cd lab
./run.sh