After the hype we (@splinter_code and me) created with our recent tweet , it’s time to reveal what we exactly did in order to get our loved JuicyPotato kicking again… (don’t expect something disruptive 😉 )
We won’t explain how the *potato exploits works, how it was fixed etc etc, there is so much literature about this and google is always your best friend!
Our starting point will be the “RogueWiRM” exploit.
In fact, we were trying to bypass the OXID resolver restrictions when we came across this strange behavior of BITS. But what was our original idea?
“(…) we found a solution by doing some redirection and implementing our own “Oxid Resolver” . The good news is that yes, we got a SYSTEM token! The bad one: it was only an identification token, really useless “
- You try to initialize a DCOM object identified by a particular CLSID from an “IStorage Object” via standard marshalling (OBJREF_STANDARD)
- The “IStorage” obect contains, among other things, the OXID, OID, and IPID parameters needed for the OXID resolution in order to get the needed bindings to connect to our DCOM object interfaces. Think about it like sort of DNS query
- From “Inside COM+: Base Services”: The OXID Resolver is a service that runs on every machine that supports COM+. It performs two important duties:
- It stores the RPC string bindings that are necessary to connect with remote objects and provides them to local clients.
- It sends ping messages to remote objects for which the local machine has clients and receives ping messages for objects running on the local machine. This aspect of the OXID Resolver supports the COM+ garbage collection mechanism.
- OXID resolver is part of “rpcss” service and runs on port 135. Starting from Windows 10 1809 & Windows Server 2019, its no more possible to query the OXID resolver on a port different than 135
- OXID resolutions are normally authenticated. We are only interested in the authentication part, which occurs via NTLM in order to steal the token, so we can specify in our case for OXID, OID and IPID just 32 random bytes.
- If you specify a remote OXID resolver, the request will be processed with an “ANONYMOUS LOGON“.
So what was our idea in our previous experiments?
- Instruct the DCOM server to perform a remote OXID query by specifying a remote IP in the binding strings instead of “127.0.0.1”. This is done in the “stringbinding” structure
- On the remote IP where is running a machine under our control, setup a “socat” listener (or whatever you prefer) for redirecting the OXID resolutions requests to a “fake” OXID RPC Server, created by us and listening on a port different from 135. The fake server can run on the same Windows machine we are trying to exploit, but not necessary.
- The fake OXID RPC server implements the “ResolveOxid2” server procedure
- This function will resolve the client (ANONYMOUS LOGON) query by returning the RPC bindings which will point to another “fake” RPC server listening on a dedicated port under our control hosted on the victim machine.
- The DCOM server will connect to the RPC server in order to perform the IRemUnkown2 interface call. By connecting tho the second RPC Server, an “Autentication Callback” is triggered and if we have SE_IMPERSONATE_NAME privileges, we can impersonate the caller via RpcImpersonateClient() call
- And yes, it worked! We got a SYSTEM token coming from BITS service in this case, but unfortunately it was an Identification token, really useless.
The good news
So we abandoned the research until some days ago, when two interesting exploits/bugs had been published:
- Sharing a Logon Session a Little Too Much published by @tiraniddo . You can also find in this blog the post along with a working POC in order to demonstrate how it was possible for the NETWORK SERVICE Account to elevate privileges (bug/feature?)
- The marvellous “Print Spoofer” exploit published by @itm4n. In this case (and for our scenario) not for the exploit itself but for the “bug” in the named pipe path validation checks.
On a late evening of some days ago, @splinter_code sent me a message:
(@splinter_code) “If we resolve the fake OXID request by providing an RPC binding with our named pipe instead of the TCP/IP sequence, we should in theory be able to impersonate the NETWORK SERVICE Account and after that… SYSTEM is just a step beyond…”
(me) “Yeah, sounds good, let’s give it a try!”
And guess what? IT WORKED!!!
Let’s dig into the details.
The “fake” OXID Resolver
First of all a little bit of context on how the OXID resolution works is required to understand the whole picture.
This is how the flow looks like, taken from msdn :
The client will be the RPCSS service that will try to connect to our rogue oxid resolver (server) in order to locate the binding information to connect to the specified dcom.
Consider that all the above requests done by the client in the OXID resolution sequence are authenticated (RPC Security Callback) impersonating the user running the COM behind the CLSID we choose (usually the one running as SYSTEM are of interests).
What *potato exploits does is intercepting this authentication and steal the token (well technically is a little bit more complicated because the authentication is forwarded to the local system OXID resolver that will do the real authentication and *potato will just alter some packets to steal the token… a classic MITM attack).
When this is done over the network (due to the MS fix) this will be an Anonymous Logon so quite useless for an EoP scenario.
Our initial idea was to exploit one of the OXID resolver (technically called IObjectExporter) method and forge a response that could trigger a privileged authentication to our controlled listener.
Looking at the scheme above 2 functions caught our attention:
- ServerAlive (ServerAlive2 for latest windows)
- ResolveOxid (ResolveOxid2 for latest windows)
So the first request was not of our interest because we couldn’t specify endpoint information (that’s what we needed) so we were always answering with a response of RPC_S_OK. This is an example of what a standard response looks like:
So we focused in the next one (ResolveOxid2) and what we saw is something interesting that we could abuse:
Observing the above traffic, that is a standard response captured on the real OXID resolver on a windows machine, we quickly spot that we could abuse this call by forging our own ResolveOxid2 response and specify as an endpoint our controlled listener.
We can specify endpoint information in the format ipaddress[endpoint] and also the TowerId (more on that later).
But… Why not registering the binding information we require on the system OXID resolver, getting the OID and OXID from the system and ask the unmarshalling of that?
Well as our knowledge the only way to do that is by registering a CLSID on the system and to do that you need Administrator privileges.
So our idea was to implement a whole OXID resolver that would answer always with our listener binding for any oxid oid it will receive, just a Fake OXID resolver.
Let’s analyze the function ResolveOxid2 to understand how to forge our response:
[idempotent] error_status_t ResolveOxid2( [in] handle_t hRpc, [in] OXID* pOxid, [in] unsigned short cRequestedProtseqs, [in, ref, size_is(cRequestedProtseqs)] unsigned short arRequestedProtseqs, [out, ref] DUALSTRINGARRAY** ppdsaOxidBindings, [out, ref] IPID* pipidRemUnknown, [out, ref] DWORD* pAuthnHint, [out, ref] COMVERSION* pComVersion );
What we need to do is to fill the DUALSTRINGARRAY ppdsaOxidBindings with our controlled listener.
Well that was a little bit tricky, but it is well documented by MS (in 1 and 2) so it was just a matter of trial and errors to forge the right offsets/sizes for the packet.
Let’s recap, we can redirect the client to our controlled endpoint (aNetworkAddr) and we can choose the protocol sequence identifier constant that identifies the protocol to be used in RPC calls (wTowerId).
There are multiple protocols supported by RPC (here a list).
In the first attempt we tried ncacn_ip_tcp, as TowerId, that allows RPC straight over TCP.
We run an RPC server (keep in mind that a “standard” user can setup and register an RPC Server) with the IRemUnknown2 interface and tried to authenticate the request in our SecurityCallback calling RpcImpersonateClient.
Returning ncacn_ip_tcp:localhost in the ResolveOxid2 response, an authentication to our RPC server (IRemUnknown2) were triggered, but unfortunately was just an identification token…
So that was a dead end… But what about “Connection-oriented named pipes” ncacn_np ?
The named pipe Endpoint mapper
First of all what is the “epmapper” ? Well it’s related to the “RpcEptMapper” service. This service resolves “RPC interfaces identifiers to transport endpoints”. Same concept as OXID Resolver, but for RPC.
The mapper runs also on TCP port 135 but can also be queried via a special named pipe “epmapper”.
This service shares the same process space as the “rpcss” service and both of them run under the NETWORK SERVICE account. So if we are able to impersonate the account under this process we can steal the SYSTEM token (because this process hosts juicy tokens)…
The first try we gave was to return a named pipe under our control in the ResolveOxid2 response:
But observing the network traffic the RPCSS still connected to the \pipe\epmapper . This is because, by protocol design, the named pipe epmapper must be used, and this is obviously a “reserved” name:
When we read the PrintSpoofer post we were impressed by clever trick of the named pipe validation path bypass (credits to @itm4n and @jonasLyk) where inserting ‘/’ in the hostname will be converted in partial path of the named pipe!
With that in mind we returned the following binding information:
And you know what? The RPCSS were trying to connect to nonexistent named pipe \roguepotato\pipe\epmapper :
Great! So we setup a pipe listener on \\.\pipe\roguepotato\pipe\epmapper, got the connection and impersonated the client. Then we inspected the token with TokenViewer :
So we had an Impersonation token of the NETWORK SERVICE account and also the LUID of the RPCSS service!
Why that? Well, the answer was in the post mentioned above:
” if you can trick the “Network Service” account to write to a named pipe over the “network” and are able to impersonate the pipe, you can access the tokens stored in RPCSS service“
All the “magic” about the fake ResolveOxid2 response is happening in this piece of code:
The Token “Kidnapper”
Perfect! Now we just needed to perform the old and well known Token Kidnapping technique to steal some SYSTEM token from the RPCSS service.
We setup a minimalist “stealer” which performs the following operations:
- get the PID of the “rpcss” service
- open the process, list all handles and for each handle try to duplicate it and get the handle type
- if handle type is “Token” and token owner is SYSTEM, try to impersonate and launch a process with CreatProcessAsUser() or CreateProcessWithToken()
- In order to get rid of the occasional “Access Denied” when trying to launch a processes in Session 0, we also setup the correct permissions for the Windows Station/Desktop
Well, let’s unleash RoguePotato and see our SYSTEM shell popping 😀
Probably you are a little bit confused at this point, let’s do a recap. Under which circumstances can I use this exploit to get SYSTEM privileges?
- You need a compromised account on the victim machine with Impersonate privileges (but this is the prerequisite for all these type of exploits). A common scenario is code execution on default IIS/MSSQL configuration.
- If you are able to impersonate NETWORK SERVICE account or if the “spooler” service is not stopped you can first try the other exploits mentioned before…
- You need to have a machine under your control where you can perform the redirect and this machine must be accessible on port 135 by the victim
- We setup a POC with 2 exe files. In fact it is also possible to launch the fake OXID Resolver in standalone mode on a Windows machine under our control when the victim’s firewall won’t accept incoming connections.
- Extra mile: In fact, in the old *potato exploit, an alternate way for stealing the token was just to setup a local RPC listener on dedicated port instead of forging the local NTLM Authentication, RpcImpersonateClient() would have done the rest.
To sum it up:
You can find the source code and POC of RoguePotato here.
Final note: We didn’t bother to report this “misbehavior?” to MS because they stated that elevating from a Local Service process (with SeImpersonate) to SYSTEM is an “expected behavior”. (RogueWinRm – Disclosure Timeline)
That’ all 😉