It seems these days I’m writing a lot of VBScript scripts. My scripts are often interacting with a MySQL database but could be doing other things as well, such as connecting to workstations to push files. One of the biggest problems with scripts that need credentials is how to store the password in these clear text files. I’ve come up with a reversible encryption to store the password. The script below allows you to completely encrypt or decrypt a password using a custom key.
Now I fancy myself as security aware – I am not an IT Security professional. And this script is not intended as a truly secure method of storing passwords. Nor am I suggesting it is unbreakable or otherwise hard to decode without the key and encryption methodology below. It is ONLY meant to OBSCURE otherwise clear-text passwords in VBScripts. And obscurity is not security. Clear?
On to the script…
Option Explicit Function GetParam(ParamNumber) 'Exclusive to VBS Scripts Dim GetParam_CmdArgs Set GetParam_CmdArgs = WScript.Arguments If GetParam_CmdArgs.Count = 3 Then GetParam = GetParam_CmdArgs(ParamNumber) ElseIf GetParam_CmdArgs.Count = 1 Then If UCase(Left(GetParam_CmdArgs(0), 1)) = "H" Then Help End If End Function Function ValidKey(Key) 'Ensures constants when computing character hexadecimal values don't exceed 255 (FF) Dim ValidKey_KeyParams ValidKey_KeyParams = Split(Key, "-") 'Using "-" as a delimiter makes it impossible to have a negative number If UBound(ValidKey_KeyParams) <> 2 Then 'Wrong number of parameters in key ValidKey = False Else 'Max computed value is 127. If ValidKey_KeyParams(0) + (ValidKey_KeyParams(1) * ValidKey_KeyParams(2)) > 127 Then ValidKey = False Else ValidKey = True End If End If End Function Function DecryptPassword(UseHash, Key) Dim DecryptPassword_KeyParams Dim DecryptPassword_PWChar Dim DecryptPassword_ChkChar Dim DecryptPassword_UseString Dim DecryptPassword_Count DecryptPassword_KeyParams = Split(Key, "-") DecryptPassword_UseString = UseHash DecryptPassword_Count = 0 Do Until Len(DecryptPassword_UseString) < 3 '3 is minimum length of a character Char 1 = Salt 0, Char 2, 3 = Hex of Ascii code DecryptPassword_ChkChar = CInt(Left(DecryptPassword_UseString, 1)) 'Start with the first character of the current version of the password string DecryptPassword_PWChar = (CInt(Clng("&h" & Mid(DecryptPassword_UseString, DecryptPassword_ChkChar + 2, 2)) - (DecryptPassword_KeyParams(0) + DecryptPassword_KeyParams(1) * DecryptPassword_ChkChar))) DecryptPassword = DecryptPassword & Chr(DecryptPassword_PWChar) DecryptPassword_UseString = Trim(Right(DecryptPassword_UseString, Len(DecryptPassword_UseString) - DecryptPassword_ChkChar - 3)) Loop End Function Function RandomChar 'Generate a random hexadecimal value Randomize Timer RandomChar = Trim(Hex(Int(Rnd * 16))) End Function Function EncryptPassword(UsePassword, Key) Dim EncryptPassword_Character Dim EncryptPassword_SaltChars Dim EncryptPassword_KeyParams Dim EncryptPassword_Salt If ValidKey(Key) = False Then WScript.Echo "The key provided is not valid." WScript.Echo "" Help End If EncryptPassword_KeyParams = Split(Key, "-") Randomize Timer If Len(UsePassword) > 0 Then For EncryptPassword_Character = 1 To Len(UsePassword) EncryptPassword_SaltChars = Int(Rnd * (EncryptPassword_KeyParams(2) + 1)) EncryptPassword = EncryptPassword & EncryptPassword_SaltChars For EncryptPassword_Salt = 1 To EncryptPassword_SaltChars EncryptPassword = EncryptPassword & RandomChar Next EncryptPassword = EncryptPassword & Hex(Asc(Mid(UsePassword, EncryptPassword_Character, 1)) + (EncryptPassword_KeyParams(0) + EncryptPassword_SaltChars * EncryptPassword_KeyParams(1))) Next End If If Len(EncryptPassword) Mod 2 = 1 Then EncryptPassword = EncryptPassword & RandomChar 'Ensures hash value is always even End Function Sub Main 'The glue to the above functions; While it may exist on other platforms, code likely differs greatly. If UCase(Left(GetParam(0), 1)) = "E" Then WScript.Echo EncryptPassword(GetParam(1), GetParam(2)) ElseIf UCase(Left(GetParam(0), 1)) = "D" Then WScript.Echo DecryptPassword(GetParam(1), GetParam(2)) ElseIf UCase(Left(GetParam(0), 1)) = "H" Then Help Else WScript.Echo "Bad parameters" End If End Sub Sub Help 'Exclusive to VBS Scripts WScript.Echo "" WScript.Echo "Usage:" WScript.Echo " " & WScript.ScriptName & " Encrypt Password Key" WScript.Echo " " & WScript.ScriptName & " Decrypt PasswordHash Key" WScript.Echo "" WSCript.Echo "Example:" WScript.Echo " " & WScript.ScriptName & " Encrypt ThisIsMyPassword 10-6-5" WScript.Echo " " & WScript.ScriptName & " Decrypt 08C07D32FCA413962EAA13E5995D 10-6-5" WScript.Echo "" WScript.Echo " Parameter 1 identifies the action. Only the first letter is" WScript.Echo " evaluated. Not case sensitive." WScript.Echo " Parameter 2 identifies the password to encode or hash to" WScript.Echo " decode. Passwords are case sensitive, hashes are not." WScript.Echo " Parameter 3 identifies the key. See below." WScript.Echo "" WScript.Echo "Notes: " WScript.Echo " The value of the first number + (second number * third number)" WScript.Echo " cannot exceed 127. A key of 60-9-5 = 105 and is valid. A key of" WScript.Echo " 50-9-9 = 131 and would not be valid. Dashes (-) must be used." WScript.Echo "" WScript.Echo " The maximum value of the last digit in the key is 9." WScript.Echo "" WScript.Quit End Sub Main 'Execute the script.
First, you’ll need the complete script so that you can encode a password, creating the hash. The hash is then used in place of your password in the script. Save the above code to a .vbs file name of your choosing. In the screen shot below, I've saved it to ObscurePW.vbs to create a hash for a sample password of "SamplePassword1"
When using with your own scripts, you just need to copy the decrypt function into your own script.
Here's a sample script that maps a network drive to \\computer1\share1 specifying the user and password to use when connecting:
Dim Network Set Network = WScript.CreateObject("WScript.Network") UserNameVariable = "administrator" PasswordVariable = "SamplePassword1" Network.MapNetworkDrive "Z:", "\\computer1\share1", "false", UserNameVariable, PasswordVariable
As anyone can see, the password is "SamplePassword1". So if we modified the script by adding the DecryptPassword function, we could then set the password to the hash above and instead of referencing the PasswordVariable, we reference Decrypt(PasswordVariable, "12-5-7"). Now this is what the code looks like:
Function DecryptPassword(UseHash, Key) Dim DecryptPassword_KeyParams Dim DecryptPassword_PWChar Dim DecryptPassword_ChkChar Dim DecryptPassword_UseString Dim DecryptPassword_Count DecryptPassword_KeyParams = Split(Key, "-") DecryptPassword_UseString = UseHash DecryptPassword_Count = 0 Do Until Len(DecryptPassword_UseString) < 3 '3 is minimum length of a character Char 1 = Salt 0, Char 2, 3 = Hex of Ascii code DecryptPassword_ChkChar = CInt(Left(DecryptPassword_UseString, 1))'Start with the first character of the current version of the password string DecryptPassword_PWChar = (CInt(Clng("&h" & Mid(DecryptPassword_UseString, DecryptPassword_ChkChar + 2, 2)) - (DecryptPassword_KeyParams(0) + DecryptPassword_KeyParams(1) * DecryptPassword_ChkChar))) DecryptPassword = DecryptPassword & Chr(DecryptPassword_PWChar) DecryptPassword_UseString = Trim(Right(DecryptPassword_UseString, Len(DecryptPassword_UseString) - DecryptPassword_ChkChar - 3)) Loop End Function Dim Network Set Network = WScript.CreateObject("WScript.Network") UserNameVariable = "administrator" PasswordVariable = "2406964158338B7B3AE2D29C6F733169A4C3BC8C5F04BB8A31096B53A0EB8637778E07F118807B511678972D27A1A425" Network.MapNetworkDrive "Z:", "\\computer1\share1", "false", UserName, DecryptPassword(PasswordVariable, "12-5-7")
Note: if you're planning on testing the above sample script, don't use as administrative share to do so. Restrictions in place through UAC can prevent even local administrator accounts from accessing the admin shares. For more information, see https://helgeklein.com/blog/2011/08/access-denied-trying-to-connect-to-administrative-shares-on-windows-7/
To encrypt, the function uses three things:
With a key of 12-5-7 we are taking the ASCII value of the character and adding 12 to it. Then we're also adding 35 to it (5x7). Finally we're adding UP TO 7 salt digits to the hash - this is randomly determined and why the hash can vary in length each time you encrypt - as well as why the hash can vary in appearance.
When we store the value to decode, the first digit is the number of salt characters (therefore, the number of salt characters cannot be greater than 9 or it would take two digits (or I'd have to modify the code to accept hexadecimal numbers)). So when a hash starts with 3, the next three digits are, in essence, garbage, and ignored. Then the next two digits (digits 5 and 6) are the ASCII value of the character + (using the 12-5-7 key) 47.
Because non-special characters are all under ASCII 128, the maximum value the key can create without forcing a 3rd hexadecimal digit when creating the hash is 127 - which is why a key like 50-9-9 would fail (50+(9x9)) = 131.
Like I said, this is not fool proof encryption. If you know how to read it - or have access to the decryption function, it's very easy to crack. But for every day prying eyes, it should do the job nicely.
This is considered reversible encryption meaning that you can decrypt it easily enough. If you're more of a programmer than I, you may be able to exploit better third party technologies like PGP. If you're not, or you don't have more than a basic need for security, this may be sufficient for you.
Again, this is NOT intended to secure highly sensitive information such as your bank account passwords, your domain admin credentials (in most cases), your bitcoin wallet, or your passwords to your private email. And I wouldn't recommend storing other people's passwords with this level of encryption.
That said, if anyone wants to IGNORE the decrypt/encrypt routines and has some kind of password cracking tool, I would encourage you to attempt to crack the password - just let me know you did it, with what tools, what password, and how long it took you.
About the Script
I encourage you to review the code, see what I'm doing and if VBScript is not a strength, hopefully, you can pick up something new from it. Some of the things I'm doing to make this RELATIVELY secure is salting each and every character in the password with a random number of salt characters. This means that each time you run the encrypt function, you will get a different value, and likely, a different length. Without the DecryptPassword function, your average IT admin or script kiddie is not likely to be able to decode this. But, OBVIOUSLY, to make this work, the DecryptPassword function is embedded in the code. Still, it will take at least a LITTLE effort for someone to obtain the actual password.
Important Note: The above script is designed to be executed from the command line using the CSCRIPT.EXE VBScript processor. The WSCRIPT.EXE VBScript processor is the default and will attempt to run things graphically. Since this script requires command line input (the action, password/hash, and key), it is not practical to run it through a simple double-click or the WSCRIPT.EXE processor. You must run it from the command line.
While this script was written and tested as a VBScript, the Encrypt and Decrypt functions should work with little to no modification on other VB platforms, such as VB6, ASP, and VBA.