Understanding Powershell SecureStrings - powershell

today, I wanted to dig deeply into the concept of SecureString .NET and Powershell, yet I don't think, I am understanding it very well.
If I have a password and convert it to a securestring. Is it saved as I entered it? (Being both encrypted or plain text).
Now if I would pass the password as a part of a PSCredential to a PSSession: what would happen? Does PSSession run ConvertFrom-SecureString on the passed password? But then the password is being encrypted again. How does it know how to pass it to a PSSesion?

I don't fully understand your question but get the jist. This will probably be easier if you think in terms of object types (some explanation). [This link is now dead.]
"If I have a password and convert it to a securestring. Is it saved as
I entered it? (Being both encrypted or plain text)"
Your password will be plain text, and have the type [String]. This is not encrypted.
The SecureString has the type [System.Security.SecureString]. It is encrypted.
This encryption happens in memory whenever you create the SecureString.
It's important to note that a key is required to decrypt the SecureString (more on that below)
Approach 1
This creates an encrypted SecureString variable called $SecurePassword. The unencrypted password does not make it to memory.
$SecurePassword = Read-Host -Prompt "Enter password" -AsSecureString
Approach 2
This creates an unencrypted String variable $PlainPassword, then a SecureString variable.
$PlainPassword = Read-Host -Prompt "Enter password"
$SecurePassword = $PlainPassword | ConvertTo-SecureString -AsPlainText -Force
"Now if I would pass the password as a part of a PSCredential to a PSSession: what would happen?"
PSSession does not accept unencrypted passwords. To simplify you can either provide a User and be prompted for a password, or pass an object that has the type PSCredential - i.e. it is expecting a secure password.
When you a pass a PSCredential, it is already encrypted with the password as a SecureString.
But the PSSession needs to decrypt it (this part I am not sure on but assume... how else can it varify it?)
To decrypt the SecureString, the key is required. The key is normally generated and as long as both machines have the same security principle, the PSSession can complete the decryption (this part I'm sure of)
This post addresses how to create a key so that a SecureString can be decrypted when there there are different principles.

Related

Get SecureString as a Plain Text Parameter

I'm trying to get a SecureString as plain text parameter to a command line PowerShell.
I know what is the form of the secure string.
For example, the string "abc" would be a Secure String of "71289371289".
Then, I want to pass "71289371289" as a parameter to the script (Running it from command line), that would be my Secure String and then Decrypt it to a clear text to pass it to another program i'm calling from Powershell.
How would I do something like this?
Update:
I ended up using Credfile with PSCredential to persist the credentials across reboots until the script is complete.
You can convert it back to a clear text password with SecureStringToBSTR:
Param(
$securestring = (Read-Host -AsSecureString)
)
Write-Host "Encrypted Password: $(ConvertFrom-SecureString $securestring)"
$ClearText = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($securestring))
Write-Host "Original Password: $ClearText"

mapping a networkdrive in PS 2.0 with encoded credentials

I need to map drives and I have an encoded PW which I need to connect to DMZ drives.
I'm using the following code to map the drives:
[Byte[]]$key = (1..16)
$pw = Get-Content .\LocalAdminCred.txt | ConvertTo-SecureString -key $key
# some other stuff
[string]$pwencoded = ConvertFrom-SecureString $pw -Key $key
$Map = New-Object -comobject Wscript.Network
$Map.MapNetworkDrive($_.letter,$_.path,$false,$_.username,$pwencoded)
What your eyes catches first is the $pwencoded Variable - I had to use a string for PW, that's why I needed to decode the Secure.String to a normal string, otherwise i would have had a type mismatch. now the problem is that .MapNetworkDrive needs the password in plain text like 'password' and mine is an encoded key and not a plain text.
To the question: Is it possible to use a encrypted password or even better a secure string to map my network drive this way? I really don't want to use plain text.
First I wanted to do the mapping with New-PSDrive but since the script must work for PS 2.0 I can't use that because the -persist Parameter doesn't exist there.
I don't think this is the answer you're looking for but Dave Wyatt has a few blog posts about using secure strings and credentials.
... I also added an -AsPlainText switch to the ConvertFrom-SecureString command, in case you want to get the plain text back.
The only two ways I know how to map drives are to use plain text credentials. The only other way would be to run the script as an account that has permissions to the network location; that way you don't have to authenticate with a password.

ConvertTo-SecureString - use encrypted password for everyone

I stored a pw encrypted in a txt file and load it into a powershell session with:
gc .\localadmincred.txt | convertto-securestring
This doesn't work because i read that only the User that encrypted that key, can convert it to a secure string, everyone als can't decrypt the string that is in my txt.
How can I store an encrypted string in a file so that everyone can use it with convertto-securestring?
You can encrypt the string with a key. If another user has the key, he/she will be able to decrypt the string.
See example: Secure Password with PowerShell: Encrypting Credentials – Part 2

Store Password as Multiple Variables

I've run into a stumbling block for a script I'm writing and I was hoping someone more knowledgeable might be able to help me out.
To put it simply, I want a user to be able to input their password and each letter of that password be assign to variables.
$uCcuGUBIJnoORUWA = "a"
$LjN6WLzWVAaM4BQN = "b"
$5qJ79dPkDGNeIsVy = "c"
… etc.
Once this is done, the password is send to a text file and outputted as the variables. So if your password was "abc" then the text file would appear as …
HASH1 = $uCcuGUBIJnoORUWA
HASH2 = $LjN6WLzWVAaM4BQN
HASH3 = $5qJ79dPkDGNeIsVy
… and so on.
Once the password is completely written and is stored on the text file, the rest of the script uses that information to match each piece of code to figure out what the password is. It then would then type out the password using something like:
[System.Windows.Forms.SendKeys]::SendWait("$uCcuGUBIJnoORUWA$LjN6WLzWVAaM4BQN$5qJ79dPkDGNeIsVy")
Right now my script is using a hard-coded password, which is less than ideal. I'm using PS2EXE to convert the .ps1 file to an .exe file so it's not in plain-text.
I understand how to store the variables and how to get the script to output the variables as the actual letters, I'm just having some trouble figuring out a way for the user to input the password and then have it stored.
How about something better than obfuscation: actual encryption?
Store the credentials in a [PSCredential] object. The password portion of this is stored as a secure string, which is already a good thing.
To export it to a file:
$cred | Export-Clixml -Path C:\master.xml
To re-import it:
$cred = Import-Clixml -Path C:\master.xml
The important thing is that the password will be encrypted when it's written out to disk. It can only be decrypted (and therefore reimported) by the same user on the same computer.
As a result I like to make the user and computer name part of the file name. Here's an example of a script to store the credentials:
$cred = Get-Credential
$cred | Export-Clixml -Path "C:\whatever\master_$env:COMPUTERNAME-$env:USERNAME.xml"
In the script where you actually want to use it:
$cred = Import-Clixml -Path "C:\whatever\master_$env:COMPUTERNAME-$env:USERNAME.xml"
It's quite useful. By embedding the user and computer in the file name you can store multiple copies of the credentials depending on how many users need to access the creds on however many computers.
And since you might need the raw password in your script, the way to get that out of a [PSCredential] object is like so:
$cred.GetNetworkCredential().Password

Store encrypted string in script

I need to store some credentials for an SMTP server in my script, but naturally I don't want to password to be stored in plaintext.
So far I've been encrypting the password like this:
"password" | ConvertTo-SecureString -AsPlainText -Force |
ConvertFrom-SecureString
and using it in a script like this:
$password = "(the long string generated from above command)"
$username = "Test#testdomain.com"
$cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username,($password | ConvertTo-SecureString)
However, when generating the $cred object I get the following error:
ConvertTo-SecureString : Key not valid for use in specified state.
Remove the ConvertFrom-SecureString and Reload it like this:
$Password = "password" | ConvertTo-SecureString -AsPlainText -Force
$username = "Test#testdomain.com"
$cred = New-Object System.Management.Automation.PsCredential($username, $Password)
Maybe Protect-String with Unprotect-String in Carbon module is what you are looking for. I use it all the time and works beautifully.
Carbon is a PowerShell module for automating the configuration of computers running Windows 7, 8, 2008, and 2012.
PLEASE READ the following carefully and think through the points before you decide this does not help the OP with his problem.
Encrypting a password using PS or .NET mechanisms is a case of "security through obscurity" unless one has a robust method to store the key. (The usual "robust methods" are "what I know", "what I am" (biometrics), and "what I have".) But obscurity is the level of security that the OP seems to be asking for ("naturally I don't want to password to be stored in plaintext"). And there's nothing wrong with that. But unlike cases where some security-through-obscurity is obtained because code is compiled, that's not the case w PS.
However, the security depends on who an "attacker" is considered to be. If you are dealing with a password that the current user generates, then the current user isn't an attacker. However it you are dealing with a password set by some other entity, but you need the user to use it, but not see it, then the current user is a potential attacker. (An example of this case: a script to join a workstation to a domain would need a user with right set of domain permissions. You might want the user to be able to join the domain as part of imaging/re-imaging his desktop, but you don't want his domain user account to have rights to join the domain, so your script uses a different set of credentials you don't want the user to know). I'm guessing the OP is asking about a case where the password is not assigned by the user. The rest of this answer addresses this case.
Using PS/.NET methods to encrypt/obtain password, is "security through obscurity" because an attacker only needs to put a breakpoint just before where password is used. With a variable name like $password it'll be easy to find a location to set the breakpoint. Obscuring the variable name (eg call the password variable $exectionContext which is a misspelling of a PS auto-variable) won't do much if 1) the script is short and/or 2) it's obvious what command requires the password.
So, instead of encrypting the password in what amounts to a fairly transparent and easy to reverse scheme, you can get what is arguably better security by just being tricky in setting $password (or whatever you call the var) to it's ultimate value. For example if the password is "join-thE_domain" you could do something like:
...other script code...
$windowTitle="Install/deinstaller joint script"
...other script code...
$paramName = "-th"
...other script code...
$status = "End main"
...other script code...
$subTitle = $windowTitle.substring(20,4)
...other script code...
$count = 32
...other script code...
# Use a regex in the following to make it more obscure
$fixedStatus = $status -replace "n","_" -replace "o",[string][char]$count
...other script code...
# If cmdlet/cmd doesn't support password as a positional parameter then
# write a function that calls cmdlet/cmd and takes password as positional parm
cmdlet-that-needs-password "arg value 1" ("$subTitle$paramName"+$fixedStatus) "arg value 3"
...other script code...
# extra code at the bottom is important to keep someone from
# just scrolling to bottom of script to see password being used
# This extra code could be just dummy code that doesn't really do anything
# Extra code can be placed throughout the script to make it more obscure
...other script code...