
VFS per Git ab95cf51 - Alto - Locale - Privilegio residuo - Gli utenti montano pipe dehydrate/unmount
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · vfs-mount-dehydrate
Classe: Privilegio residuo Portata: Locale
VFS for Git master ab95cf51 (tag v2.0.26259.1) - Microsoft
CreatePipeByName sulla pipe di mount per-enlistment concede a BUILTIN\Users ReadWrite | CreateNewInstance. Non c'è alcun controllo del SID del proprietario né ImpersonateNamedPipeClient. HandleDehydrateFolders esegue Path.Combine sui nomi di cartella controllati dall'attaccante e li passa a git tramite ProcessStartInfo.Arguments. Il percorso dehydrate della CLI esegue IsFolderValid. Il gestore del mount no.
Chiunque nel gruppo Users che riesca a indovinare \\.\pipe\GVFS_<ENLISTMENT> può smontare ProjFS, spostare directory come l'utente del mount e forzare git reset HEAD --hard.
| ID | nessuna CVE al momento |
| Classe | Privilegio residuo (ACL named-pipe) / path traversal / argument injection |
| Portata | Locale |
| 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 |
| Prodotto | VFS for Git |
| Affetto | master ab95cf51d6ffd0ee0e2feae7180078fe9ecbbb3a; tag dell'installer v2.0.26229.1 / v2.0.26259.1. Solo Windows. |
| Auth | Locale. Qualsiasi principal BUILTIN\Users mentre la vittima ha un mount |
| Licenza | GNU Affero GPL v3.0 |
| Lab | Solo 127.0.0.1. how=gadget (nessuna VM Windows / ProjFS live). |
Protocollo della pipe: UTF-8 Header o Header|body, terminato dal byte 0x03. Il nome della pipe è GVFS_ + la root dell'enlistment in maiuscolo con : sostituito da _ (esempio C:\Repos\Foo -> \\.\pipe\GVFS_C_\REPOS\FOO).
La vittima ha gvfs mount su una root nota. L'attaccante è un altro principal Users sulla stessa macchina. Integrità e disponibilità come la vittima. Non SYSTEM. Non una shell di per sé.
| Privilegio | Metodo / sink | Cosa concede | Recon / integrità / disponibilità | Provato in lab o dedotto |
|---|---|---|---|---|
| Connettersi alla pipe di mount della vittima | ACL di CreatePipeByName BuiltinUsers ReadWrite | CreateNewInstance; nessun controllo del proprietario | Parlare con l'IPC del mount | Recon della pipe | Provato in lab (ACL del gadget). SID dell'attaccante accettato; la modalità solo-proprietario restituisce connect-denied |
| Leggere il percorso dell'enlistment, il percorso della cache, RepoUrl | GetStatus | Trapelare la root usata per il residuo #1 / #4 | Recon | Provato in lab. Il JSON di stato includeva RepoUrl e GVFS_C_\REPOS\FOO |
Smontare ProjFS e uscire da gvfs.mount | messaggio Unmount | Mettere giù l'enlistment | Disponibilità | Provato in lab. Risponde Ack poi Complete; stato del mount Unmounted |
| Smontare su un errore di parsing | ReleaseLock con body vuoto -> RequestData == null -> UnmountAndStopWorkingDirectoryCallbacks + Environment.Exit | Stesso unmount più l'uscita del processo | Disponibilità | Provato in lab. ReleaseLock| vuoto ha smontato |
| Scartare il working tree della vittima | Dehydrate|{"Folders":"--hard",...}. movedFolders.Add anche se la directory sorgente manca. Reset("HEAD", "\"--hard\" ") diventa git reset HEAD --hard dopo la rimozione delle virgolette | Distruggere file tracciati modificati | Integrità / disponibilità | Provato in lab. Il file tracciato modificato ha perso la stringa witness |
| Spostare una cartella fuori dal worktree come la vittima | Path.Combine(WorkingDirectoryBackingRoot, folder) + MoveDirectory verso BackupFolderPath | Rubare / ricollocare una cartella del worktree | Integrità | Provato in lab. keepme ha lasciato src ed è apparso sotto il percorso di backup dell'attaccante |
Attraversare .. (la IsFolderValid della CLI non viene usata) | stesso Combine; ..\secret | Spostare una cartella sorella su cui l'utente del mount può scrivere | Integrità | Provato in lab. La cartella sorella secret è finita sotto il percorso di backup dell'attaccante. Applicare IsFolderValid l'ha bloccato |
| Rubare o distruggere altre directory scrivibili dalla vittima | stesso move con una catena ..\ che risolve fuori dall'enlistment | Spostamento più ampio come l'utente del mount | Integrità | Dedotto da Combine. Questo lab usa solo un albero usa-e-getta |
| Esecuzione di codice come la vittima | questa pipe | Creazione di processo di un'immagine dell'attaccante | Integrità | Chiuso. Nessuna creazione di processo di un'immagine dell'attaccante su questo residuo (vedi vfs-service-sid e vfs-hook-impersonate) |
| SYSTEM | IPC del mount | LocalSystem | Integrità | Chiuso. Il mount gira come l'utente dell'enlistment |
--hard non è un percorso ... La IsFolderValid della CLI lo consente comunque. Una correzione deve anche rifiutare nomi di cartella che sembrano opzioni.
Altri residui dello stesso pin: vfs-quote-breakout, vfs-clone-acl, vfs-service-sid, vfs-hook-impersonate, vfs-install-cwd.
Ho fissato VFSForGit master ab95cf51 dopo il merge del 2026-09-28. La pipe del servizio e la pipe di mount per-enlistment condividono CreatePipeByName. Il dehydrate sulla CLI valida le cartelle. Il gestore del mount no.
Vicoli ciechi già registrati: trattare --hard come un path traversal (è argument injection dopo la rimozione delle virgolette); rivendicare SYSTEM; una reverse shell. Teatro. Mancava un Windows nativo, quindi il lab è un gadget di quelle funzioni più un albero git usa-e-getta.
python3 vfs-mount-dehydrate-Abraxas-Labs.py
oppure
cd lab
./run.sh
Gadget HTTP su 127.0.0.1:19640 che parla la forma dei messaggi della pipe. Albero git usa-e-getta dentro il container. Nessuna reverse shell. Nessun percorso di profilo reale. L'ultima riga 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
Limitare CreatePipeByName al SID del proprietario dell'enlistment più LocalSystem. Rimuovere CreateNewInstance per Users. Impersonare il client e richiedere il proprietario. Replicare IsFolderValid in HandleDehydrateFolders e rifiutare i nomi che sembrano opzioni di git. Non trattare un errore di parsing di ReleaseLock come uscita del processo.