Windows: Constrained Impersonation Capability EoP
Platform: Windows 10 1703/1709 (not tested earlier versions)
Class: Elevation of Privilege
Summary: It’s possible to use the constrained impersonation capability added in Windows 10 to impersonate a lowbox SYSTEM token leading to EoP.
Windows 10 added a new security check during impersonation of a token which relies on an AppContainer capability Constrained Impersonation which allows a LowBox process to impersonate another LowBox token, even if it’s for a different user, as long as it meets certain requirements. Specifically:
I’d assume that the thoughts around the security of this constrained impersonation capability is preventing an exist lowbox process gaining that capability. However this can be abused from a normal user privilege level by creating a new AC process with the capability. As a normal user it’s possible to create a new lowbox token from an existing one which has any capabilities you like and the package SID can be arbitrary.
The only limiting factor is getting hold of a suitable token which has the same session ID. This is easy for example in UAC scenarios (including OTS elevation) but of course that’s a UAC bypass. There’s various tricks to get a SYSTEM token but most of the services run in Session 0. However there are a few processes running as SYSTEM but in the same session on a default install of Windows including CSRSS and Winlogon. There’s also the consent process which is part of UAC which is spawned in the user session. Therefore one way to get the token is to try and elevate a process running on a WebDAV share (hosted on localhost) and negotiate the NTLM/Negotiate auth in a similar way to previous issues I’ve reported (e.g. cases 21243 and 21878).
With a SYSTEM token handle it’s now possible to impersonate it as a lowbox from a normal user account. Of course this isn’t a direct privilege escalation as you can’t access administrator resources, however you can find system services which do the wrong thing. One example is code which just checks the Authentication ID of the token and assumes if it’s the SYSTEM ID then it’s trusted. A second example are AC processes which either run as SYSTEM or have tried to lock down themselves, a good example is the UMFD process, resources created by this process have access to SYSTEM as well as the package SID so you could inject code through hijacking a thread or one of the processes named resources. The final example are services which increase the IL of the caller, such as the print spooler bug I reported in case 41850, which you could get an arbitrary write as SYSTEM which gives you direct EoP.
Proof of Concept:
I’ve provided a PoC as a C# project. It implements a WebDAV server on localhost which will require authentication. Any user which tries to open a file on the share will have its token captured. It then uses UAC consent to get a call to the WebDAV server as a system token in the current session. Note that although I’m abusing UAC it’s not a UAC bypass, it’s just a convenient way of getting the token. This would still work in OTS UAC as the token happens before the process is actually executed (which means the password doesn’t have to be entered) so it’s still an issue. Once a suitable token has been captured the PoC spawns a new process in an AC and impersonates the system token on the main thread. It then abuses some functionality which was “fixed” in MS15-10, that it’s possible to open a service with SERVICE_STATUS access rights as long as the caller is SYSTEM. Admittedly this seemed to be a bogus fix as impersonation shouldn’t work like that in RPC, but in this case it doesn’t really matter as we can actually impersonate a SYSTEM token. The PoC stops at the point of getting a valid handle to the service, I’ve not worked out what you can usefully do with that handle, maybe start/stop a service you wouldn’t normally be able to?
Expected Result: Impersonating the SYSTEM token in a LowBox shouldn’t be possible.
Observed Result: The test binary is running while impersonating the SYSTEM token. It’s opened a handle to the WebClient service with SERVICE_STATUS access rights.