I just finished playing  with the Rotten Potato C# exploit  in order to get it work standalone that the author  @breenmachine  released the C++ standalone version of  “Rotten Potato”. He really did a great job! https://github.com/foxglovesec/RottenPotatoNG 

Time for me to play with this new version, dig deeper in some “obscure” Windows API   calls and access tokens  in order to adjust code for my  needs..

The modified VS 2015 C++ project can be found here: https://github.com/decoder-it/lonelypotato  along with exe file, so if you are only interested in pentesting there is no need to move forward 😉

But  if you are la curious guy, just like me, keep on reading! My goal is to understand better the Windows access tokens and impersonation techniques. This can be very  helpful in Windows Privilege Escalation.

I will assume that you already know about the Rotten Potato exploit and have read all the posts. I will also assume that you are aware about Windows sessions, stations and desktops and that you have read my previous posts about the “Lonely Potato” here and here

Environment is the same as in the first post : Windows 2016 Server+ IIS + MSSQL Express.

Very short recap: with Rotten Potato exploit, we get an “admin” access token and if we have the privilege to impersonate the token we can run a process in this (very) high context -> SYSTEM. Typically, LocalService accounts (IIS, MSSQL) have the SeImpersonatePrivilege and SeAssignPrimaryToken privilege so they are able to impersonate a different user via the 2 Windows API CreatProcessAsUser() and CreateProcessWithTokenW().

First, we will modify the main() function in order to pass our arguments:

rotten2

We want to  test the 2 CreateProcess..() functions and invoke a program by command line (as usual a reverse powershell)

We can specifiy which API CreateProcess..() functions we want to check and with the jolly character we instruct the program to first  test CreateProcessAsUser()  and if it fails check the second one.

Please notice that main() changed in wmain() in order to use wide-character variables.

I won’t explain what the program does etc. .. just point out some facts.

OK, let’s go on…

whoami /priv does not show the SeImpersonatePrivilege in a standard cmd shell

You can test it on your own: create a regular user and assign him the “Impersonate Client after Authentication”  and “Replace process level token” via “gpedit.msc”.

Screenshot from 2018-01-14 15-47-41

 

Screenshot from 2018-01-14 15-48-06.png

Now logon with this user, open a standard cmd shell and an elevated one (run as administrator)

priv1

Do you notice the difference? SeImperosonatePrivilege is present only in the elevated session, SeAssignPrimaryTokenPrivilege in both, good to know!

 

How can I check if my process runs  in elevated mode?

A simple example is worth a thousand words:

tokenelev

 

We get the token of our process and then pass it to  GetTokenIformation(). This function can  do a lot of queries on our token, in this case we will ask if we if we run  in elevated mode.

So, if the statement is true, our process is elevated.  Don’t worry, “Local Service” processes always run in elevated mode but in other circumstances it could be useful in order to  “abuse” from other privileges…

What are the privileges needed by the 2 CreateProcess..() API calls?

MS says that CreateProcessAsUser() only needs the  the SeAssignPrimaryTokenPrivilege , whereas CreateProcessWithTokenW only needs the SeImpersonatePrivilege.

If you don’t have the right privlege, the API call with fail:

tokenscreen1

 

As you can see, I launched the  CreateProcessWithTokenW() without the necessary privilege and got an error.

The SeAssignPrimaryToken privilege is present but not enabled, how can I enable it?

Normally  SeImpersonatePrivilege is enabled, SeAssignPrimaryToken not. Again, with the right API calls it’s very easy task:

enablepriv

All we need now is enable the privilege:

EnablePriv(hToken, SE_ASSIGNPRIMARYTOKEN_NAME);

The return value of these API  calls is sometimes misleading, they can return true even if the privilege is not present.  Therefore I would suggest to check the GetLastError() Windows function.

Which type of access token are needed by the CreateProcess..() functions?

Quick & dirty: There are two type of access tokens:

  • Primary Tokens: for creating new processes
  • Impersonation Tokens: for creating new threads

What type of token is the “elevated_token”?

With the Rotten Potato exploit we get our  elevated token:

QuerySecurityContextToken(test->negotiator->phContext, &elevated_token)

Is it a primary or impersonate token?

tokentype.PNG

 

If token type is 1, we have a primary token, otherwise an impersonation token. In our case, we have to deal with an impersonation token:

[+] Elevated Token type:2

 

How can I change my access token to primary?

No problem, we can duplicate the “elevated_token” in a primary  token:

duptoken.PNG

If everything works fine, our “duped_token” will be of type:1 (primary)

Instead of calling a new process, can I start a new thread impersonating the elevated token?

Yes of course:

thread.PNG

All we have to do is  replace the thread’s token with the elevated one (remember, “elevated_token” is of type 2).

That’s why we started the thread in suspended state and then replaced the token with the elevated one via SetThreadToken().

What can we do inside the thread? Here we are SYSTEM, so imagination is the only limit…for example add the current user to administrators group:

 

thread2

 

CreateProcessWithTokenW() seems not to work if called in Session 0 (typically a remote shell spawned by IIS or MSSQL)

I noticed this in testing the C# version but did not investigate too much…

The function call returns true but nothing happens, strange behavior!

First of all what are the main differences between this versions?

We know that CreateProcessWithTokenW() needs the SeImpersonatePrivilege . In fact, when we use this function, we make a call to the “Secondary Logon Service” which then internally should call the CreateProcessAsUser() function?

After some tests, I discovered that the only  way to get  CreateProcessWithTokenW() working in session 0 is to set an interactive session.

si.lpDesktop = L"Winsta0\\default";

where “Winsta0\default” is the default interactive session.

That’s why  our process without Station/Desktop gets killed by Windows desktop manager.

This is strange, I even read somewhere that it was not possible to create an interactive session when calling CreateProcessWithTokenW() from a Local Service.

It’s not clear why CreateProcessAsUser() works in Session 0 even  if we do not set the Windows Station/Desktop. Need to investigate better, but for now that’s all we need 😉

Do I really need  a primary token for the CreateProcess..() functions?

No! You can use indifferently a primary or impersonation token, it works the same. This is not true for threads where you need and impersonation token.

 

Do I really need  to enable the SeAssignPrimaryToken Privilege?

Again, no! Surpisingly, during my tests, I discovered that it was not necessary to enable the privilege!

Will the Rotten Potato exploit work if I change the default accounts running  the service (IIS, MSSQL)?

Well, I’ll talk about that in a future post 😉

 

 

 

 

 

4 thoughts on “Potatoes and tokens

  1. Great article, very clear for a non-windows expert…
    Do you think that kind of exploit can work by the network (like first HTTP=>SMB exploit hot potato)?
    For example, if we can force with HTTP traffic SYSTEM account to perform authentication to some TCP listener we control in the network?

    Like

  2. Well, i thought that ntlm reflect back to a local RPC TCP endpoint was patched (derbycon2016 video from breen, time 12:10)… Am i wrong?
    So before impersonate token through local negociation, how do you get a session on the victim if you can’t reflect ntlm authentication though local network?
    If i misunderstood the trick and this relay is still possible, is there some tool to deploy http or smb server, ask ntlm authentication (impacket..), then reflect back to victim’s RPC endpoint to get a shell?
    Thanks

    Like

  3. In this case, the “rotten potato exploit”, you reflect back a DCOM DCE/RPC connection (BITS) to a our fake listening TCP socket and access the NTLM authentication. (https://bugs.chromium.org/p/project-zero/issues/detail?id=325&redir=1). As far as I know there are no fixes for this. The goal of my article
    was to describe how to abuse from the privileges (impersonate and assign) once you have a system/admin token obtained via these techniques.

    Like

Leave a comment