
VFS pour Git ab95cf51 - Moyen - Local - Reste de privilège - le pipe de service fait confiance au JSON OwnerSID
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · vfs-service-sid
Classe : Résidu de privilège Portée : Locale
VFS for Git master ab95cf51 (tag v2.0.26259.1) - Microsoft
GVFS.Service s'exécute en tant que LocalSystem. WindowsPlatform.CreatePipeByName accorde à BUILTIN\Users ReadWrite | CreateNewInstance sur GVFS.Service.pipe. RegisterRepoHandler.Run stocke le JSON OwnerSID tel quel. Rien n'appelle ImpersonateNamedPipeClient.
Tout principal Users peut enregistrer un chemin d'attaquant sous un SID de victime. Lors du SessionLogon, le service CreateProcessAsUser gvfs mount <attacker path> en tant que la victime. L'enfant n'est pas NT AUTHORITY\SYSTEM.
| ID | pas encore de CVE |
| Classe | Résidu de privilège (autorisation manquante / exécution inter-utilisateurs) |
| Portée | Locale |
| CWE | CWE-862, CWE-284 |
| CVSS | Moyen : 6.6 CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N |
| Produit | VFS for Git |
| Affecté | master ab95cf51d6ffd0ee0e2feae7180078fe9ecbbb3a ; tags GitHub v2.0.26229.1 / v2.0.26259.1. Windows uniquement. |
| Auth | Locale. Tout principal BUILTIN\Users. Connexion de la victime pour l'automount. |
| Licence | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1 uniquement. how=gadget (pas de GVFS.Service Windows réel). |
Git 2.35+ dubious-ownership peut alors faire échouer git dans une arborescence contrôlée par l'attaquant. Le processus démarre tout de même en tant que la victime. Si le chemin appartient à la victime mais est inscriptible par l'attaquant (vfs-clone-acl), mount git, credential.helper et les hooks s'exécutent en tant que cet utilisateur.
Tout principal BUILTIN\Users sur une machine où le service est installé. Se connecter à \\.\pipe\GVFS.Service.pipe. Aucun privilège supplémentaire.
| Privilège | Méthode / sink | Ce que cela accorde | Recon / intégrité / disponibilité | Prouvé en lab ou déduit |
|---|---|---|---|---|
| Lister toutes les racines d'enlistment actives | GetActiveRepoListRequest sans vérification de SID (TryGetActiveRepos, pas GetActiveReposForUser) | Chemins utilisés comme oracle de nom de pipe pour le résidu #2 | Recon | Prouvé en lab. Le gadget a listé /lab/repos/victim |
| Désactiver un enlistment de victime pour qu'il ne s'automounte pas | UnregisterRepoRequest {EnlistmentRoot} sans vérification de SID (TryDeactivateRepo) | Les dépôts de la victime restent non montés | Disponibilité | Prouvé en lab. Ligne de la victime disparue de la liste active |
| Persister un chemin d'attaquant sous le SID de la victime | RegisterRepoRequest JSON EnlistmentRoot + OwnerSID ; TryRegisterRepo stocke les deux | Ligne de registre chemin-attaquant + SID victime | Intégrité | Prouvé en lab. Ligne de registre /lab/repos/attacker + S-1-5-21-1000-2000-3000-1001 |
Démarrer GVFS.exe mount <attacker path> en tant que la victime à la connexion | OnSessionChange SessionLogon -> AutoMountRepos(userId) -> TryRunAs / CreateProcessAsUser | Création de processus inter-utilisateurs en tant qu'utilisateur connecté | Intégrité | Prouvé en lab sur le gadget. Parent lancé GVFS.Service, argv mount /lab/repos/attacker, uid 1002 victim, témoin écrit |
| S'exécuter en tant que NT AUTHORITY\SYSTEM | Même automount | Enfant LocalSystem | Intégrité | Fermé. L'enfant whoami est la victime, pas SYSTEM (child-not-system=yes) |
Git / hook / credential.helper en tant que la victime dans une arborescence contrôlée par l'attaquant | La création de processus a toujours lieu ; git 2.35+ safe.directory peut échouer | RCE par hook en tant que victime si l'arborescence appartient à la victime mais est inscriptible par l'attaquant | Intégrité | Déduit. Le lab n'a pas exécuté le git de Microsoft. Lier le chemin comme appartenant à la victime mais inscriptible par l'attaquant pour un RCE par hook |
Fermé sur ce pin : accès distant non authentifié (le named pipe est local). Shell SYSTEM. L'ACL par défaut du pipe laisse toujours entrer les Users ; un register patché qui lie le SID du client (gadget X-Patched: 1) n'automounte pas le chemin de l'attaquant pour la victime.
Résidus du même pin : vfs-quote-breakout, vfs-mount-dehydrate, vfs-clone-acl, vfs-hook-impersonate, vfs-install-cwd.
J'ai épinglé VFSForGit master ab95cf51 après le merge du 2026-09-28. L'ACL du pipe du service est Users ReadWrite. Register stocke le JSON OwnerSID. SessionLogon automonte en tant que ce SID. Rien n'usurpe l'identité du client du pipe.
Fausses pistes déjà consignées : appeler cela un shell SYSTEM (l'automount est un CreateProcessAsUser du jeton de connexion) ; traiter le git safe.directory comme une autorisation de l'IPC ; un reverse shell. Du théâtre. Windows natif était absent, donc le lab est un gadget des handlers du pin plus un oracle TCP Unix.
python3 vfs-service-sid-Abraxas-Labs.py
ou
cd lab
./run.sh
Gadget HTTP sur 127.0.0.1:19660. Témoin VFS-SERVICE-SID-WITNESS. La dernière ligne utilise how=gadget.
SUCCESS vfs-service-sid how=gadget register-forged-sid=yes automount-as-victim=yes child-not-system=yes VFS-SERVICE-SID-WITNESS
Usurper l'identité du client du named pipe (ImpersonateNamedPipeClient / NamedPipeServerStream.RunAsClient). Ignorer le JSON OwnerSID ; lier TryRegisterRepo au SID du client. Restreindre CreatePipeByName à l'utilisateur interactif et à LocalSystem (retirer BuiltinUsersSid ReadWrite / CreateNewInstance). GetActiveRepoList ne devrait retourner que les dépôts de ce client. UnregisterRepo devrait exiger la même correspondance de SID.