Thursday, April 17, 2008

Ever needed an Automated RunAs?

I went down this path for a Microsoft Deployment Toolkit (BDD) SOE build, as it was running in the wrong context for inserting details into a SQL database (computers and members of an Active Directory Group had access but the local administrator account didn't).

This powershell script accepts a number of arguments and then launches the process as the specified user;

Usage:
Start-RunAs.ps1 process Path [Arguments] [Domain] [User] [Password]

Examples;

Start-RunAs.ps1 "notepad.exe" "$($Env:SystemRoot)\System32"
Start-RunAs.ps1 "notepad.exe" "$($Env:SystemRoot)\System32" "" "domain"
"user"
Start-RunAs.ps1 "notepad.exe" "$($Env:SystemRoot)\System32" ""
"password" "user" "password"



The script could do with some tidying up for day to day use, but you get the idea...


Whilst I have some misgivings about the security of the "SecureString" it may be useful for some user's who wish to run their shell using Principle of Least Privilege...

Probably most notably though was the exercise and the way the PSH team decided to implement the SecureString concept.

It doesn't seem too secure, but as the PSH team offer, it wasn't meant to be, after all the owner of the process (interactive user) already knows the password they just typed in.


The following Powershell code snippet suggests that the password entry is secure. However, in the current session, the password can be found.

$PWD = Read-Host "Enter your password" -assecurestring[Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServi
ces.Marshal]::SecureStringToBSTR($PWD))


Running the above commands results in;
Enter your password:: ****
System.Security.SecureString
Test


Still from the angle of security by obscurity, it would stop most prying eyes.


A simpler Runas I hear you ask?

$psi = New-Object System.Diagnostics.ProcessStartInfo "notepad.exe"
$psi.Verb = "runas"
[System.Diagnostics.Process]::Start($psi)

This launches the RunAs GUI, and then the process.

ConvertTo-SecureString
http://technet.microsoft.com/en-us/library/bb978707.aspx

http://www.leeholmes.com/blog/PowerShellCredentialsAndGetNetworkCredential.aspx

Windows PowerShell Securing the Shell
http://technet.microsoft.com/en-us/magazine/cc137808.aspx


param( [string] $FileName = $(throw "Please specify a filename to execute"),
[string] $FilePath = $(throw "Please specify a path to the filename"),
[string] $Arguments,
[string] $UserDomain,
[string] $UserName,
[string] $UserPassword
)

$Scripts = Split-Path $Myinvocation.Mycommand.Path
# Start a specified process as a specific user (RunAs)
#Set Error Handling
$ErrorActionPreference = "SilentlyContinue"
$Me = "Start-RunAs"
# Ensure the supplied filename/path exists

$TaskDesc = "Verify FileName exists"
get-item $FilePath\$FileName
If ($? -eq $False) {
Write-Host " $($TaskDesc) $($Error[0])"
Exit 1
}
# When a username but no password has been supplied, request credentials from user

If ($UserName -ne "" -and $UserPassword -eq "") {
$TaskDesc = "Request User Credentials for $($UserName)"
$UserCred = Get-Credential "$($UserDomain)\$($UserName)"
If ($? -eq $True) {
$UserDomain = $UserCred.UserName.Split("\")[0]
$UserName = $UserCred.UserName.Split("\")[1]
$UserPassword = $UserCred.GetNetworkCredential().Password
} Else {
Write-Host " $($TaskDesc): $($Error[0])"
Exit 2
}
$UserPassword = $UserCred.GetNetworkCredential().Password
} Else {
# present a blank credentials window for the user to populate
$TaskDesc = "Request User Credentials"
$UserCred = Get-Credential
If ($? -eq $True) {
$UserDomain = $UserCred.UserName.Split("\")[0]
$UserName = $UserCred.UserName.Split("\")[1]
$UserPassword = $UserCred.GetNetworkCredential().Password
} Else {
Write-Host " $($TaskDesc): $($Error[0])"
Exit 2
}
}

# A local domain can also be specified
$DOMAINUSER = $UserName
$DOMAINUSERPASSWORD = $UserPassword
$DOMAINUSERDOMAIN = $UserDomain

$SleepInterval = 2

# Number of seconds to wait for process to exit
$psi = New-Object System.Diagnostics.ProcessStartInfo "$($FileName)"
# Configure other process execution parameters
$psi.UseShellExecute = $False

$psi.UserName = $DOMAINUSER
$psi.Password = ConvertTo-SecureString "$($DOMAINUSERPASSWORD)" -AsPlainText -Force
$psi.Domain = $DOMAINUSERDOMAIN
$psi.WorkingDirectory = $FilePath
# $psi.WindowStyle = "Hidden"
$psi.Arguments = $Arguments

$TaskDesc = "Execute process;

"Write-Host "$($TaskDesc)"
Write-Host "$($psi.WorkingDirectory)\$($psi.FileName) $($psi.Arguments) as user $($psi.Domain)\$($psi.UserName)"
$ProcessStart = [System.Diagnostics.Process]::Start($psi)

If ($?) {
While ($ProcessStart.HasExited -ne $TRUE) {
Start-Sleep -s $SleepInterval
}
$ProcessStart.HasExited
} Else {
Write-Host "$($TaskDesc) failed, $($Error[0])"
}

No comments: