Mar 24

Recently I was presenting with an interesting problem: I needed to execute code under the User security context as a System account. Typically, you can execute code as a windows service under the system account without issue. Apparently, though, there may issues when trying to access WaitHandles across security contexts. 

To elaborate, when you create an EventWaitHandle in C# as the current user, it will, unless specifically specified, create it with the default security privileges. To prevent malicious programs from fooling around with your global handles, Windows separates these into "security contexts." You would see this as a .NET UnauthorizedAccessException.

Moving along, if you are unable to execute both programs under the same security context (same user), there is still hope! I was able to overcome this by using Windows Impersonation. The premise for the code is that it will execute any code within the application as a different user. As a caveat, this appears to only work when executing the code as the SYSTEM account (i.e. a windows service). If you try to impersonate a user as the admin, or the other way around, it will NOT work. 

Without further adieu, the code:

    //Tokens required to call OpenProcessToken. All three are required to impersonate a user context
    public const UInt32 TOKEN_DUPLICATE = 0x0002;
    public const UInt32 TOKEN_IMPERSONATE = 0x0004;
    public const UInt32 TOKEN_QUERY = 0x0008;


    //OpenProcessToken will open a process via its handle and attempt to retrieve the user token for that process.
    //We need this to grab the user token from a known shared program in order to run as the same user context. This prevents permission

   
    //This function will only succeed if executed under the SYSTEM account for security reasons
    [DllImport("advapi32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle);

    --------------------------------------------------------------------------------
        Process[] p = Process.GetProcessesByName("KnownUserProgram");
        if (p.Length > 0)
        {
          IntPtr pHandle = p[0].Handle;
          IntPtr currentHandle = WindowsIdentity.GetCurrent().Token;
          //In order to impersonate, duplicate, impersonate and query access must be requested.
          //OpenProcessToken attempts to steal a user token from a known application. This will only work under the SYSTEM account
          bool success = OpenProcessToken(pHandle, TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY, out currentHandle);

          if (success)
          {
            Console.Writeline("Successfully captured user token");
            WindowsIdentity id = new WindowsIdentity(currentHandle);
            //Impersonate will cause the program to begin executing as if it were the current user         

            WindowsImpersonationContext context = id.Impersonate();
            Console.Writeline("Impersonation complete");
          }
          else
          {
            Console.Writeline("Failed to get user token.");
          }
        }
        else
        {
          Console.Writeline("Mainline is not running, cannot grab user token");
        }
      }
      catch (Exception ex)
      {
        Console.Writeline(ex.ToString());
        Console.Writeline("Impersonation failed");
      }
      Console.Writeline( String.Format("Continuing under the {0} context", WindowsIdentity.GetCurrent().Name));

Tags: | | |