Return to
HomePage
Securely Executing a New Process from a .NET Application (VB.NET)
Applies To
* .NET Framework 2.0
* VB.NET
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.
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
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