ConvertTo-SecureString gives different experience

2019-06-04 05:26发布

问题:

I am running into an issue creating a Credential Object from an XML File on a remote server. Here is the code I am using to test

XML File

<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
  <Obj RefId="0">
     <TN RefId="0">
        <T>Selected.System.Management.Automation.PSCredential</T>
        <T>System.Management.Automation.PSCustomObject</T>
        <T>System.Object</T>
     </TN>
     <MS>
        <S N="UserName">domain\username</S>
        <S N="Password">01000000d08c9ddf0115d1118c7a00c04fc297eb010000001f19c6a42b9b0d48af2c531892e737ce000000000200000000001066000000010000200000006fb8862fbaea7b83cd2bcab35d7a8c8b4d71b7764c2a91d68eb3873864bc9d83000000000e8000000002000020000000fcbcc5552c3eb40ec337594f8286b08780709c1ac583d4679dcd7a3f5a92441b20000000c8e274811ed7a411b6741b2c65a67363f6aef380e684d13218d1ecc1281dfdb940000000c7279e81e21a1e57eed7da61e969f34fe2adf3d7e534bb5e10b89902adf4fdf20a69ec7e9b9e56dab512c789043a3b2cf0611e3b4893658b7c20f7892ce0ddfd</S>
</MS>

PowerShell Code

    $cred = Import-Clixml "Payload\DeploymentCredential.xml"
    write-host $cred
    $cred.Password = ConvertTo-SecureString $cred.Password 
    write-host $cred.Password
    $Credential = New-Object System.Management.Automation.PsCredential($cred.UserName, $cred.Password)
    write-host $Credential.GetNetworkCredential().password

On one server (my local machine) it works completely fine, but on the remote server, I get this error

Key not valid for use in specified state.    
+ CategoryInfo          : InvalidArgument: (:) [ConvertTo-SecureString], CryptographicException
+ FullyQualifiedErrorId : ImportSecureString_InvalidArgument_CryptographicError,Microsoft.PowerShell.Commands.ConvertToSecureStringCommand

Both have the same version of PowerShell (3.0 Build -1 Revision -1), so I am not sure what the issue is.

回答1:

The issue is how the original credential is created before being exported to xml.

When you use the command ConvertTo-SecureString it encrypts the plaintext password with the encryption key on the local machine, under your user account. This means that if you export it to xml, you can only use it on that same local machine.

The minute you copy the xml file to another machine and try to import the credential object, it won't work because it will be trying to decrypt it with it's local keys which don't match. (hence the error message). This is an important security measure as it prevents me from copying the file and using it on another computer.

If you need to have the user account on another computer to run something, then there is two options:

  1. (Most secure) Create the credential object on each remote computer that you need it. This way it will use the local encryption keys and will prevent people from being able to steal the account.
  2. (Least secure) When you create the credential with ConvertTo-SecureString you can specify the -Key or -SecureKey parameter. This way instead of using the local encryption keys, it will use the one you specify. Then in your script, you provide the same key to decrypt it. This is less secure because all I have to do is steal the credential file, and take a look inside your script (to see the key) and then I have stolen the account.

--Edit--

Here is an example of how to use a shared key. It is literally only one step up from writing in a plaintext password in your script, and is only used to obfuscate the password. There are many other -better- ways of running scripts on remote machines like PowerShell Remoting (See: Learn to Use Remoting in PowerShell). Or using Task Scheduler with saved credentials.

$PlainPassword = "P@ssw0rd"
$SecurePassword = $PlainPassword | ConvertTo-SecureString -AsPlainText -Force

$key = (3,4,2,3,56,34,254,222,1,1,2,23,42,54,33,233,1,34,2,7,6,5,35,43)

$SecurePasswordKey = ConvertFrom-SecureString $SecurePassword -Key $key

#Output the hash
$SecurePasswordKey

#Output
76492d1116743f0423413b16050a5345MgB8ADIAKwBZAEkALwB0ADUAZwBQAHoAbwBNAEEAUwA0AFQAagB0AGsANwBmAHcAPQA9AHwAYgA3ADgAMwBjAGIANAAzADIAZAAwADEAYQA1AGUAMwBjAGUAYgA2AGMAMQBkADcAYQA3ADMAZAA1ADQAYwA0ADMAYgBlAGEANQAyAGQANQA0AGUAYgA5AGEAMgA0AGIANwBhAGIAMQAzADAAMwAzAGEANAA4ADEANQA0AGEAMAA=

On remote machine:

$SecurePasswordKey = '76492d1116743f0423413b16050a5345MgB8ADIAKwBZAEkALwB0ADUAZwBQAHoAbwBNAEEAUwA0AFQAagB0AGsANwBmAHcAPQA9AHwAYgA3ADgAMwBjAGIANAAzADIAZAAwADEAYQA1AGUAMwBjAGUAYgA2AGMAMQBkADcAYQA3ADMAZAA1ADQAYwA0ADMAYgBlAGEANQAyAGQANQA0AGUAYgA5AGEAMgA0AGIANwBhAGIAMQAzADAAMwAzAGEANAA4ADEANQA0AGEAMAA='

$key = (3,4,2,3,56,34,254,222,1,1,2,23,42,54,33,233,1,34,2,7,6,5,35,43)

$SecurePassword = ConvertTo-SecureString -String $SecurePasswordKey -Key $key


回答2:

Here's one method for some randomness in creating the key if you choose to use the answer from HAL9256.

[byte[]]$Rand = for($var=1;$var -le 24){
    Get-Random -min 1 -max 255
    $var++
}

We create an array of bytes which is filled with 24 random numbers from 1 to 255. These numbers are not displayed and exist only when the script is run.

Then we have a key which can be used in the above answer. The value of $Rand will disappear once the script executes, or you use Remove-Variable Rand

Just be sure to save the data from $Rand to some place secure or the key used to encrypt the data is lost.