Return to HomePage



Securely Executing a New Process from a .NET Application (VB.NET)

Objectives

* Secure execution of a new process from within a running application
* Secure storage and retrieval of credentials needed to run a process encrypted in the DPAPI
* Deploying an application in "least privilege" mode and making use of another, privileged component to perform a critical task

Summary

The purpose of this code snippet is to illustrate how to execute a new process under a different user account within a running .NET application.


Applies To

* .NET Framework 2.0
* VB.NET

Scenarios

* Application component needs to access functionality that only exists in a different runtime component
* Application occasionally needs access to privileged functionality but wants to use a low-privileged security context for enhanced overall security

Solution Example

' Execute a process under another user account, making
' use of credentials stored in the DPAPI
Sub ExecuteProcess(ByVal fullProcessPath As String)
		    		' Retrieve the user and password from the registry and decrypt
		    		Dim encryptedUser As Byte() = Registry.GetValue("HKEY_CURRENT_USER\ProcessExecCreds", "User", Nothing)
		    		Dim encryptedPass As Byte() = Registry.GetValue("HKEY_CURRENT_USER\ProcessExecCreds", "Pass", Nothing)
	

		    		Dim user As String = [Encoding.ASCII.GetString(ProtectedData.Unprotect(encryptedUser,] Nothing, [DataProtectionScope.CurrentUser))]
		    		Dim pass As String = [Encoding.ASCII.GetString(ProtectedData.Unprotect(encryptedPass,] Nothing, [DataProtectionScope.CurrentUser))]
		    		Dim securepass As New [SecureString()]
		    		Dim b As Char
		    		For Each b In [pass.ToCharArray()]
		    			[securepass.AppendChar(b)]
		    		Next b
	

		    		' Execute a process as our stored user account
		    		' This process will run a simple batch file that outputs the runtime account name
		    		Dim process As New Process()
		    		[process.StartInfo.UseShellExecute] = False
		    		[process.StartInfo.RedirectStandardOutput] = True
		    		[process.StartInfo.RedirectStandardError] = True
		    		[process.StartInfo.CreateNoWindow] = True
	

		    		[process.StartInfo.FileName] = [fullProcessPath]
		    		[process.StartInfo.WorkingDirectory] = "C:\"
		    		[process.StartInfo.UserName] = user
		    		[process.StartInfo.Password] = securepass
	

		    		process.Start()
	

		    		Dim output As String = [process.StandardOutput.ReadToEnd()]
		    		[Console.WriteLine(output)]
	
End Sub 'ExecuteProcess


Problem Example

The following example executes simple execution of a process within the user context defined by the running executable. An example

		 	' Start my process with a given argument
	
Process.Start("MyProc.exe", "MyArg")

* Parent application must run with the same level of privileges as the executed process
* Process path is not fully specified, rendering the Process.Start call more vulnerable to trojan execution

Other problem practices not shown in this example:
* Application makes use of a different user context to run the process, but stores the credentials in clear text in a file or database


Test Case

The following classes must be included in any project making use of the sample code provided above:

Imports System.Diagnostics
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.Security
Imports System.Security.AccessControl
Imports System.Security.Cryptography
Imports System.Text
Imports Microsoft.Win32

Run the following code to store a set of credentials in the registry using the DPAPI and, in conjunction with the solution method, execute a command as a given user. The example test case executes the "whoami" command.

Sub Main(ByVal args() As String)
		    		' prompt for credentials and store in the registry using DPAPI
		    		[StoreUserCreds()]
	

		    		' retrieve the credentials from the DPAPI and run a process
		    		ExecuteProcess("whoami.exe")
	
End Sub 'Main

' This method provides a mechanism for storing credentials
' for a runtime process in the registry using the DPAPI.
Sub StoreUserCreds()
		    		' Obtain username and password for the account
		    		Console.Write("Enter user name for process execution: ")
		    		Dim user As String = [Console.ReadLine()]
		    		Console.Write("Enter password for this user name: ")
		    		Dim pass As String = [Console.ReadLine()]
	

		    		' Convert the username and passwords to byte arrays and encrypt 
		    		' the data by using the DPAPI [ProtectedData] class. 
		    		Dim encryptedUser As Byte() = [ProtectedData.Protect(UnicodeEncoding.ASCII.GetBytes(user),] Nothing, [DataProtectionScope.CurrentUser)]
		    		Dim encryptedPass As Byte() = [ProtectedData.Protect(UnicodeEncoding.ASCII.GetBytes(pass),] Nothing, [DataProtectionScope.CurrentUser)]
	

		    		' Create a security context for a new key that we will use to store 
		    		' the credentials that will restrict access to only the application user.
		    		Dim userEnv As String = [Environment.UserDomainName] + "\" + [Environment.UserName]
		    		Dim security As New [RegistrySecurity()]
		    		Dim rule As New [RegistryAccessRule(userEnv,] [RegistryRights.FullControl,] 				[InheritanceFlags.ContainerInherit,] [PropagationFlags.None,] [AccessControlType.Allow)]
		    		[security.AddAccessRule(rule)]
	

		    		' Create a new registry key and apply the security context 
		    		Registry.CurrentUser.CreateSubKey("ProcessExecCreds", 						[RegistryKeyPermissionCheck.ReadWriteSubTree,] security)
	

		    		' Write the encrypted credentials into the registry
		    		Registry.SetValue("HKEY_CURRENT_USER\ProcessExecCreds", "User", encryptedUser)
		    		Registry.SetValue("HKEY_CURRENT_USER\ProcessExecCreds", "Pass", encryptedPass)
	
End Sub 'StoreUserCreds


Expected Result

The following output was obtaining by running the test case as "vm-win2003\Administrator"

Enter user name for process execution: joeuser
Enter password for this user name: joeuser1234
vm-win2003\joeuser

More Information

The code makes use of the CurrentUser scope instead of the MachineKey scope so that other, potentially malicious applications cannot access this key in the registry and decrypt. This provides additional security but sacrifices interoperability with other applications. Consider using the MachineKey scope if you have a need to share credential data between applications.


Additional Resources

* User Impersonation (.NET): http://msdn.microsoft.com/library/en-us/dnpag2/html/PAGHT000023.asp
* Process Class Reference: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemdiagnosticsprocessclassstarttopic.asp


Attributes

* Applies To: .NET Framework 2.0, VB
* Category: I/O
* Author: Jonathan Bailey




Return to HomePage
Microsoft Communities