Protecting Powershell Credentials (NOT)

Published: 2016-12-02. Last Updated: 2016-12-02 14:35:57 UTC
by Rob VandenBrink (Version: 1)
3 comment(s)

If you're like me, you've worked through at least one Powershell tutorial, class or even a "how-to" blog.  And you've likely been advised to use the PSCredential construct to store credentials.  The discussion usually covers that this a secure way to collect credentials, then store them in a variable for later use.  You can even store them in a file and read them back later.  Awesome - this solves a real problem you thought - or does it?

For instance, to collect credentials for a VMware vSphere infrastructure (I'm wrapping up a number of audit scripts this week), you'd do something like:

$vicreds = Get-Credential $null

Which gets you a familiar input screen:


But while I was working on my scripts, I got to thinking.  Wait a minute - I'm using this same credential variable to authenticate to vSphere, to map drives and to login to several web interfaces.  What that means to me is that these credentials are being decrypted as they're used in at least some cases.  And given that Powershell is essentially a front-end to dotNet and every other Windows API ever invented, that means that we can likely decrypt this password too.

Let's take a look.  First, what's in that PSCredentials variable?

$vicreds | gm

   TypeName: System.Management.Automation.PSCredential

Name                 MemberType Definition
----                 ---------- ----------
Equals               Method     bool Equals(System.Object obj)
GetHashCode          Method     int GetHashCode()
GetNetworkCredential Method     System.Net.NetworkCredential GetNetworkCrede...
GetObjectData        Method     void GetObjectData(System.Runtime.Serializat...
GetType              Method     type GetType()
ToString             Method     string ToString()
Password             Property   securestring Password {get;}
UserName             Property   string UserName {get;}


The username is in the clear:

$vicreds.username
someuserid

However, the password is in the Powershell "securestring" format:

$vicreds.password
System.Security.SecureString


Digging deeper, what's behind that passord property?

$vicreds.password | gm

   TypeName: System.Security.SecureString

Name         MemberType Definition
----         ---------- ----------
AppendChar   Method     void AppendChar(char c)
Clear        Method     void Clear()
Copy         Method     securestring Copy()
Dispose      Method     void Dispose(), void IDisposable.Dispose()
Equals       Method     bool Equals(System.Object obj)
GetHashCode  Method     int GetHashCode()
GetType      Method     type GetType()
InsertAt     Method     void InsertAt(int index, char c)
IsReadOnly   Method     bool IsReadOnly()
MakeReadOnly Method     void MakeReadOnly()
RemoveAt     Method     void RemoveAt(int index)
SetAt        Method     void SetAt(int index, char c)
ToString     Method     string ToString()
Length       Property   int Length {get;}

Running digging deeper with "tostring", we get no further.

$vicreds.password.tostring()
System.Security.SecureString

But dumping the password length, we get the right answer!  This means it's getting decrypted for-sure.

$vicreds.password.length
9

After some googling, I found the "convertfrom-securestring" command, but what I get out of that is a boatload of obfuscation:

ConvertFrom-SecureString $vicreds.password
01000000d08c9ddf0115d1118c7a00c04fc297eb01000000da2ad689e7762d4ba723d22648abc93
b00000000020000000000106600000001000020000000aae149136cd7e40f2be310cfa95f2985a9
82a1734d4391be96ccd2098389ed81000000000e8000000002000020000000a6682556eb824853f
026f2dcd19d9e7506b81c4fce950e46ce1d46bc50612f5220000000b01a820022b2a199da1e9348
27d15e2aa0175cd775dc1179da1e706f5214554c40000000bbd980faa6682cb753dfa9899faf0b8
43e5b72b9b27650a34fe980f1432988130e4e939f8fee3254487128c82acd2f4e33cdbb36c0ee9a
e50b6e555015ad646a

After a bit more looking, it was as simple as finding the right command - which is what we pretty much knew going in!

$vicreds.GetNetworkCredential().password
S3cr3tPWd

Yup, that's the super-secret password we started with!

The point of all of this?  There is no secure, native way to store passwords for re-use.  For some reason as an industry, lots of folks have got the understanding that PSCredential makes a great "vault" for passwords though.  The PSCredential approach is definitely the way to go in any code you might be writing.  For me though, I'd be sure to use it only where required, zero out these variables when you're done with them to at least try to work against memory scraping (and hope that zeroing them out actually works), and I'm not storing them off to files for later re-use.   If you can use certificates for authentication, that's really the best way to go - though there are good and not-so-good ways to do this too (stay tuned).  Or better yet, 2FA beats every other method hands-down!

And for goodness sake, don't use PSCredentials as a credential store for use in authentication of other users - don't take userid/password entry and compare it to encrypted passwords of any kind!  Salted hashes (PBKDF2 by preference) is still the way to go for this!

===============
Rob VandenBrink
Compugen

Keywords: PowerShell
3 comment(s)

Comments

What happens if you try to decrypt the password saved in a file, but as another user or on another computer? For example, with this procedure:

http://www.adminarsenal.com/admin-arsenal-blog/secure-password-with-powershell-encrypting-credentials-part-1/
or
https://blog.kloud.com.au/2016/04/21/using-saved-credentials-securely-in-powershell-scripts/

In my testing, I wasn't able to decrypt the password unless I was on the same computer and logged on as the same user as when I created the file. It's been a while since I did this, and maybe I messed up...

John
That's a point in the favour of PSCredentials - if it's an interactive process, you're entering your own password in your own session. This would certainly reduce the risk factor by a mile, so much better than I realized, thanks very much!

But if it's saved to a file, then it's only as secure as the credentials that saved them or uses the file. I would expect that a common use of this type of saved file would be to give a user an elevated access via a script, without giving them actual credentials. For instance, giving the helpdesk rights to shutdown vSphere during a power outage - if you've ever had to race the thermometer, then this situation will be really familiar! If you can use the file, then you (or anyone who can compromise your account) can decrypt the file.

Thanks for the additional info though, it certainly modifies the discussion when considering the risk of using this method!!
It uses DPAPI under the hood as clearly shown by 'ConvertFrom-SecureString $vicreds.password' output. So whatever applies to DPAPI should hold here.

Diary Archives