Relaying Potatoes: Another Unexpected Privilege Escalation Vulnerability in Windows RPC Protocol

By Antonio Cocomazzi and Andrea Pierini

Executive Summary

  • Every Windows system is vulnerable to a particular NTLM relay attack that could allow attackers to escalate privileges from User to Domain Admin.
  • The current status of this vulnerability is “won’t fix”.
  • Enterprise security teams are encouraged to follow the recommendations and mitigations given below.

Introduction

NTLM relaying [1] is a well known technique that has long been abused by attackers. Despite the continuous “fixes” from 2001 onwards, it is still possible in a MITM scenario, for certain protocols where signing is not enabled, to intercept the NTLM messages and to “relay” the authentication to a target resource in order to elevate privileges. Normally, NTLM relays need user intervention, so you have to trick the victim to authenticate to a resource under your control.

In recent years [2] [3] [4], we conducted research into the so-called “DCOM DCE/RPC Local NTLM Reflection”, in particular when it comes to negotiating locally a SYSTEM token and impersonating it, thus leading to an elevation from a SERVICE account to SYSTEM.

Despite recent fixes, it is still possible under certain conditions to negotiate a highly privileged token and to impersonate it during the unmarshalling process of initializing a DCOM object, identified by particular CLSID, via the IStorage Object trigger [5].

During our earlier research, where we implemented a “fake” Oxid Resolver instructing the DCOM client to connect to a RPC Server under our control, we observed that two interesting NTLM authentications took place:

  1. Oxid Resolution (IObjectExporter::ResolveOxid2 call)
  2. IRemUnknown Interface  (IRemUnknown2::RemRelease call)

We decided to investigate the possibility of relaying these authentications to a different resource on a remote machine such as LDAP, SMB, and HTTP instead of stealing the token which requires the impersonation privileges.

First of all, we needed a “usable” authentication, possibly coming from highly privileged users like “Domain Admins” and so on. Some particular CLSID, belonging to application identifiers (AppId) which run under the context of the “Interactive User” can come to the rescue, because they “impersonate” the logged on user in the first session when instantiated in session 0. This is really cool because no victim user interaction is required!

The basic idea was that a standard user could trigger the privileged authentication without the victim’s interaction and relay it to a privileged service on another machine, e.g. ldap server, in order to elevate the user privileges.

In order to understand if there was a potential exploitation path, we needed to answer the following questions:

  • Does the RPC client sign (and check) the NTLM messages?
  • Could the RPC authentication be relayed in this particular cross protocol scenario?

In this post, we will go through the process of how we discovered answers to those questions and, step-by-step, show how we achieved an exploitation path. We went through a lengthy process of responsible disclosure with Microsoft before publishing. Although Microsoft considers the vulnerability an important privilege escalation, it has been classified as “Won’t Fix”.

The NTLM Auth Messages

Our idea was to use one of two authentications as described above. The first one (IObjectExporter::ResolveOxid2) seemed unpromising because the NTLM “Sign flag” was set and we were not sure if the upper layer protocols would consider it or not.

The second one (IRemUnknown2::RemRelease) met our expectations, and the flag was not set (more on this later…):

Now that we knew which authentication to use, the next step was to trigger and intercept it.

The RPC Trigger

It all started from this article [6] by James Forshaw, in which he discovered a way to abuse the the DCOM activation service by unmarshalling an IStorage object and reflecting the NTLM back to a local RPC TCP endpoint to achieve a local privilege escalation. While this vulnerability has been patched, the DCOM activation service was (and still is) a working trigger for RPC authentications.

This is still the trigger of all the “*potato” exploits in order to escalate privileges by leveraging the impersonation privileges.

Nowadays, those exploits focus on stealing a token from a privileged service (usually those running as SYSTEM are of interest), but we know that some CLSID (a way to identify COM class objects) impersonate the user connected to the first Session outside Session 0. [7]

If we have a shell in Session 0, even as a low privileged user, and trigger these particular CLSIDs, we will obtain an NTLM authentication from the user who is interactively connected (if more than one user is interactively connected, we will get that of the user with lowest session id).

So our hypothetical scenario could be the following:

  1. A compromised account with a shell in Session 0. This could be:
    • A low privileged user who can connect to the machine via WinRM-PSSession [8] or ssh [9]
    • A low privileged user who has been granted “Logon as a batch job” rights so that he can create and then run scheduled tasks with the property “Run the task whether the user is logged in or not” from an interactive session on this machine (local or via RDP)
    • A service account even running under a Hardened Virtual Account (e.g., without impersonation privileges [10]).
  2. A highly privileged user logged on to the same machine (local or via RDP)

With that in mind, we focused on analyzing all the “vulnerable” CLSID that we could use to trigger this authentication. These are the ones we found on a Windows Server 2019:

  • BrowserBroker Class
    {0002DF02-0000-0000-C000-000000000046}
  • AuthBrokerUI
    {0ea79562-d4f6-47ba-b7f2-1e9b06ba16a4}
  • Easconsent.dll
    {5167B42F-C111-47A1-ACC4-8EABE61B0B54}
  • Authentication UI CredUI Out of Proc Helper for Non-AppContainer Clients
    {924DC564-16A6-42EB-929A-9A61FA7DA06F}
  • UserInfoDialog
    {934b410c-43e4-415e-9935-fbc081ba93a9}
  • CLSID_LockScreenContentionFlyout
    {BA441419-0B3F-4FB6-A903-D16CC14CCA44}
  • Picker Host
    {c58ca859-80bc-48df-8f06-ffa94a405bff}
  • IsolatedMessageDialogFactory
    {f65817c8-dd85-4136-89f0-b9d12939f2c4}
  • SPPUIObjectInteractive Class
    {F87B28F1-DA9A-4F35-8EC0-800EFCF26B83}
  • CastServerInteractiveUser
    {f8842f8e-dafe-4b37-9d38-4e0714a61149}

Finding an Exploitation Path

Having found a perfect RPC trigger, we started to prepare all the pieces of our possible exploitation scenario.

First, we wanted to understand if we could relay this authentication in a cross protocol scenario, so we wrote a cpp POC that performs a MITM between an RPC authentication and relay over an HTTP server that requires authentication, and guess what? It worked!

We were able to browse protected files on the webserver on behalf of the user who authenticated to our RPC server.

This was the workflow implemented in our MITM:

Once we identified that our RPC authentication could be relayed in a cross protocol scenario, we put together the final exploitation path:

  1. An attacker has a shell in Session 0 on the “victim” machine with a low privileged account;
  2. On this machine a privileged user, like a Domain Admin, is logged on interactively;
  3. The attacker triggers the DCOM activation service by unmarshalling an IStorage object, calling CoGetInstanceFromIstorage with one of the CLSIDs that impersonate the user logged on interactively and setting the IP of the attacker machine for Oxid resolution requests;
  4. The attacker implements a MITM by just listening on port 135 on his machine, which will receive the IObjectExporter::ResolveOxid2 authenticated call and be forwarded to the “fake” Oxid resolver. Even if this call is authenticated, the NTLM “Sign flag” is set so it will be skipped;
  5. The fake Oxid resolver returns a string binding for an RPC endpoint under the attacker’s control;
  6. The victim machine/user will make an authenticated call IRemUnknown2::RemRelease contacting the RPC server (without the Sign flag set);
  7. Authentication will be relayed to a privileged resource such as LDAP, SMB, HTTP or other.Here we had two paths that we could have followed:
    1. Implement in ntlmrelayx a “minimalistic” RPC server with the impacket libs [11].
    2. Encapsulate and forward the authentication in a protocol already implemented and supported in ntlmrelayx[12], e.g. HTTP.
  8. We chose the second path and implemented the whole logic of the MITM and HTTP encapsulator in a POC (RemotePotato0). We then forwarded everything to ntlmrelayx and let it do the job by targeting the LDAP server running on the Domain Controller and adding a new domain admin (or elevate user privileges).

The following schema shows the entire attack flow:

Dealing with Signing Restrictions

Now that exploitation was successful, we tried to understand why the “Sign” flag was not set in IRemUnknown2 calls.

After several tests, we discovered that the poisoned response we give back from our fake Oxid Resolver could influence the setting of the “Sign” flag. One of the fields returned by the Oxid resolver is the security bindings which tell the client which security provider is to be used with the authentication (AuthnSvc).

MSRPC (Microsoft implementation of DCE/RPC protocol) supports a variety of “Security Providers”, including NTLM.

This is a list of the available security providers:

If the provider is set to NTLM (RPC_C_AUTHN_WINNT), it won’t enforce the signing; if set to SPNEGO (RPC_C_AUTHN_GSS_NEGOTIATE), it will. And this is what we did in order to use the correct provider in our poisoned response code:

(*ppdsaOxidBindings)->aStringArray[securityOffset]=RPC_C_AUTHN_WINNT;

We also know that in order to perform these types of attacks, the “Authentication Level” should be RPC_AUTHN_LEVEL_CONNECT (0x2) because this defines an authentication mechanism without enforcing encryption/signing. We can define this kind of RPC authentication as “weak” and potentially vulnerable to relay attacks.

In our ResolveOxid2, we can control this behavior by setting the desired authentication level in pAuthnHint parameter, which returns the minimum accepted authentication level of the object exporter.

error_status_t ResolveOxid2
(
    handle_t        hRpc,
    OXID* pOxid, 
    unsigned short  cRequestedProtseqs, 
    unsigned short  arRequestedProtseqs[],
    DUALSTRINGARRAY** ppdsaOxidBindings,
    IPID* pipidRemUnknown, 
    DWORD* pAuthnHint, 
    COMVERSION* pComVersion
)
{ 
    *pAuthnHint = RPC_C_AUTHN_LEVEL_CONNECT;

And the client will make a call to the IRemUnkown2 interface at least with this level of authentication.

Note: if we set a higher value for the authentication level in the pAuthnHint parameter, for example RPC_C_AUTHN_LEVEL_PKT (0x4), the “Sign” flag will be set again!

Dealing with MIC Restrictions

We also performed a relaying test with SMB protocol on servers where signing was not enabled, and of course it worked, too.

We only ran into trouble with one particular CLSID: {c58ca859-80bc-48df-8f06-ffa94a405bff}, when trying to relay because of the “BAD IMPERSONATION LEVEL”.

In this case, the NTLM “Identify” flag was set in the IRemUnknown2 call (which means that the server should not impersonate the client) and was taken into consideration by SMBv2 protocol.

But what if we alter this flag during our MITM operations? We tried to unset the flag in the forwarded NTLM type 1 message:

and reset it in the NTLM type 2 response message:

A quick test demonstrated that it worked!

Some days after, Microsoft released the November 2020 security patches and magically it stopped working. The SMB handshakes always ended up with a generic “INVALID PARAMETER” which was triggered by an invalid NTLM authentication.

After some testing, we discovered that the root cause was the Message Integrity Check (MIC). It  seemed to be always checked, even if the NTLM “Sign” flag was not set [13], thus altering the NTLM messages led to a signature mismatch.

The Proof Of Concept

We have released a POC of the RemotePotato0 attack here:

https://github.com/antonioCoco/RemotePotato0

Below you can see a quick demonstration:

Mitigations & Detection

Given that Microsoft will not release an official patch, some mitigation by hardening your servers should be undertaken.

For HTTP(S), you should remove all non-TLS-protected HTTP bindings (prefer SSL everywhere, particularly where NTLM is used) and configure Channel Binding Tokens validation by setting the tokenChecking attribute to a minimum of Allow (if not Require) as documented here.

For LDAP, you should set the Domain controller: LDAP server signing requirements Group Policy to Require signature for non-LDAPS LDAP connections as documented here.

In addition, you should also set the Domain controller: LDAP server channel binding token requirements Group Policy to a minimum of When Supported (if not Always) as documented here.

For SMB, you should configure SMB Signing by setting the Group Policy Digitally sign server communication (always) as documented here.

We have also released a YARA rule to detect RemotePotato0:

rule SentinelOne_RemotePotato0_privesc {
    meta:
        author = "SentinelOne"    
        description = "Detects RemotePotato0 binary"
        reference = "https://www.sentinelone.com/wp-content/uploads/relaying-potatoes-dce-rpc-ntlm-relay-eop"   
    strings:    
    	$import1 = "CoGetInstanceFromIStorage"
        $istorage_clsid = "{00000306-0000-0000-c000-000000000046}" nocase wide ascii    
        $meow_header = { 4d 45 4f 57 }
        $clsid1 = "{11111111-2222-3333-4444-555555555555}" nocase wide ascii
        $clsid2 = "{5167B42F-C111-47A1-ACC4-8EABE61B0B54}" nocase wide ascii
   condition:       
         (uint16(0) == 0x5A4D) and $import1 and $istorage_clsid and $meow_header and 1 of ($clsid*)
}

Conclusion 

We successfully demonstrated how it was possible to trigger an authenticated RPC/DCOM call and relay the NTLM authentication to other protocols. This is different from other known techniques such as CVE-2020-1113 [14] and CVE-2021-1678 [15], where relaying happens between a generic “client” protocol vs. an RPC server. In this case, we had an RPC client whose authentication was relayed to other “server” protocols and without “victim” interaction. Therefore, we hope that MS reconsider their decision not to fix this serious vulnerability.

Disclosure Timeline

11/30/2020 – Submitted vulnerability to MSRC case 62293
12/29/2020 – Microsoft confirmed vulnerability as Important - Elevation of Privilege
1/7/2021 – We informed Microsoft that full details about this issue would have been published after 90 days since our first notification
1/7/2021 – Microsoft asked to keep this issue confidential until 13th of April 2021 (135 days after our first notification) and that a fix was scheduled for that day
2/10/2021 – We agreed to keep this issue confidential as per Microsoft’s request
Mar-Apr/2021 – Sent multiple notifications in order to understand if the fix would be released on the agreed date
4/6/2021 – Microsoft informed us that a security fix would not be released on the 13th of April. No commitment for an exact date for a fix
4/13/2021  – Microsoft informed us that, after an extensive review, they determined that “Servers must defend themselves against NTLM relay attacks” (side note: setting the sign flag in NTLM provider as well as SPNEGO would have inhibited this exploit…)
4/26/2021 – Disclosing this issue

References

  1. https://en.hackndo.com/ntlm-relay/
  2. http://ohpe.it/juicy-potato/
  3. https://decoder.cloud/2019/12/06/we-thought-they-were-potatoes-but-they-were-beans/
  4. https://decoder.cloud/2020/05/11/no-more-juicypotato-old-story-welcome-roguepotato/
  5. https://github.com/decoder-it/juicy_2
  6. https://bugs.chromium.org/p/project-zero/issues/detail?id=325
  7. https://github.com/ohpe/juicy-potato/tree/master/CLSID
  8. https://docs.microsoft.com/en-us/powershell/scripting/learn/remoting/running-remote-commands?view=powershell-5.1
  9. https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse
  10. https://decoder.cloud/2020/11/05/hands-off-my-service-account/
  11. https://github.com/SecureAuthCorp/impacket
  12. https://github.com/SecureAuthCorp/impacket/blob/master/examples/ntlmrelayx.py
  13. https://twitter.com/decoder_it/status/1347976999567032321
  14. https://blog.compass-security.com/2020/05/relaying-ntlm-authentication-over-rpc/
  15. https://www.crowdstrike.com/blog/cve-2021-1678-printer-spooler-relay-security-advisory/