
VFS para Git ab95cf51 - Alta - Local - Privilégio remanescente - Usuários montam pipe dehydrate/unmount
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · vfs-mount-dehydrate
Classe: Privilégio remanescente Alcance: Local
VFS for Git master ab95cf51 (tag v2.0.26259.1) - Microsoft
CreatePipeByName no pipe de montagem por enlistment concede a BUILTIN\Users ReadWrite | CreateNewInstance. Não há verificação de SID do proprietário nem ImpersonateNamedPipeClient. HandleDehydrateFolders faz Path.Combine de nomes de pasta controlados pelo atacante e os alimenta no git via ProcessStartInfo.Arguments. O caminho de dehydrate da CLI executa IsFolderValid. O handler de montagem não.
Qualquer pessoa em Users que consiga adivinhar \\.\pipe\GVFS_<ENLISTMENT> pode desmontar o ProjFS, mover diretórios como o usuário da montagem e forçar git reset HEAD --hard.
| ID | ainda sem CVE |
| Classe | Privilégio remanescente (ACL de named pipe) / path traversal / injeção 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 |
| Produto | VFS for Git |
| Afetado | master ab95cf51d6ffd0ee0e2feae7180078fe9ecbbb3a; tags do instalador v2.0.26229.1 / v2.0.26259.1. Somente Windows. |
| Autenticação | Local. Qualquer principal BUILTIN\Users enquanto a vítima tem uma montagem |
| Licença | GNU Affero GPL v3.0 |
| Lab | Somente 127.0.0.1. how=gadget (sem VM Windows / ProjFS real). |
Protocolo do pipe: UTF-8 Header ou Header|body, terminado pelo byte 0x03. O nome do pipe é GVFS_ + raiz do enlistment em maiúsculas com : substituído por _ (exemplo C:\Repos\Foo -> \\.\pipe\GVFS_C_\REPOS\FOO).
A vítima tem gvfs mount em uma raiz conhecida. O atacante é outro principal Users na mesma máquina. Integridade e disponibilidade como a vítima. Não SYSTEM. Não um shell por si só.
| Privilégio | Método / sink | O que concede | Recon / integridade / disponibilidade | Comprovado em lab ou inferido |
|---|---|---|---|---|
| Conectar ao pipe de montagem da vítima | ACL de CreatePipeByName BuiltinUsers ReadWrite | CreateNewInstance; sem verificação de proprietário | Falar o IPC de montagem | Recon do pipe | Comprovado em lab (ACL do gadget). SID do atacante aceito; modo somente-proprietário retorna connect-denied |
| Ler caminho do enlistment, caminho de cache, RepoUrl | GetStatus | Vazar a raiz usada para o leftover #1 / #4 | Recon | Comprovado em lab. O JSON de status incluía RepoUrl e GVFS_C_\REPOS\FOO |
Desmontar ProjFS e sair do gvfs.mount | mensagem Unmount | Derrubar o enlistment | Disponibilidade | Comprovado em lab. Responde Ack e depois Complete; estado da montagem Unmounted |
| Desmontar em uma falha de parsing | ReleaseLock com corpo vazio -> RequestData == null -> UnmountAndStopWorkingDirectoryCallbacks + Environment.Exit | Mesma desmontagem mais saída do processo | Disponibilidade | Comprovado em lab. ReleaseLock| vazio desmontou |
| Descartar a árvore de trabalho da vítima | Dehydrate|{"Folders":"--hard",...}. movedFolders.Add mesmo se o diretório de origem estiver ausente. Reset("HEAD", "\"--hard\" ") vira git reset HEAD --hard após remoção de aspas | Destruir arquivos rastreados modificados | Integridade / disponibilidade | Comprovado em lab. Arquivo rastreado modificado perdeu a string testemunha |
| Mover uma pasta para fora da árvore de trabalho como a vítima | Path.Combine(WorkingDirectoryBackingRoot, folder) + MoveDirectory para BackupFolderPath | Roubar / realocar uma pasta da árvore de trabalho | Integridade | Comprovado em lab. keepme saiu do src e apareceu sob o caminho de backup do atacante |
Traversar .. (o IsFolderValid da CLI não é usado) | mesmo Combine; ..\secret | Mover um irmão no qual o usuário da montagem pode escrever | Integridade | Comprovado em lab. O irmão secret foi parar sob o caminho de backup do atacante. Aplicar IsFolderValid bloqueou |
| Roubar ou destruir outros diretórios graváveis pela vítima | mesmo move com uma cadeia ..\ que resolve fora do enlistment | Move mais amplo como o usuário da montagem | Integridade | Inferido a partir do Combine. Este lab usa apenas uma árvore descartável |
| Execução de código como a vítima | este pipe | Criação de processo de imagem do atacante | Integridade | Fechado. Sem criação de processo de imagem do atacante neste leftover (ver vfs-service-sid e vfs-hook-impersonate) |
| SYSTEM | IPC de montagem | LocalSystem | Integridade | Fechado. A montagem roda como o usuário do enlistment |
--hard não é um caminho ... O IsFolderValid da CLI ainda o permite. Uma correção também deve rejeitar nomes de pasta que pareçam opções.
Leftovers do mesmo pin: vfs-quote-breakout, vfs-clone-acl, vfs-service-sid, vfs-hook-impersonate, vfs-install-cwd.
Fiz o pin do VFSForGit master ab95cf51 após o merge de 2026-09-28. O pipe do serviço e o pipe de montagem por enlistment compartilham CreatePipeByName. O dehydrate na CLI valida pastas. O handler de montagem não.
Caminhos errados já registrados: tratar --hard como path traversal (é injeção de argumentos após remoção de aspas); alegar SYSTEM; um reverse shell. Teatro. O Windows nativo estava ausente, então o lab é um gadget dessas funções mais uma árvore git descartável.
python3 vfs-mount-dehydrate-Abraxas-Labs.py
ou
cd lab
./run.sh
Gadget HTTP em 127.0.0.1:19640 falando o formato de mensagem do pipe. Árvore git descartável dentro do contêiner. Sem reverse shell. Sem caminho de perfil real. A última linha 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 ao SID do proprietário do enlistment mais LocalSystem. Remover CreateNewInstance para Users. Personificar o cliente e exigir proprietário. Replicar IsFolderValid em HandleDehydrateFolders e rejeitar nomes que pareçam opções do git. Não tratar falha de parsing de ReleaseLock como saída do processo.