PoshExec.cs
using System; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.IO.Pipes; using System.Runtime.ConstrainedExecution; using System.Security.AccessControl; using System.Security.Cryptography; using System.Security.Principal; using System.ServiceProcess; using System.Runtime.InteropServices; using System.Threading; using System.Xml.Serialization; namespace PoshExecSvr { public class Service1 : ServiceBase { private ManualResetEvent _started = new ManualResetEvent(false); private System.ComponentModel.IContainer components; protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } private void InitializeComponent() { components = new System.ComponentModel.Container(); ServiceName = "PoshExecSvc"; } public Service1() { InitializeComponent(); } public void Start() { Listen("PoshExecSvrPipe"); _started.WaitOne(); } protected override void OnStart(string[] args) { Start(); } string _pipeName; private PipeSecurity _security; public void Listen(string pipeName) { try { _security = new PipeSecurity(); // Allow Everyone read and write access to the pipe. _security.SetAccessRule(new PipeAccessRule("Authenticated Users", PipeAccessRights.ReadWrite, AccessControlType.Allow)); // Allow the Administrators group full access to the pipe. _security.SetAccessRule(new PipeAccessRule("Administrators", PipeAccessRights.FullControl, AccessControlType.Allow)); // Set to class level var so we can re-use in the async callback method _pipeName = pipeName; // Create the new async pipe var pipeServer = new NamedPipeServerStream(_pipeName, PipeDirection.In, 1, PipeTransmissionMode.Message, PipeOptions.Asynchronous, 1024, 1024, _security); // Wait for a connection pipeServer.BeginWaitForConnection(WaitForConnectionCallBack, pipeServer); _started.Set(); } catch (Exception oEx) { Debug.WriteLine(oEx.Message); } } private void WaitForConnectionCallBack(IAsyncResult iar) { try { // Get the pipe var pipeServer = (NamedPipeServerStream)iar.AsyncState; // End waiting for the connection pipeServer.EndWaitForConnection(iar); var bRequest = new byte[1024]; do { int cbRequest = bRequest.Length; pipeServer.Read(bRequest, 0, cbRequest); } while (!pipeServer.IsMessageComplete); var serializer = new XmlSerializer(typeof(StartInfo)); var startInfo = (StartInfo)serializer.Deserialize(new MemoryStream(bRequest)); NativeMethods.EnableSecurityRights("SeTcbPrivilege", true); NativeMethods.EnableSecurityRights("SeAssignPrimaryTokenPrivilege", true); NativeMethods.EnableSecurityRights("SeIncreaseQuotaPrivilege", true); pipeServer.RunAsClient(() => { PROCESS_INFORMATION procInfo; var startupInfo = new STARTUPINFO(); int size = Marshal.SizeOf(startupInfo); startupInfo.cb = size; if (startInfo.Interact) { //TODO: Find right session ID and call SetTokenInformation to set it to the user token startupInfo.dwFlags = 0x00000001; //#define STARTF_USESHOWWINDOW startupInfo.wShowWindow = 5; //#define SW_SHOW startupInfo.lpDesktop = "WinSta0\\Default"; } if (!CreateProcessAsUser(WindowsIdentity.GetCurrent().Token, null, startInfo.CommandLine, IntPtr.Zero, IntPtr.Zero, false, 0, IntPtr.Zero, null, ref startupInfo, out procInfo)) { Debug.WriteLine(String.Format("{0}", Marshal.GetLastWin32Error())); } }); // Kill original sever and create new wait server pipeServer.Close(); pipeServer = new NamedPipeServerStream(_pipeName, PipeDirection.In, 1, PipeTransmissionMode.Message, PipeOptions.Asynchronous, 1024, 1024, _security); // Recursively wait for the connection again and again.... pipeServer.BeginWaitForConnection( WaitForConnectionCallBack, pipeServer); } catch (Exception ex) { Debug.WriteLine(ex.Message); } } [DllImport("kernel32.dll", SetLastError = true)] static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); [DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Auto)] static extern bool CreateProcessAsUser( IntPtr hToken, string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); [DllImport("Wtsapi32.dll", SetLastError = true)] static extern bool WTSQueryUserToken(UInt32 sessionId, out IntPtr handle); [DllImport("Kernel32.dll", SetLastError = true)] static extern UInt32 WTSGetActiveConsoleSessionId(); } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 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 dwYSize; 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; } [StructLayout(LayoutKind.Sequential)] internal struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public int dwProcessId; public int dwThreadId; } [StructLayout(LayoutKind.Sequential)] public struct SECURITY_ATTRIBUTES { public int nLength; public IntPtr lpSecurityDescriptor; public int bInheritHandle; } [Serializable] public class StartInfo { public string CommandLine; public bool Interact; } static class Program { /// <summary> /// The main entry point for the application. /// </summary> static void Main() { var service = new Service1(); if (!Environment.UserInteractive) { var servicesToRun = new ServiceBase[] { service }; ServiceBase.Run(servicesToRun); return; } service.Start(); Console.ReadLine(); } } [Flags] internal enum TokenAccessLevels { AssignPrimary = 0x00000001, Duplicate = 0x00000002, Impersonate = 0x00000004, Query = 0x00000008, QuerySource = 0x00000010, AdjustPrivileges = 0x00000020, AdjustGroups = 0x00000040, AdjustDefault = 0x00000080, AdjustSessionId = 0x00000100, Read = 0x00020000 | Query, Write = 0x00020000 | AdjustPrivileges | AdjustGroups | AdjustDefault, AllAccess = 0x000F0000 | AssignPrimary | Duplicate | Impersonate | Query | QuerySource | AdjustPrivileges | AdjustGroups | AdjustDefault | AdjustSessionId, MaximumAllowed = 0x02000000 } internal enum SecurityImpersonationLevel { Anonymous = 0, Identification = 1, Impersonation = 2, Delegation = 3, } internal enum TokenType { Primary = 1, Impersonation = 2, } internal sealed class NativeMethods { public static void EnableSecurityRights(string desiredAccess, bool on) { IntPtr token = IntPtr.Zero; if (!OpenProcessToken(GetCurrentProcess(), TokenAccessLevels.AdjustPrivileges | TokenAccessLevels.Query, ref token)) { return; } LUID luid = new LUID(); if (!LookupPrivilegeValue(null, desiredAccess, ref luid)) { CloseHandle(token); return; } TOKEN_PRIVILEGE tp = new TOKEN_PRIVILEGE(); tp.PrivilegeCount = 1; tp.Privilege = new LUID_AND_ATTRIBUTES(); tp.Privilege.Attributes = on ? SE_PRIVILEGE_ENABLED : SE_PRIVILEGE_DISABLED; tp.Privilege.Luid = luid; int cbTp = Marshal.SizeOf(tp); if (!AdjustTokenPrivileges(token, false, ref tp, (uint) cbTp, IntPtr.Zero, IntPtr.Zero)) { Debug.WriteLine(new Win32Exception().Message); } CloseHandle(token); } internal const uint SE_PRIVILEGE_DISABLED = 0x00000000; internal const uint SE_PRIVILEGE_ENABLED = 0x00000002; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] internal struct LUID { internal uint LowPart; internal uint HighPart; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] internal struct LUID_AND_ATTRIBUTES { internal LUID Luid; internal uint Attributes; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] internal struct TOKEN_PRIVILEGE { internal uint PrivilegeCount; internal LUID_AND_ATTRIBUTES Privilege; } internal const string ADVAPI32 = "advapi32.dll"; internal const string KERNEL32 = "kernel32.dll"; internal const int ERROR_SUCCESS = 0x0; internal const int ERROR_ACCESS_DENIED = 0x5; internal const int ERROR_NOT_ENOUGH_MEMORY = 0x8; internal const int ERROR_NO_TOKEN = 0x3f0; internal const int ERROR_NOT_ALL_ASSIGNED = 0x514; internal const int ERROR_NO_SUCH_PRIVILEGE = 0x521; internal const int ERROR_CANT_OPEN_ANONYMOUS = 0x543; [DllImport( KERNEL32, SetLastError = true)] [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] internal static extern bool CloseHandle(IntPtr handle); [DllImport( ADVAPI32, CharSet = CharSet.Unicode, SetLastError = true)] [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] internal static extern bool AdjustTokenPrivileges( [In] IntPtr TokenHandle, [In] bool DisableAllPrivileges, [In] ref TOKEN_PRIVILEGE NewState, [In] uint BufferLength, [In, Out] IntPtr PreviousState, [In, Out] IntPtr ReturnLength); [DllImport( ADVAPI32, CharSet = CharSet.Auto, SetLastError = true)] [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] internal static extern bool RevertToSelf(); [DllImport( ADVAPI32, EntryPoint = "LookupPrivilegeValueW", CharSet = CharSet.Auto, SetLastError = true)] [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] internal static extern bool LookupPrivilegeValue( [In] string lpSystemName, [In] string lpName, [In, Out] ref LUID Luid); [DllImport( KERNEL32, CharSet = CharSet.Auto, SetLastError = true)] [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] internal static extern IntPtr GetCurrentProcess(); [DllImport( KERNEL32, CharSet = CharSet.Auto, SetLastError = true)] [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] internal static extern IntPtr GetCurrentThread(); [DllImport( ADVAPI32, CharSet = CharSet.Unicode, SetLastError = true)] [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] internal static extern bool OpenProcessToken( [In] IntPtr ProcessToken, [In] TokenAccessLevels DesiredAccess, [In, Out] ref IntPtr TokenHandle); [DllImport (ADVAPI32, CharSet = CharSet.Unicode, SetLastError = true)] [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] internal static extern bool OpenThreadToken( [In] IntPtr ThreadToken, [In] TokenAccessLevels DesiredAccess, [In] bool OpenAsSelf, [In, Out] ref IntPtr TokenHandle); [DllImport (ADVAPI32, CharSet = CharSet.Unicode, SetLastError = true)] [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] internal static extern bool DuplicateTokenEx( [In] IntPtr ExistingToken, [In] TokenAccessLevels DesiredAccess, [In] IntPtr TokenAttributes, [In] SecurityImpersonationLevel ImpersonationLevel, [In] TokenType TokenType, [In, Out] ref IntPtr NewToken); [DllImport (ADVAPI32, CharSet = CharSet.Unicode, SetLastError = true)] [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] internal static extern bool SetThreadToken( [In] IntPtr Thread, [In] IntPtr Token); } } |