Store Password as Multiple Variables - powershell

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

Related

Force Powershell to create AD user folder when setting HomeDirectory value

During the New-ADUser command: -HomeDirectory = '\sharedrive\folder$%username%'
When this value is set in Powershell, the value appears as \\sharedrive\usersfolder$\%username% under the Home Directory.
Result:
However, if I manually enter that same string into the Active Directory Users and Computers menu and then hit apply, it autofills to the person's username where %username% exists and creates a folder on that share drive with proper permissions, shown below:
How can I get Powershell to autofill this username and create the folder? Just writing the string out with the user's name does not work.
I think there are a couple of problems here. PowerShell doesn't know how to interpret your input string '\sharedrive\folder$%username%'
For one, it is a literal string - so even if PowerShell knew that $%username% meant "convert this value to the username environment variable" it would still interpret it as the literal characters entered in the string. What you're appearing to try and do is string interpolation which requires that you use double quotes ".
Second, the %username% portion is not the way that you use environment variables in PowerShell. There are a few ways to get this value. $env:USERNAME should work on windows, so would [Environment]::username.
So you would want to do something like this "\sharedrive\folder$([Environment]::username)"
for a user 'someuser' this would return "\sharedrive\foldersomeuser"
You're confusing both environment variables AND how these values are set in ADUC and when using Powershell cmdlets.
%username% is a Windows environment variable which can be translated, thus when you hit the "Apply" button the ADUC console translates the username from the account you're creating, then in the background goes and creates that folder using the credentials of whoever is running ADUC, then (if you hit the "Yes" button) also repermissions it to give that user Full Control.
Once again its the ADUC which creates the folder for you, it doesn't happen just because you set those properties on the account object. If you want Powershell to do this then you need to carry out the folder creation and permissioning yourself, either manually or via more Powershell scripting.
Try something like this:
$NewUser = Read-Host "please enter a username"
New-ADUser -Name "$NewUser" -HomeDirectory "\sharedrive\folder\$NewUser"
Btw Efie is absolutely right about how environment variables are being passed, but the other thing is that those variables he mentioned will be the variables of whomever is running the script, not the user you are creating.

Encrypt password for a Powershell script to be used by other customers

I've been searching all around the internet for a really safe solution but still nothing worth. My script expects a connection to O365 but I actually run it on my machine so I can almost store the password in a file and then decrypt it easily with my account. The problem begins when I want to - for example - make a zip with all the files and give it to a customer without the fear to get my password stolen.
The only solution I have found is to use an exact key to encrypt but, how could I "hide" it? I mean, I first encrypt using
[Byte[]] $key = (1..16)
then I go into the script and use:
$File = “path\Password.pwd"
[Byte[]] $key = (1..16)
Get-Content $File | ConvertTo-SecureString -Key $key
but if the key I used to encrypt is written in the script, they should be able also to decrypt and we go back to the main problem. Is there any solution for this problem?
Thanks in advance, hope I won't get downvoted because I really couldn't find anything.
^
Update: I edit my request because I made a few researches and I assumed that the best way to encrypt is using the default Windows Apis. Now my request is if possible to run a script in a server with different users. Maybe the user could make a "run as administrator" that would let him run it and decrypt the password without any problem... I mean the big problem is I need it working on a server for multiple users who are not all administrators. Thanks.

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