Protecting Powershell Credentials (NOT)
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
Comments