Windows: DCOM DCE/RPC Local NTLM Reflection Elevation of Privilege
Platform: Windows 8.1 Update (not tested on Windows 7, 10)
Class: Elevation of Privilege
Local DCOM DCE/RPC connections can be reflected back to a listening TCP socket allowing access to an NTLM authentication challenge for LocalSystem user which can be replayed to the local DCOM activation service to elevate privileges.
Note, before we start I realize that you didn’t fix the WebDAV => SMB one, you might conclude that this is a won’t fix as well but I couldn’t find good documentation on how to improve the security situation with DCOM-DCE/RPC to mitigate it (at least anything which seemed to work). Also the behaviour is slightly different. I did point out in the original report that WebDAV wasn’t necessarily the only way of getting an NTLM authentication challenge, with DCE/RPC being a specific example. Anyway on to the description.
When a DCOM object is passed to an out of process COM server the object reference is marshalled in an OBJREF stream. For marshal-by-reference this results in an OBJREF_STANDARD stream being generated which provides enough information to the server to locate the original object and bind to it. Along with the identity for the object is a list of RPC binding strings (containing a TowerId and a string). This can be abused to connect to an arbitrary TCP port when an unmarshal occurs by specifying the tower as NCACN_IP_TCP and a string in the form “host[port]”. When the object resolver tries to bind the RPC port it will make a TCP connection to the specified address and if needed will try and do authentication based on the security bindings.
If we specify the NTLM authentication service in the bindings then the authentication will use basic NTLM. We just need to get a privileged COM service to unmarshal the object, we could do this on a per-service basis by finding an appropriate DCOM call which takes an object, however we can do it generically by abusing the activation service which takes a marshalled IStorage object and do it against any system service (such as BITS). So this gets us a reflected NTLM authentication challenge, but what to do with it? There are at least 3 uses for this:
1) Use the NTLM for reflection back to the local SMB service (as per the WebDAV version) but of course this is considered mitigated.
2) Locally negotiate the NTLM which will give you back a full impersonation level token for LocalSystem (even done as a normal user). This is of most use for escaping Local Service/Network Service because you can combine this with SeImpersonatePrivilege for token kidnapping.
3) Reflect the NTLM back to a local RPC TCP endpoint, this is what we’ll do for a proof-of-concept.
It turns out that a number of RPC endpoints over TCP enforce at least RPC_C_AUTHN_LEVEL_CALL or RPC_C_AUTHN_LEVEL_PKT level security on the RPC channel. This, like SMB signing, makes it difficult to do reflection attacks. However not all services do, for example the DCOM IRemoteSCMActivator service doesn’t seem to enforce signing for the call (only requires RPC_C_AUTHN_LEVEL_CONNECT). While by default it does require authentication as an administrator we get that from the LocalSystem NTLM reflection. This allows us to create any out-of-process COM object locally as LocalSystem (unless it’s already a COM service where it uses the identity of the service).
While you can create the object there’s a problem, you can’t typically communicate with the object afterwards. Services such as WMI listen on TCP so can be accessed using the NTLM reflection afterwards however this enforces signing so any calls will fail. You can request a NCALRPC binding from the activation but this will be limited by the ability of the current user to bind to it. It turns out that our answer to this problem has already been used once, we can abuse the same IStorage marshalling in the activation process to initialize an object through IPersistFile or IPersistStorage before returning back to the caller. As long as you can find an object which runs out of process, can be initialized through one those interfaces and does something exploitable during initialization we can use it to elevate privileges.
A good example is everyone’s favourite COM object, the OLE Packager. As shown by various researchers, specifically Haifei Li (see https://blogs.mcafee.com/mcafee-labs/dropping-files-temp-folder-raises-security-concerns). This will drop arbitrary files to the user’s temporary folder during IPersistStorage initialization. The temporary folder for localsystem is %windir%\temp which is writable by normal authenticated users. Also while it’s a DLL server it is registered with an AppId supported DllSurrogate so we can create it out-of-process.
Due to the way the target path during initialization is created it doesn’t look obvious how you’d exploit this, especially as we can’t create file level symbolic links as a normal user. One attack we can do is as we control the filename we can use the $INDEX_ALLOCATION trick to convert a directory junction to a file level symbolic link. However that leaves you with a timing issue as a call is made to PathFileExists which will then fall back into generating the temporary file. Considering the packager, if it fails to initialize causes heap corruption in Dllhost (seriously) this isn’t a good approach.
Instead we can abuse the operation of PathFindFileName which has odd behaviour if the path ends with a slash, specifically it assumes the last component is the name plus the tailing slash, it doesn’t remove it. You also have to use forward slash otherwise it doesn’t work (you get an invalid name when generating the temporary file). So if we use the path, c:\path\somefile/ in the package it first checks %windir%\temp\somefile to see if it exists. We can drop a directory at that location so that the check continues to generate the temporary file. The code will then try %windir%\temp\somefile/ (2) which as Win32 handles both slashes tries to create a file inside a directory we control, which we set as a junction to a system directory (we can change the file name as well but that’s left as an exercise for the reader).
Proof of Concept:
I’ve provided a PoC which creates an arbitrary file at local system privileges. In the PoC the file is typically called ‘ (2)’ or similar, however through combined junction/object manager symlink attacks this can be created with any name you like (just the PoC doesn’t bother to do so).
The PoC has only been tested on 64 bit Windows 8.1 update.
1) Extract the PoC to a location on a local harddisk which is writable by a normal user
2) Execute Poc_DCERPCNTLMReflection_EoP.exe file as a normal user
3) There should be a ‘ (2)’ file created in the c:\windows directory with arbitrary content.
The NTLM reflection should fail
The NTLM reflection succeeds and an arbitrary file is created as a privileged user
Known Issues with the PoC:
1) If an existing instance of the package class is running as local system it will not reinitialize the object and the exploit will not work.
2) Sometimes the exploit fails completely during the reflection attack, this is typically indicated by a lot of error output being display to the console. Re-running the poc usually fixes this.
This bug is subject to a 90 day disclosure deadline. If 90 days elapse without a broadly available patch, then the bug report will automatically become visible to the public.