Consecutive SecureString hashes don't match - powershell

An encrypted password is stored in my .ps1 script. When setting up the script in a new environment, user has to manually configure this password at the beginning of the script. I've set up a switch parameter so the user can run the script that way and it will give the user the required hash. However, when prompting to input the password twice to make sure there are no typos - or such - in it I cannot get the password hashes to match. To demonstrate, I can manually enter this in PowerShell twice in a row and get different results entering the exact same password:
Read-Host -AsSecureString 'Enter password' | ConvertFrom-SecureString
This is with or without -Key or -SecureKey parameter. How can I prompt the user for password (twice to make sure they match) and if/when they do match, output the password hash?

This isn't a very pretty solution, but does the job decrypting the given passwords and then comparing them:
if ([Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringtoBSTR(($enterpw | ConvertTo-SecureString -Key $key))) -ne [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringtoBSTR(($enterpw2 | ConvertTo-SecureString -Key $key)))) {
Write-Host "Given passwords do not match"
Break
}

Related

Is there a way to use ConvertFrom-SecureString and ConvertTo-SecureString with any user?

I'm using the following to create the password file:
$path = "C:\Users\USER\Desktop"
$passwd = Read-Host "enter desired password" -AsSecureString
$encpwd = ConvertFrom-SecureString $passwd
$encpwd > $path\filename.bin
Then calling the file with:
# define path to store password and input password
$path = "C:\Users\USER\Desktop"
# get the encrypted password from the path
$encpwd = Get-Content $path\filename.bin
# convert file to secure string
$passwd = ConvertTo-SecureString $encpwd
# define needed credential
$cred = new-object System.Management.Automation.PSCredential 'WIN-SERVER\AdminForQB',$passwd
# go to DVD drive launch setup.exe as user with privileges to launch the program with no user input required
Set-Location "C:\Program Files (x86)\Intuit\QuickBooks 2017\"
Start-Process PowerShell -windowstyle hidden -Cred $cred -ArgumentList .\QBW32PremierAccountant.exe
My goal is be able to run QB2017 with admin privs without giving the user admin privs. The issue I am having is that I have to generate the password file on each user or I get the following error if a user trys to use one generated from another user:
Key not valid for use in specified state.
Is there a way to use this method without generating a key per user?
When you use ConvertTo-SecureString and ConvertFrom-SecureString without a Key or SecureKey, Powershell will use Windows Data Protection API (DPAPI) to encrypt/decrypt your strings. This means that it will only work for the same user on the same computer.
When you use a Key/SecureKey, the Advanced Encryption Standard (AES) encryption algorithm is used. You are able to use the stored credential from any machine with any user so long as you know the AES Key that was used.
More info on this topic (with example code)
Note: This is only Security through obscurity, which isn't good practice. If you store the Key on the same computer as the encrypted password, it's not much more secure than plain text passwords!
PowerShell is the wrong solution for this.
To fix this you should give them read/write access to the folders and registry key QB2017 needs access to. If you still have issues, create a Shim using Microsoft's Application Compatibly Toolkit.
https://deployhappiness.com/fixing-applications-that-require-administrator-rights/

Prevent PowerShell script from being read

I have the below PowerShell script (myscript.ps1) in which I ask for username and password. Depending on the username and password it copies a file to a certain destination.
$credentials = Get-Credential
if ($credentials.Username -eq "user1" -And $credentials.GetNetworkCredential().password -eq "pass1")
{ Copy-Item "test1.pdf" "\test\test1.pdf"; }
else
{ Copy-Item "test2.pdf" "\test\test2.pdf"; }
Requirement: I want to make this file protected so no one can edit it and see the username and password.
PS2EXE
I found a solution found here which converts the PowerShell script to an .exe file. When I originally run the script using PowerShell a dialog box appears allowing me to enter the username and password:
After the .exe is generated and when I run it the credentials dialog box no longer appears. Instead, the console appears saying "Credential:"
I don't know why? I want the credentials form to still appear when running the exe. Any thoughts please?
Q: Why does the EXE prompt with "Credential"?
This isn't an answer to the real question, and is based on guessing/supposition about PS2EXE, but I hope it is useful to clear up some confusion.
Having looked briefly at the PS2EXE page linked above, it seems that this utility encodes the script in Base64 and bundles it with a lightweight (?) custom PowerShell host. When run, I suppose the EXE starts the host, decodes the script and runs it.
The problem is that the Get-Credential cmdlet is running within a PS host that probably can't interact with the desktop. That is, it can't put up the GUI prompt for credentials. It therefore needs to prompt for the Credential property on the command line, explaining why you see that behaviour.
Workaround with Read-Host?
Instead of trying to use Get-Credential to prompt for username and password, you could embrace what PS2EXE seems to be doing and just use Read-Host:
$UserName = Read-Host "Enter username"
$Password = Read-Host "Enter password" -AsSecureString
$Credentials = New-Object System.Management.Automation.PSCredential $UserName,$Password
if ($credentials.Username -eq "user1" -And $credentials.GetNetworkCredential().password -eq "pass1")
{ ... }
Using -AsSecureString will hide the password on the screen. The $Password variable will be of type System.Security.SecureString, which can be used to create a PSCredential object as shown.
You'd need to test this, but it seems that you're able to read from the shell but not from a GUI prompt.
And just to be clear: none of this is anywhere near best-practice security. If you need authentication/authorization for these activities, step back and look at the problem again.
Workaround with two scripts?
It seems that PS2EXE doesn't support -AsSecureString in the same way that normal PowerShell does, i.e. it doesn't hide the characters. A possible workaround for this would be to collect the username and password from the user in one script and then pass them to a PS2EXE-converted script for processing.
Launch-MyScript.ps1:
$Credentials = Get-Credential
& MyScript.exe $Credentials.Username $Credentials.Password
MyScript.exe (coverted with PS2EXE):
param($Username,$Password)
$Credentials = New-Object System.Management.Automation.PSCredential $Username,$Password
if ($Credentials.Username -eq "user1" -and
$Credentials.GetNetworkCredential().password -eq "pass1")
{
...
}
The user runs Launch-MyScript.ps1 and completes the password prompt. Then the EXE is run automatically with the username and password passed in as arguments. Note that, as shown above, the password is a Secure String. Test this; I'm not using PS2EXE so it's a theoretical solution at the moment.
If you can't pass $Password along the pipeline as a Secure String object, you can convert it to text with ConvertFrom-SecureString in the first script, then conver it back with ConvertTo-SecureString in the second one.
According to this article http://windowsitpro.com/powershell/protect-your-powershell-scripts you should first set ur execution policy to AllSigned by Set-ExecutionPolicy AllSigned, then create a certificate using makecert cmdlet.
Then u can sign single script using Set-AuthenticodeSignature cmdlet or use .pfx File to Sign a Script which appears even safer.
Hope it helps a bit.

Powershell Automation - Passing Password after executing a exe file

I working on automating the specific task using powershell and getting error while passing the password. Below is my task. Below are the tasks I need to automate.
Execute exe file (For ex: export.exe)
It will prompt for password twice (Enter your password and Reenter your password)
After Entering our password Twice, It will ask for confirmarion Yes or No: (I need to give 'Yes' or No to continue )
I tried automating the above first two steps. First I get the stored password in a file using the below command
$password = get-content C:\cred.txt | convertto-securestring
Then I tried executing the below commands in script
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $pass
Invoke-Expression "& `"C:\Program Files\XX\XX\bin\export.exe`""
But I dont know how to use the password from the file twice to continue the script. I am new to powershell. Please help me.
I don't think that your application will accept PowerShell's PSCredential object. More likely, you just have to pass plaintext password twice and then Yes.
Try this (assuming that export.exe is console application):
'Password', 'Password', 'Yes' | & 'C:\Program Files\XX\XX\bin\export.exe'
This will send 3 strings, separated by the newline (Enter) to the export.exe's stdin.

Entering a password for DomainCredential in Rename-Computer in Powershell

I am trying to use a Powershell script to rename computers based on their serial numbers.
So far I have -
Set-ExecutionPolicy -Unrestricted
$name = (Get-WmiObject win32_bios).SerialNumber.Trim()
Rename-Computer -NewName $name -DomainCredential \
I don't want this to prompt because I have a few thousand system to image and I would like this to just do the rename silently and then the MDT 2012 Update 1 will do the reboot.
I am a n00b when it comes to powershell and to scripting and I have spent the better part of a week trying to figure this out. I can get the rename to work locally with no problems but I am hoping I can get some help doing it silently.
I guess my question is, how do I put a password in my powershell script so I don't have to enter it at the workstation?
I'm a little late, but couldn't you do: OSDComputerName=%serialnumber% in your task sequence rules since you're using MDT?
When the task sequence runs it should just set the computer name to that serial number. If you had a prefix or suffix like OSDComputerName=WS%SerialNumber%
You can use the Get-WmiObject command let for the same and pass a password stored in a variable like below
(Get-WmiObject win32_computersystem).Rename( $NewName,$passwd,'domain\username')
to store the password in variable, you can do something like
$key = 1..32 | ForEach-Object { Get-Random -Maximum 256 }
$passwd = Read-Host "Enter password" -AsSecureString
$encpwd = ConvertFrom-SecureString $passwd -Key $key
$encpwd
This examples are taken from this post how to pass credentials to rename command?
go through this post; it explains both secure and non-secure way of storing the password in great detail.

How can I use powershell's read-host function to accept a password for an external service?

I have a script I'm writing that makes a connection to a SOAP service. After the connection is made, I need to pass in a the username/pass with every command I send. The problem I have is that when I use read-host to do this, my password is shown in cleartext and remains in the shell:
PS C:\Users\Egr> Read-Host "Enter Pass"
Enter Pass: MyPassword
MyPassword
If I hide it with -AsSecureString, the value can no longer be passed to the service because it is now a System.Security.SecureString object:
PS C:\Users\gross> Read-Host "Enter Pass" -AsSecureString
Enter Pass: **********
System.Security.SecureString
When I pass this, it does not work. I don't care about the passwords being passed to the service in cleartext, I just don't want them sticking around on a user's shell after they enter their password. Is it possible to hide the Read-Host input, but still have the password stored as cleartext? If not, is there a way I can pass the System.Security.SecureString object as cleartext?
Thanks
$Password is a Securestring, and this will return the plain text password.
[Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password))
You can save the password(input) as a variable and pass it to your service. If the code is run in a script or as a function, the variable containing the password will be deleted after it's done(they are stored in a temp. local scope). If you run the commands in the console(or dot-source the script like . .\myscript.ps1), the password variable will stay in the session scope, and they will be stored until you delete it or close the session. If you want to be sure the variable is removed after your script is run, you can delete it yourself. Like this:
#Get password in cleartext and store in $password variable
$password = Read-Host "Enter Pass"
#run code that needs password stored in $password
#Delete password
Remove-Variable password
To read more about how variables are stored in scopes, check out about_Scopes
There is a way to do this in PowerShell versions 6.x+:
$password = read-host -maskinput "Enter password"
, thanks to jborean93 for pointing this out to me.