
VFS para Git ab95cf51 - Alta - Local - Privilegio residual - Los usuarios montan pipe dehydrate/unmount
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · vfs-mount-dehydrate
Clase: Privilegio residual Alcance: Local
VFS for Git master ab95cf51 (tag v2.0.26259.1) - Microsoft
CreatePipeByName en la tubería de montaje por enlistment otorga a BUILTIN\Users ReadWrite | CreateNewInstance. No hay verificación del SID del propietario ni ImpersonateNamedPipeClient. HandleDehydrateFolders aplica Path.Combine a nombres de carpeta del atacante y los pasa a git mediante ProcessStartInfo.Arguments. La ruta de dehydrate de la CLI ejecuta IsFolderValid. El manejador del montaje no.
Cualquiera en Users que pueda adivinar \\.\pipe\GVFS_<ENLISTMENT> puede desmontar ProjFS, mover directorios como el usuario del montaje y forzar git reset HEAD --hard.
| ID | aún sin CVE |
| Clase | Privilegio residual (ACL de named-pipe) / path traversal / inyección de argumentos |
| Alcance | Local |
| CWE | CWE-284, CWE-22, CWE-88 |
| CVSS | Alto: 7.8 CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H |
| Producto | VFS for Git |
| Afectado | master ab95cf51d6ffd0ee0e2feae7180078fe9ecbbb3a; tags del instalador v2.0.26229.1 / v2.0.26259.1. Solo Windows. |
| Auth | Local. Cualquier principal BUILTIN\Users mientras la víctima tiene un montaje |
| Licencia | GNU Affero GPL v3.0 |
| Lab | Solo 127.0.0.1. how=gadget (sin VM de Windows / ProjFS en vivo). |
Protocolo de la tubería: Header o Header|body en UTF-8, terminado por el byte 0x03. El nombre de la tubería es GVFS_ + la raíz del enlistment en mayúsculas con : reemplazado por _ (ejemplo C:\Repos\Foo -> \\.\pipe\GVFS_C_\REPOS\FOO).
La víctima tiene gvfs mount en una raíz conocida. El atacante es otro principal de Users en la misma máquina. Integridad y disponibilidad como la víctima. No SYSTEM. No una shell por sí solo.
| Privilegio | Método / sink | Qué otorga | Recon / integridad / disponibilidad | Probado en lab o inferido |
|---|---|---|---|---|
| Conectarse a la tubería de montaje de la víctima | ACL de CreatePipeByName BuiltinUsers ReadWrite | CreateNewInstance; sin verificación de propietario | Hablar el IPC del montaje | Recon de la tubería | Probado en lab (ACL del gadget). SID del atacante aceptado; el modo solo-propietario devuelve conexión denegada |
| Leer la ruta del enlistment, la ruta de caché, RepoUrl | GetStatus | Filtrar la raíz usada para el residual #1 / #4 | Recon | Probado en lab. El JSON de estado incluía RepoUrl y GVFS_C_\REPOS\FOO |
Desmontar ProjFS y salir de gvfs.mount | mensaje Unmount | Tumbar el enlistment | Disponibilidad | Probado en lab. Responde Ack y luego Complete; estado del montaje Unmounted |
| Desmontar ante un fallo de parseo | ReleaseLock con cuerpo vacío -> RequestData == null -> UnmountAndStopWorkingDirectoryCallbacks + Environment.Exit | El mismo desmontaje más la salida del proceso | Disponibilidad | Probado en lab. ReleaseLock| vacío desmontó |
| Descartar el árbol de trabajo de la víctima | Dehydrate|{"Folders":"--hard",...}. movedFolders.Add incluso si el directorio origen no existe. Reset("HEAD", "\"--hard\" ") se convierte en git reset HEAD --hard tras eliminar las comillas | Destruir archivos rastreados con cambios | Integridad / disponibilidad | Probado en lab. Un archivo rastreado con cambios perdió la cadena testigo |
| Mover una carpeta fuera del worktree como la víctima | Path.Combine(WorkingDirectoryBackingRoot, folder) + MoveDirectory a BackupFolderPath | Robar / reubicar una carpeta del worktree | Integridad | Probado en lab. keepme salió del src y apareció bajo la ruta de backup del atacante |
Recorrer .. (la CLI IsFolderValid no se usa) | el mismo Combine; ..\secret | Mover un hermano sobre el que el usuario del montaje puede escribir | Integridad | Probado en lab. El hermano secret acabó bajo la ruta de backup del atacante. Aplicar IsFolderValid lo bloqueó |
| Robar o destruir otros directorios escribibles por la víctima | el mismo move con una cadena ..\ que resuelve fuera del enlistment | Movimiento más amplio como el usuario del montaje | Integridad | Inferido a partir de Combine. Este lab usa solo un árbol desechable |
| Ejecución de código como la víctima | esta tubería | Creación de proceso de una imagen del atacante | Integridad | Cerrado. Sin creación de proceso de una imagen del atacante en este residual (ver vfs-service-sid y vfs-hook-impersonate) |
| SYSTEM | IPC del montaje | LocalSystem | Integridad | Cerrado. El montaje se ejecuta como el usuario del enlistment |
--hard no es una ruta ... La CLI IsFolderValid aún lo permite. Una corrección también debe rechazar nombres de carpeta que parezcan opciones.
Residuales del mismo pin: vfs-quote-breakout, vfs-clone-acl, vfs-service-sid, vfs-hook-impersonate, vfs-install-cwd.
Fijé VFSForGit master ab95cf51 tras el merge del 2026-09-28. La tubería del servicio y la tubería de montaje por enlistment comparten CreatePipeByName. Dehydrate en la CLI valida las carpetas. El manejador del montaje no.
Caminos equivocados ya registrados: tratar --hard como un path traversal (es inyección de argumentos tras eliminar las comillas); afirmar SYSTEM; una reverse shell. Teatro. Faltaba Windows nativo, así que el lab es un gadget de esas funciones más un árbol git desechable.
python3 vfs-mount-dehydrate-Abraxas-Labs.py
o
cd lab
./run.sh
Gadget HTTP en 127.0.0.1:19640 hablando la forma de mensaje de la tubería. Árbol git desechable dentro del contenedor. Sin reverse shell. Sin ruta de perfil real. La última línea usa 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
Restringir CreatePipeByName al SID del propietario del enlistment más LocalSystem. Eliminar CreateNewInstance para Users. Suplantar al cliente y exigir propietario. Replicar IsFolderValid en HandleDehydrateFolders y rechazar nombres que parezcan opciones de git. No tratar un fallo de parseo de ReleaseLock como salida del proceso.