Hi,
I have a Windows service that is written in Python which runs under a local administrator account. Under various conditions it creates a New-PSSession to the local computer as another user (e.g. administrator) and exports a PSCredential for the same account. However until Internet Explorer has been run as administrator the export fails with "access is denied". The system is Windows 2012 R2.
I assume that there is simply something which isn't being initialised until after IE has been run but I am unable to work out what this is or how it might be replicated in the powershell script. After a reboot the system returns to the initial non-working state.
Before IE:
PS C:\sbin> C:\sbin\CredentialWriter.ps1 -Path C:\tmp\test.xml -ForUser .\Administrator -ForPw TheAdminPassword
Id Name PSJobTypeName State HasMoreData Location
Command
-- ---- ------------- ----- ----------- --------
-------
70 Job70 RemoteJob Completed True localhost
...
Exception calling "Protect" with "3" argument(s): "Access is denied.
" Access is denied.
Exception calling "Protect" with "3" argument(s): "Access is denied.
"
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : CryptographicException
+ PSComputerName : localhost
Access is denied.
+ CategoryInfo : NotSpecified: (:) [ConvertFrom-SecureString], CryptographicException
+ FullyQualifiedErrorId : System.Security.Cryptography.CryptographicException,Microsoft.PowerShell.Commands.ConvertFromSecureStringCommand
+ PSComputerName : localhost
After IE:
PS C:\sbin> C:\sbin\CredentialWriter.ps1 -Path C:\tmp\test.xml -ForUser .\Administrator -ForPw TheAdminPassword
Id Name PSJobTypeName State HasMoreData Location
Command
-- ---- ------------- ----- ----------- --------
-------
72 Job72 RemoteJob Completed True localhost
...
01000000d08c9ddf0115....
The script:
<#
.SYNOPSIS
Create a PSCredential file for a user account.
.DESCRIPTION
This is a script which can create a PSCredential file for any local account. It can optionally write
the credentials out for consumption by another user account account.
.PARAMETER Path
The file which will be created containing the PSCredential.
.PARAMETER ForUser
The username which the PSCredential is being created for.
.PARAMETER ForPw
The password for ForUser which will be encoded.
.PARAMETER AsUser
Write the credential out for use by this user.
.PARAMETER AsPw
The password the AsUser account.
.PARAMETER Help
Get help with this command.
.EXAMPLE
CredWriter -Path "C:\a\path\creds.xml" -ForUser ".\Administrator" -ForPw "AdminP4S$w0rd"
This will write the ForUser credential to C:\a\path\creds.xml which can be subsequently used by
the same user to automatically execute commands.
.EXAMPLE
CredWriter -Path "C:\a\path\creds.xml" -ForUser ".\Administrator" -ForPw "AdminP4S$w0rd" -AsUser ".\UnprivilegedUser" -AsPw "UnprivPw"
This will write the ForUser credential to C:\a\path\creds.xml which can subsequently be used byt the UnprivilegedUser to run
a command with the ForUser account. WARNING: There are no restrictions on what commands the credential file could be used
to execute once it is given to the untrusted user.
#>
[CmdletBinding(DefaultParameterSetName="HelpOnly")]
param(
[Parameter(ParameterSetName="ForAccount",Mandatory=$true)]
[Parameter(ParameterSetName="AsAccount",Mandatory=$true)]
[string]$Path,
[Parameter(ParameterSetName="ForAccount",Mandatory=$true)]
[Parameter(ParameterSetName="AsAccount",Mandatory=$true)]
[string]$ForUser,
[Parameter(ParameterSetName="ForAccount",Mandatory=$true)]
[Parameter(ParameterSetName="AsAccount",Mandatory=$true)]
[string]$ForPw,
[Parameter(ParameterSetName="AsAccount",Mandatory=$true)]
[string]$AsUser,
[Parameter(ParameterSetName="AsAccount",Mandatory=$true)]
[string]$AsPw,
[Parameter(ParameterSetName="HelpOnly",Mandatory=$false)]
[Parameter(ParameterSetName="ForAccount",Mandatory=$false)]
[Parameter(ParameterSetName="AsAccount",Mandatory=$false)]
[switch]$Help
)
# print help if requested
if($help -or ($PSCmdlet.ParameterSetName -eq "HelpOnly")) {
Get-Help $myInvocation.MyCommand.Path
exit
}
# If no AsUser account given then write credentials as ForUser.
if(!$AsUser) {
$AsUser = $ForUser
$AsPw = $ForPw
}
$EAsPw = ConvertTo-SecureString -String $AsPw -AsPlainText -Force
$AsCred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $AsUser, $EAsPw
try {
# Executing as a job in the current session fails as it seems that there is a restriction on
# executing the command with a nother user credential when invoked from a service. At the console
# command line there is no problem
#$job = Start-Job -InitializationScript ([ScriptBlock]::Create("Set-Location C:\sbin")) -Credential $AsCred -ScriptBlock {
# As an alternate method we create a new powershell session to the local computer and issues commands
# through this.
$s = New-PSSession -EnableNetworkAccess -Credential $AsCred -ErrorAction Stop
$job = Invoke-Command -AsJob -Session $s -ScriptBlock {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)][string]$Path,
[Parameter(Mandatory=$true)][string]$ForUser,
[Parameter(Mandatory=$true)][string]$ForPw
)
Add-Type -Assembly System.Security
# This always works
#$x = [System.Security.Cryptography.ProtectedData]::Protect( "fff".tochararray(), $null, [System.Security.Cryptography.DataProtectionScope]::LocalMachine);
# Only works after IE executed under -ForUser
$x = [System.Security.Cryptography.ProtectedData]::Protect( "fff".tochararray(), $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser);
$EForPw = ConvertTo-SecureString -String $ForPw -AsPlainText -Force
Write-Host ($EForPw | ConvertFrom-SecureString)
} -ArgumentList $Path, $ForUser, $ForPw
Wait-Job -Job $job
Write-Host ($job.ChildJobs[0].Error)
Write-Host ($job.ChildJobs[0].JobStateInfo.Reason.Message)
Write-Host ($job.ChildJobs[0].JobStateInfo.Reason)
Receive-Job -Job $job
}
catch {
Write-Host "Start-Job failed: "
$_ | Format-List * -Force
}
finally {
if($s) {
Remove-PSSession -Session $s
}
}