
VFS для Git ab95cf51 — высокий — локальный — остаточные привилегии — пользователи монтируют pipe dehydrate/unmount
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · vfs-mount-dehydrate
Класс: Остаточные привилегии Доступ: Локальный
VFS for Git master ab95cf51 (тег v2.0.26259.1) - Microsoft
CreatePipeByName на канале монтирования для каждого enlistment предоставляет BUILTIN\Users права ReadWrite | CreateNewInstance. Проверка SID владельца и ImpersonateNamedPipeClient отсутствуют. HandleDehydrateFolders объединяет имена папок, контролируемые атакующим, через Path.Combine и передаёт их git через ProcessStartInfo.Arguments. Путь dehydrate в CLI выполняет IsFolderValid. Обработчик монтирования — нет.
Любой пользователь из группы Users, способный угадать \\.\pipe\GVFS_<ENLISTMENT>, может размонтировать ProjFS, перемещать каталоги от имени пользователя монтирования и принудительно выполнить git reset HEAD --hard.
| ID | CVE пока нет |
| Класс | Остаточные привилегии (ACL именованного канала) / обход пути / инъекция аргументов |
| Доступ | Локальный |
| CWE | CWE-284, CWE-22, CWE-88 |
| CVSS | Высокий: 7.8 CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H |
| Продукт | VFS for Git |
| Затронуто | master ab95cf51d6ffd0ee0e2feae7180078fe9ecbbb3a; теги установщика v2.0.26229.1 / v2.0.26259.1. Только Windows. |
| Аутентификация | Локальная. Любой субъект BUILTIN\Users, пока у жертвы есть монтирование |
| Лицензия | GNU Affero GPL v3.0 |
| Лаборатория | Только 127.0.0.1. how=gadget (без Windows VM / живого ProjFS). |
Протокол канала: UTF-8 Header или Header|body, завершается байтом 0x03. Имя канала — это GVFS_ + корень enlistment в верхнем регистре с заменой : на _ (пример C:\Repos\Foo -> \\.\pipe\GVFS_C_\REPOS\FOO).
У жертвы выполнен gvfs mount в известном корне. Атакующий — другой субъект Users на той же машине. Целостность и доступность на уровне жертвы. Не SYSTEM. Не оболочка сама по себе.
| Привилегия | Метод / точка срабатывания | Что даёт | Разведка / целостность / доступность | Доказано в лаборатории или выведено |
|---|---|---|---|---|
| Подключение к каналу монтирования жертвы | ACL CreatePipeByName BuiltinUsers ReadWrite | CreateNewInstance; нет проверки владельца | Обмен данными по IPC монтирования | Разведка канала | Доказано в лаборатории (ACL гаджета). SID атакующего принимается; режим только для владельца возвращает отказ в подключении |
| Чтение пути enlistment, пути кэша, RepoUrl | GetStatus | Утечка корня, используемого для остатков #1 / #4 | Разведка | Доказано в лаборатории. JSON статуса содержал RepoUrl и GVFS_C_\REPOS\FOO |
Размонтирование ProjFS и завершение gvfs.mount | сообщение Unmount | Вывод enlistment из строя | Доступность | Доказано в лаборатории. Отвечает Ack, затем Complete; состояние монтирования Unmounted |
| Размонтирование при ошибке разбора | ReleaseLock с пустым телом -> RequestData == null -> UnmountAndStopWorkingDirectoryCallbacks + Environment.Exit | То же размонтирование плюс завершение процесса | Доступность | Доказано в лаборатории. Пустой ReleaseLock| размонтировал |
| Уничтожение рабочего дерева жертвы | Dehydrate|{"Folders":"--hard",...}. movedFolders.Add даже если исходный каталог отсутствует. Reset("HEAD", "\"--hard\" ") превращается в git reset HEAD --hard после удаления кавычек | Уничтожение изменённых отслеживаемых файлов | Целостность / доступность | Доказано в лаборатории. Изменённый отслеживаемый файл потерял строку-свидетель |
| Перемещение папки за пределы рабочего дерева от имени жертвы | Path.Combine(WorkingDirectoryBackingRoot, folder) + MoveDirectory в BackupFolderPath | Кража / перемещение папки рабочего дерева | Целостность | Доказано в лаборатории. keepme покинул src и появился по пути резервной копии атакующего |
Обход .. (CLI IsFolderValid не используется) | тот же Combine; ..\secret | Перемещение соседнего элемента, доступного на запись пользователю монтирования | Целостность | Доказано в лаборатории. Соседний secret оказался по пути резервной копии атакующего. Применение IsFolderValid заблокировало это |
| Кража или уничтожение других каталогов, доступных жертве на запись | то же перемещение с цепочкой ..\, разрешающейся за пределами enlistment | Более широкое перемещение от имени пользователя монтирования | Целостность | Выведено из Combine. В этой лаборатории используется только одноразовое дерево |
| Выполнение кода от имени жертвы | этот канал | Создание процесса с образом атакующего | Целостность | Закрыто. Создание процесса с образом атакующего на этом остатке отсутствует (см. vfs-service-sid и vfs-hook-impersonate) |
| SYSTEM | IPC монтирования | LocalSystem | Целостность | Закрыто. Монтирование выполняется от имени пользователя enlistment |
--hard не является путём ... CLI IsFolderValid всё равно его допускает. Исправление также должно отклонять имена папок, похожие на опции.
Остатки того же пина: vfs-quote-breakout, vfs-clone-acl, vfs-service-sid, vfs-hook-impersonate, vfs-install-cwd.
Я зафиксировал VFSForGit master ab95cf51 после слияния 2026-09-28. Канал службы и канал монтирования для каждого enlistment используют общий CreatePipeByName. Dehydrate в CLI проверяет папки. Обработчик монтирования — нет.
Уже зафиксированные ошибочные пути: трактовка --hard как обхода пути (это инъекция аргументов после удаления кавычек); заявление о SYSTEM; обратная оболочка. Театр. Нативная Windows отсутствовала, поэтому лаборатория представляет собой гаджет из этих функций плюс одноразовое git-дерево.
python3 vfs-mount-dehydrate-Abraxas-Labs.py
или
cd lab
./run.sh
Гаджет HTTP на 127.0.0.1:19640, воспроизводящий форму сообщений канала. Одноразовое git-дерево внутри контейнера. Без обратной оболочки. Без реального пути профиля. Последняя строка использует how=gadget.
SUCCESS vfs-mount-dehydrate how=gadget unmount=yes reset-hard=yes dirty-gone=yes move-inside=yes move-dotdot=yes pipe-acl=users-no-owner is-folder-valid-not-on-pipe=yes VFS-MOUNT-DEHYDRATE-WITNESS
Ограничить CreatePipeByName SID владельца enlistment плюс LocalSystem. Убрать CreateNewInstance для Users. Имперсонировать клиента и требовать владельца. Продублировать IsFolderValid в HandleDehydrateFolders и отклонять имена, похожие на опции git. Не трактовать ошибку разбора ReleaseLock как завершение процесса.