Store encrypted string in script - powershell

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

Related

How to bringtofront a messagebox in Powershell

Just need a little help with a Powershell Script.
I have a last messagebox on my script. what i want to accomplish is bring the messagebox in front of all the windows.
cmdlet that i use is
$end=[system.Windows.Forms.Messagebox]::Show('StartUP Tool Progress Completed!','StartUP Warning')
Alternatively, if all you need is a message box you can use the Wscript Shell:
$wshell = New-Object -ComObject Wscript.Shell
$wshell.Popup("StartUP Tool Progress Completed",0,"Completed",0x0)
For more information: Popup Method
This is a WinForms question, more than a PowerShell question. You'll need to pass in Form.ActiveForm. Form.ActiveForm would give you the currently active form, even if you are raising your MessageBox from any other class.
However, I think you might want to look at Read-Host -AsSecureString or, more preferably, Get-Credential if the prompt is for confidential data.
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.
Thankfully, PowerShell has a lot of built-in help for launching dialogs. You're trying to ask for parameters.
You should use the 
[Parameter(Mandatory=$true)]
 attribute, and correct typing, to ask for the parameters. Read up on "params" if you haven't already.
If you use Parameter attribute 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.
Finally, you can try this dirty trick, leveraging Microsoft Visual basic DLLs:
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') | Out-Null
$computer = [Microsoft.VisualBasic.Interaction]::InputBox("Enter a computer name", "Computer", "$env:computername")

Powershell ISE v5 Presentations.Open Method Password Protected

I'm trying to automate opening and saving a lot of powerpoints that all have the same password. I have done this with excels already, the only point that is unclear is I couldn't find any information online about passing a password variable through presentation.open. I was able to do that with WorkBooks.open
The script looks like this"
add-type -AssemblyName microsoft.VisualBasic
add-type -AssemblyName System.Windows.Forms
$passwd = Read-Host("Type in the password:")
$objPP = new-object -comobject powerpoint.application
$objPP.visible = [Microsoft.Office.Core.MsoTriState]::msoTrue
#Paste in Path to Powerpoints
dir C:\Users\me\Desktop\cracking\*.pptx | ForEach-Object {
$doc = $objPP.presentations.open($_.FullName)
}
I'm not sure how to configure the parameter of presentation.open to pass $passwd
When I did this with excel it basically looked like this:
$doc = $objExcel.WorkBooks.Open($_.FullName,1,$false,5,"$passwd","$passwd")
Is there a way to do this with powerpoint?
OR is there a way to focus in on the password box so I can use SENDKEYs?
That may also work and I may try to go that route to do this with PDFs.
The problem I noticed is once that password box comes up my script will not continue until after I type in the password. I can't SENDKEYs to the password box.
Thanks.
The syntax for opening a password-protected presentation in VBA, assuming you know the password, is:
Presentations.Open("c:\temp\open.pptx::password::")
IIRC, no error results if you use a password on a non-passworded file; passing the wrong password results in a trappable error, so you can use that to test for password protected files even when you don't know the password ... just pass a bogus password and test for errors.

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

common ways to pass state from cmdlet to cmdlet

I am creating my own set of cmdlets. They all need the same state data (like location of DB and credentials for connecting to DB). I assume this must be a common need and wonder what the common idiom for doing this is.
the obvious one is something like
$db = my-make-dbcreds db=xxx creds=yyyy ...
my-verb1 $db | my-verb2 $db -foo 42...
my-verb8 $db bar wiz
.....
but i was wondering about other ways. Can I silently pipe the state from one to another. I know I can do this if state is the only thing I pipe but these cmdlets return data
Can I set up global variables that I use if the user doesnt specify state in the command
Passing the information state through the pipe is a little lost on me. You could update your cmdlets to return objects that the next cmdlet will accept via ValueFromPipeline. When you mentioned
like location of DB and credentials for connecting to DB
the best this I could think that you want is....
SPLATTING!
Splatting is a method of passing a collection of parameter
values to a command as unit. Windows PowerShell associates
each value in the collection with a command parameter.
In its simplest form
$params = #{
Filter = "*.txt"
Path = "C:\temp"
}
Get-ChildItem #params
Create a hashtable of parameters and values and splat them to the command. The you can edit the table as the unique call to the cmdlet would allow.
$params.Path = "C:\eventemperor"
Get-ChildItem #params
I changed the path but left the filter the same. You also dont have to have everything in $params you splat and use other parameters in the same call.
It is just a matter of populating the variables as you see fit and changing them as the case requires.
Spewing on the pipeline
Pretty that is what it is actually called. If you use advanced function parameters you can chain properties from one cmdlet to the next if you really wanted to. FWIW I think splatting is better in your case but have a look at the following.
function One{
param(
[parameter(Mandatory=$true,
ValueFromPipeline=$True,
ValueFromPipelineByPropertyName=$true)]
[String[]]
$Pastry
)
write-host "You Must Like $Pastry"
Write-Output (New-Object -TypeName PSCustomObject -Property #{Pastry= $pastry})
# If you have at least PowerShell 3.0
# [pscustomobject]#{Pastry= $pastry}
}
Simple function that writes the variable $pastry to the console but also outputs an object for the next pipe. So running the command
"Eclairs" | One | One | Out-Null
We get the following output
You Must Like Eclairs
You Must Like Eclairs
We need to pipe to Out-Null at the end else you would get this.
Pastry
------
{Eclairs}
Perhaps not the best example but you should get the idea. If you wanted to extract information between the pipe calls you could use Tee-Object.
"Eclair" | One | Tee-Object -Variable oneresults | One | Out-Null
$oneresults
Consider Parameter Default Values
Revisiting this concept after trying to find a better way to pass SQL connection information between many function working against the same database. I am not sure if this is the best thing to do but it certainly simplifies thing for me.
The basic idea is to add a rule for your cmdlet or wildcard rule if your cmdlets share a naming convention. For instance I have a series of functions that interact with our ticketing system. They all start with Get-Task.... and all configured with SQL connection information.
$invokeSQLParameters = #{
ServerInstance = "serverName"
Username = $Credentials.UserName
Password = $Credentials.GetNetworkCredential().Password
}
$PSDefaultParameterValues.Add("New-Task*:Connection",$invokeSQLParameters)
$PSDefaultParameterValues.Add("Get-Task*:Connection",$invokeSQLParameters)
So now in my functions I have a parameter called Connection that will always be populated with $invokeSQLParameters as long as the above is done before the call. I still use splatting as well
Invoke-Sqlcmd -Query $getCommentsQuery #Connection
You can read up more about this at about_parameters_default_values