Get SecureString as a Plain Text Parameter - powershell

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"

Related

How to pass a variable having a dollar sign to another variable in PowerShell?

I am using an On-Prem Server to run VSTS Build/Releases.
Currently, I am trying to pass a variable from VSTS: $(password) to my script.
Suppose the value of this variable $(password) is 'stringwith$sign'`
This variable $(password) needs to be injected into a string in my script:
$string = "I need to inject my password string from VSTS here "$(password)""
The String should look like this:
$string = I need to inject my password string from VSTS here "stringwith$sign"
How do I achieve this? The build/release will fail if I simply add it as $(password) since it thinks $sign in "stringwith$sign" is a variable. I cannot even use '' quotes since my variable $(password) needs to be inserted in $string.
Without seeing any sample code, it's a bit hard to tell how your script works.
But basically, if you are setting a string literal that contains special characters, you can stop them from being parsed by using single quotes instead of double-quotes. For example, if you execute
$password = "stringwith$sign"
$password
Then the value of password is stringwith.
This is because powershell has parsed the string and treated $sign as being the name of a variable and has attempted to insert the value of $sign. But as $sign hasn't been declared, the default value of empty string is used.
However, if you used single quotes, i.e.
$password = 'stringwith$sign'
$password
Then the value of password is stringwith$sign.
Subsequently, setting
$string = "I need to inject my password string from VSTS here ""$password"""
gives $string the value of
I need to inject my password string from VSTS here "stringwith$sign"
You can also use format operator:
$myVar = 'okay$dollar'
'My string with my var {0} inside' -f $myVar
or Get-Variable:
$myVar = 'okay$dollar'
"My string with my var $(Get-Variable myvar | select -expandproperty value) inside"
You just need to use the format ${env:password} instead of $(password) to get the variable password's value.
Such as if you add a PowerShell task with below script:
$string="I need to inject my password string from VSTS here ${env:test}"
echo $string
Then it will show I need to inject my password string from VSTS here stringwith$sign in the build log.

Understanding Powershell SecureStrings

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.

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.

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

Prompt for user input in PowerShell

I want to prompt the user for a series of inputs, including a password and a filename.
I have an example of using host.ui.prompt, which seems sensible, but I can't understand the return.
Is there a better way to get user input in PowerShell?
Read-Host is a simple option for getting string input from a user.
$name = Read-Host 'What is your username?'
To hide passwords you can use:
$pass = Read-Host 'What is your password?' -AsSecureString
To convert the password to plain text:
[Runtime.InteropServices.Marshal]::PtrToStringAuto(
[Runtime.InteropServices.Marshal]::SecureStringToBSTR($pass))
As for the type returned by $host.UI.Prompt(), if you run the code at the link posted in #Christian's comment, you can find out the return type by piping it to Get-Member (for example, $results | gm). The result is a Dictionary where the key is the name of a FieldDescription object used in the prompt. To access the result for the first prompt in the linked example you would type: $results['String Field'].
To access information without invoking a method, leave the parentheses off:
PS> $Host.UI.Prompt
MemberType : Method
OverloadDefinitions : {System.Collections.Generic.Dictionary[string,psobject] Pr
ompt(string caption, string message, System.Collections.Ob
jectModel.Collection[System.Management.Automation.Host.Fie
ldDescription] descriptions)}
TypeNameOfValue : System.Management.Automation.PSMethod
Value : System.Collections.Generic.Dictionary[string,psobject] Pro
mpt(string caption, string message, System.Collections.Obj
ectModel.Collection[System.Management.Automation.Host.Fiel
dDescription] descriptions)
Name : Prompt
IsInstance : True
$Host.UI.Prompt.OverloadDefinitions will give you the definition(s) of the method. Each definition displays as <Return Type> <Method Name>(<Parameters>).
Using parameter binding is definitely the way to go here. Not only is it very quick to write (just add [Parameter(Mandatory=$true)] above your mandatory parameters), but it's also the only option that you won't hate yourself for later.
More below:
[Console]::ReadLine is explicitly forbidden by the FxCop rules for PowerShell. Why? Because it only works in PowerShell.exe, not PowerShell ISE, PowerGUI, etc.
Read-Host is, quite simply, bad form. Read-Host uncontrollably stops the script to prompt the user, which means that you can never have another script that includes the script that uses Read-Host.
You're trying to ask for parameters.
You should use the [Parameter(Mandatory=$true)] attribute, and correct typing, to ask for the parameters.
If you use this on a [SecureString], it will prompt for a password field. If you use this on a Credential type, ([Management.Automation.PSCredential]), the credentials dialog will pop up, if the parameter isn't there. A string will just become a plain old text box. If you add a HelpMessage to the parameter attribute (that is, [Parameter(Mandatory = $true, HelpMessage = 'New User Credentials')]) then it will become help text for the prompt.
Place this at the top of your script. It will cause the script to prompt the user for a password. The resulting password can then be used elsewhere in your script via $pw.
Param(
[Parameter(Mandatory=$true, Position=0, HelpMessage="Password?")]
[SecureString]$password
)
$pw = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($password))
If you want to debug and see the value of the password you just read, use:
write-host $pw
As an alternative, you could add it as a script parameter for input as part of script execution
param(
[Parameter(Mandatory = $True,valueFromPipeline=$true)][String] $value1,
[Parameter(Mandatory = $True,valueFromPipeline=$true)][String] $value2
)