Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to store and retrieve a password in a file from a batch script?

@ECHO OFF
DFC.EXE get /isfrozen

IF Errorlevel 1 GOTO Frozen
IF Errorlevel 0 GOTO Thawed

:Frozen
Echo Errorlevel 1 Computer is Frozen, Thawing Now
DFC.EXE fakepassword123 /BOOTTHAWED
Echo
Goto END

:Thawed
Echo Errorlevel 0 Computer is Not Frozen
Echo
Goto END

:END
Exit

I am trying to run a very simple batch file but I don't want the password stored in plain text if someone were to edit it.

Right now the DFC.exe password line is where the password is stored.

Is there a way to hide this password in another file and call it from there?

like image 928
Riley Avatar asked Dec 21 '19 01:12

Riley


People also ask

How do you give credentials in a batch script that copies files to a network location?

Try using the net use command in your script to map the share first, because you can provide it credentials. Then, your copy command should use those credentials.

How do I make a non editable batch file?

GUI Method. Right click on the file, you want to make it Read-only. Click on the Properties tab from the context menu. Click the Read-only check box belong to Attributes from on the General tab from the popped up Properties window.

How do I extract a batch file?

To open the BAT file in Notepad, right-click it and choose Show more options > Edit from the menu (or just Edit in some Windows versions). You might find it helpful to use more advanced text editors that support syntax highlighting when editing a BAT file.

How to store a password in a batch file?

A few days ago I stumbled upon a more secure way to store a Password using a batch file. When it encodes the letters even the same letters aren't the same 40 digit string making it more secure. set /p filename=Enter the File name you wish to save the Text under (Eg. Text): Echo Encrypting... echo Encrypting complete!

Can I store a password inside a script?

The main rule is: never store the password inside the script. If the script will run only manually (someone executing the script), do not store the password on the script, instead, make the script ask the password every time it's executed. Create a specific user for the purpose you need. ensure the user has the minimum permissions as possible.

How to make a script ask for password every time it runs?

If the script will run only manually (someone executing the script), do not store the password on the script, instead, make the script ask the password every time it's executed. Create a specific user for the purpose you need. ensure the user has the minimum permissions as possible.

How to securely store passwords in PowerShell?

Not to worry – as always powershell has a module to save our skins. Here’s how you store your passwords securely and retrieve them when needed: Powershell thankfully has the ConvertFrom-SecureString module to convert any text to a secure string.


1 Answers

You can use PowerShell to store the password on disk in a way that (by default) is only retrievable by the currently executing user on the computer it was created on. If you can't write your entire script in PowerShell, you can at least do this by calling powershell.exe during your script.

If you want to use the credential from a single machine and user

To create the credential file (this will prompt you for the credential, the username doesn't matter in this case but must be provided):

powershell.exe -c "Get-Credential | Export-CliXml cred.xml"

Export-CliXml is a special cmdlet that serializes a PowerShell object to disk, and with some edge caveats (like don't try doing this with a COM object and expect it to work), can usually be used to read the object back into another session as we do below. And to read it in from your script to a variable and use it in your command:

for /f %i in ('powershell.exe -c "( Import-CliXml cred.xml ).GetNetworkCredential().Password"') do set PASSWORD=%i
DFC.EXE %PASSWORD% /BOOTTHAWED

We need to obtain the network credential so we can get a usable password as part of that command. If you do find yourself needing the username as well in other commands, you can also get that in a similar fashion:

for /f %i in ('powershell.exe -c "( Import-CliXml cred.xml ).UserName"') do set USERNAME=%i

In the case of the username, it is not encrypted so you don't need to return the network credential to obtain a useable username.

If you want to use the credential from multiple machines and users

Above, we leverage Get-Credential to create the initial credential for its simplicity, but it's not strictly necessary to build a credential object with it.

If you need to decrypt the credential from multiple machines or users, it's a little more complicated. You won't be able to leverage Get-Credential and will have to build the credential yourself, and store only the password in a file (you could store the username as well but for this case).

Preparing the secret

To prepare the credential, first we'll need to create a key file. This is more complex than being able to use Get-Credential but you only have to do these steps when you generate the keyfile or password file the first time. It's best to do this step from powershell.exe:

$keyFile = "C:\path\to\keyfile.key"

# you can adjust this number for different levels of AES encryption
# 32 = 256 bits
# 24 = 192 bits
# 16 = 128 bits
$key = New-Object Byte[] 32
[Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($key)

# Save the key file to disk
$key | Out-File $keyFile

Now that we have the key file, we can store your password to disk using it (remember, this time we are just storing the password in the file, not a PSCredential object). Once again, this is best done from powershell.exe like with the previous step:

# Save your password in a file so you don't have it plaintext in your history
$insecurePassFile = "C:\path\to\insecurePassFile.txt"

# This will be the encrypted password outfile
$securePassFile = "C:\path\to\securePassFile.txt"

# This can also be a UNC path if on a share
$keyFile = "C:\path\to\keyfile.key"

# Read the key in
$key = Get-Content $keyFile

# Read the plaintext password in and convert it to a secure string using our key
$password = Get-Content $insecurePassFile | Select-Object -First 1 | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString -key $key

# Write the encrypted password out to a file to be read in later using our key
$password | Out-File $securePassFile

Now you will have your encrypted password file saved to $securePassFile. At this point you can copy your password file and key to a location on the network.

Using the credential in your batch script

Now we're going back to batch world. To read the password in, you'll need to know the location of the password file and key file, and have permissions to access both of these. I apologize, but this is going to be a long PowerShell command to fit on one line:

for /f %i in ('powershell.exe -c "$key = Get-Content \\server.domain.tld\share\path\to\keyfile.key; [System.Net.NetworkCredential]::new("", ( Get-Content \\server.domain.tld\share\path\to\password.txt | ConvertTo-SecureString -Key $key ) ).Password"') do set PASSWORD=%i

That was a mouthful, so let's break it down:

  1. The batch for loop is the "magic" required to set a variable to a command's output. Eventually sets the PASSWORD variable to the output of the PowerShell command. IMO the people who wrote the command prompt are masochists XD.
  2. This begins the actual decryption process. First we need to read the $key from the key file. Without this we can't decrypt our password from the password file.
  3. We create a new network credential that we can extract the password from by creating a new System.Net.NetworkCredential object. The first parameter is the username (which we don't need here, an empty string works) and the second is the SecureString password:
  4. For the password argument, we read the password from the file and decrypt it to a proper SecureString using the key from our key file.
  5. From the resulting NetworkCredential object we are able to read the Password property which is a usable password we can return.
  6. As part of the original batch for loop, PASSWORD is set to the output of the previous PowerShell command, which in this case is the Password property of the NetworkCredential we built.

Treat your key like any other sensitive password

If you provided your own key, make sure to store your key somewhere secure. Only the users and machines that should have access to it should be able to read it. Ideally, credentials and secrets should be stored and retrieved from a secrets vault (e.g. Hashicorp Vault, Keepass, etc.) but file ACLs can be used to control who can access this information as well.


Do note that when the account password changes, you will have to regenerate cred.xml if you are relying on the default encryption behavior.

like image 136
Bender the Greatest Avatar answered Oct 06 '22 01:10

Bender the Greatest