ConvertTo-SecureString Key not valid for use in specified state - powershell

I'm getting an error when running a script as a particular user, but not as my own user account. This is on Windows 2012R2. Powershell module was installed and has been in use for some time, but using another account prevents it from running. Because I have been able to run it, I feel like I can rule out any missing files or faulty commands within the script. The rest of the script is dependent on this. I also have run the convertto-securestring options using the service account that will be running the commands through the automation, but it still is not working.
This is the secure password script I ran to convert the password
$password = "password"
$secureStringPwd = $password | ConvertTo-SecureString -AsPlainText -Force
$secureStringText = $secureStringPwd | ConvertFrom-SecureString
Set-Content "E:\temp\ExportedPassword.txt" $secureStringText`
This is the error message after trying to run as user B
line 4 $pwdTxt = Get-Content "E:\temp\ExportedPassword.txt"
line 5 $securePwd = $pwdTxt | ConvertTo-SecureString
ConvertTo-SecureString : Key not valid for use in specified state.
At E:\temp\get_user_cliffsv4.ps1:5 char:24
+ $securePwd = $pwdTxt | ConvertTo-SecureString
+ ~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [ConvertTo-SecureString],
CryptographicException
+ FullyQualifiedErrorId :
ImportSecureString_InvalidArgument_CryptographicError,Microsoft.PowerShell.
Commands.ConvertToSecureStringCommand
I have run this script numerous times through automation software, but when testing I had to run the command as my personal account because of this error and to get around this so I could complete testing, I invoked that option. My question is, is the account I'm using missing any particular rights? Do I have to install the Powershell extensions under that account or can I set/change something in PS or on the server that will allow the service account to run this script? Thanks in advance!

The issue was not the service account's ability to run the command, it was that the System account was running the command, so the secure password it was passing was not usable. I found this link that provided a script that scheduled the script to run using the system account, thereby creating the file by system which will correctly used the password file and passed the credentials correctly. Thank you to Keith Francis and the site for hosting the code and always, I'm very thankful for these communities that provide an outlet for support.
https://community.spiceworks.com/scripts/show/3517-use-powershell-securestring-with-windows-system-account

Related

Powershell setting new local users forced password change

Afternoon folks,
I've got a quick question. I'm making a script to make local user accounts based off a csv file. I have it all working no problem using the New-LocalUser command. What I am curious about is there a parameter string I can add or anything to have it so the user Has to change the password upon first login?
I've looked through https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.localaccounts/new-localuser?view=powershell-5.1 I just was wondering if there was something I've missed.
You haven't. Most of the time, when cmdlets are released, they don't include all functionality for a particular technology (e.g. Get-Service versus Win32_Service). In this case, New-LocalUser, Get-LocalUser, Set-LocalUser are in this boat.
However, in order to achieve what you're after, the WinNT provider has exposed this functionality for a long time:
$u = New-LocalUser -Name test -Password ('123456789' | ConvertTo-SecureString -AsPlainText -Force)
$WinNt = [adsi]"WinNT://localhost/$($u.Name)"
$WinNt.PasswordExpired = 1
$WinNt.SetInfo()

Import Module with an different user account

Are you able to import a module through PowerShell with a different user account? I am specifically attempting to import the ActiveDirectory module with a different account to the currently logged in one.
I don't want to go all out for the console though because I am attempting to use the current Outlook process to send an email after the part of the code is done, and if the entire console is elevated it will give a COM error (instance of PowerShell and Outlook are not elevated together).
The SMTP way of sending an email or through Send-Mail won't work as even though I can ping the SMTP server, I get the below error message, which from what I've read is because I am unable to communicate with the SMTP server appropriately?
Exception calling "Send" with "1" argument(s): "Failure sending mail."
At C:\Users\\Desktop\SCRIPT.ps1:64 char:9
+ $SMTP.Send($MSG)
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : SmtpException
You can't import a module with a different account as it doesn't work this way. You need to run the individual commands themselves with alternative credentials.
As you mentioned AD I've used Get-ADUser as an example but a lot of powershell commands have a Credential or PSCredential parameter of some kind, check the documentation to find out.
$Credentials = Get-Credential
Get-ADUser JohnSmith -Properties DistinguishedName -Credential $Credentials
This above example will prompt for credentials, but you can also save them in the script instead on entering them every time.
NOTE: Saving credentials in a file isn't secure so be careful what credentials you save and where you store them!
$Username = "DomainUserName"
$Password = "PlainPassword" | ConvertTo-SecureString -AsPlainText -Force
$Credentials = New-Object System.Management.Automation.PSCredential($Username ,$Password)
There are also other ways to save credentials, but that's too much to go into here.
The AD module for powershell is a wrapper around much of the .NET framework's System.DirectoryServices namespace of code.
.NET in turn is wrapped on top of the older COM ADSI component.
Because of this, it is possible to use windows cached credentials to handle the AD work without using the -Credential option.
If you cache a Windows Domain credential prior to running the script, the AD cmdlets will use those cached credentials to authenticate to the DC. Of course, there's no requirement to remove the cached credential...but realize it's static. if the password changes in the domain, you need to re-cache the cred.
The management of domain creds can be done by command line as well using the cmdkey.exe program that is present since Win7. Using this command line tool, you could set the windows credential just before you run your script, then remove the credential after.
Note that the use of the cached creds is based solely on the server name that the cmdlet will attempt to communicate. If you are not specifying a DC in your cmdlet calls, then it will use the %logonserver% environment variable.
The critical piece then is that the servername used by ADSI must match exactly in the credential cache. If the short name (server01) is used, then that must be in the cache. If the full dns name is used (server01.domain.com), then that must be in the cache. If you feel that your script may change to another server, then that server must be in the cache.

ConvertTo-SecureString gives different experience on different servers

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.
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:
(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.
(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
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.

How to run PowerShell script from a computer to untrusted domain?

I have some PowerShell scripts to update data in active directory. Now I want to run these scripts from another domain joined computer, but the user that is currently logged in does not have admin rights to AD to run the scripts. How can I pass the credentials first to connect to domain as administrator and then run the script?
I know about the command get-credentials but I don't want any manual intervention.
There is batch file which runs the script and I want to put the credentials once.
I also don't want to show the password to the logged in user. Is there any possibility we can save the password in encrypted format?
Hope there is trust between the two domains
$Server = 'XXXXXXXXXX'
$username = 'Domain\XXXXXXXXXX'
$password = 'XXXXXXXXXX'
$securepassword = ConvertTo-SecureString $Password -AsPlainText -force
$cred = New-Object System.Management.Automation.PSCredential -ArgumentList ($username,$securepassword)
Get-ADComputer -Identity $Server -Credential $cred
You can change the entire script in to exe file using PowerGUI and use credentials to save it from being opened.
or
use the script by Brenton J.W. Blawat for encryption located at http://gallery.technet.microsoft.com/scriptcenter/PowerShell-Script-410ef9df
or
use the simple script mentioned in the below article
http://www.interworks.com/blogs/trhymer/2013/07/08/powershell-how-encrypt-and-store-credentials-securely-use-automation-script
Instead of using a batch file you could write a VBS wrapper and then use the script encoder to turn it into a VBE. The script encoder is technically not supported in Vista or 7 but it still works if you can find it somewhere. The other option would be to put all your code into a .Net EXE. Once it’s compiled it would hide the password from an ordinary user. Someone that knows what they are doing could still extract it so be aware of that. The same goes of an encoded VBE.

Use Powershell with CruiseControl.Net for secure password storage?

The CruiseControl.Net tfs integration requires a username and password for a user with rather high privileges to be stored in the configuration files. I'm not very fond of that, and have tried to think of ways to have the password stored securely and yet maintain fully automated builds.
Powershell has good support for this with the ConvertTo-SecureStringand ConvertFrom-SecureString commands, and something like this would handle the encryption:
"password-string" | ConvertTo-SecureString -AsPlainText -Force | Out-File pwd.dat
And this could handle decryption:
$user = "mydomain\myuser"
$pwd = Get-Content pwd.dat | ConvertFrom-SecureString
$cred = New-Object System.Management.Automation.PSCredential($user, $pwd)
CruiseControl.Net has a <powershell> task that can be used for running powershell scripts, but I'm unsure whether this can be made to return anyhting. I'm wondering if anyone has any ideas on how to implement secure passwords with CruiseControl.Net? Using Powershell isn't necessary, I'm just figuring it would be neat.
Found a workaround at least: if you omit the <user>,<password> and <domain> parameters, CruiseControl will use the credentials for the user running the service.
The more general question, whether it is possble to catch output remains to be tested. I guess it could be possible to set an environment variable using [system]::SetEnvironmentVariable(name,value), but I haven't tested this.