4

I am executing my PowerShell scripts in c# through PowerShell runspace. We wrap our credentials in the credential object. However, the password gets exposed when we write the below statement.

$Credential = Get-Credential
$Password = $credential.GetNetworkCredential().Password

here $Password will have the password in plain text, which I want to prevent the user from writing any such statements that are targetted at retrieving the password from the credentials object

One such way that I can think of is, that I scan the script text before executing it, and if I find such a statement, I report. Is there a better way of achieving this in native PowerShell or any other way?

puneet
  • 492
  • 1
  • 8
  • 25
  • Use cmdlets that support the `-Credential` parameter, e.g. [`Invoke-Command`](https://learn.microsoft.com/powershell/module/microsoft.powershell.core/invoke-command?view=powershell-6) `-Credential $Credential ...`, meaning that it is probably more a limitation of the application that you trying to invoke which appearently only accepts plain text passwords. – iRon Apr 16 '19 at 06:04
  • User registers his credentials with our web tenant, which we make available to the powershell script in the credentials object so that he can execute his powershell script in the context of that credential. Inside the script, he uses the Commandlets that take the -Credential parameter only. But, if someone hacks the web session, he can modify the script to write $credential.GetNetworkCredential().Password and get the password. How do i restrict the script to avoid/remove such statements? – puneet Apr 16 '19 at 06:20
  • 1
    `$credential.GetNetworkCredential().Password` will only work under the account that registered the password. – iRon Apr 16 '19 at 06:28
  • @puneet - why do they need to hack the script to reveal the password when they can just hack the script to *use the credential as is* to do malicious things? Why is one worse/better than the other? – Damien_The_Unbeliever Apr 16 '19 at 07:43
  • @Damien_The_Unbeliever, Our PowerShell scripts do execute from the web portal (Asp.NET, c#), and if someone hacks the web session, then he can very well modify the script to include "Password = $credential.GetNetworkCredential().Password" and retrieve the password, because scripts results are flushed to the database, and then available to the user to be viewed later. – puneet Apr 16 '19 at 08:19
  • @puneet - but my point was, they don't (necessarily) even need to print the password if they can already get hold of the credential object and *use it directly* to cause damage. It's a very narrow attack model where them getting the password is a concern but them being able to *use* the credentials (without bothering to find out the password) is not. – Damien_The_Unbeliever Apr 16 '19 at 13:43
  • @Damien_The_Unbeliever, I get your point, but what I have explained here is very limited scope of the scenario. I cannot reveal the whole details of why I need such behavior. I am not worried about the Credentials object, but just the password retrieval part from it. If I just find out the text "GetNetworkCredential()" within the script, and then stop the script execution, would it suffice? Or are there any other ways, I can get the password extracted from the Credentials object? I believe this is only the one. – puneet Apr 17 '19 at 06:29
  • Although not a direct answer to your question, another thing you might want to look for if you go the script-parsing method is something like this: `$Password = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($Credential.Password))`. `$Password` will then have the plaintext password. – Jessie Westlake Dec 14 '20 at 20:41
  • @iRon so this means the only way to convert the secure string file back into plain text would be to do so on the same computer and same account that encrypted it in the first place? As in we don't have to worry about people maliciously downloading it and decrypting it? – FaceySmile Jul 23 '21 at 06:13
  • 1
    @FaceySmile: yes, (till a sertain extend, read the remarks behind the [`SecureString` class](https://learn.microsoft.com/en-us/dotnet/api/system.security.securestring)). This also implies that the script using the `SecureString` won't work under other accounts. *The general approach of dealing with credentials is to avoid them and instead rely on other means to authenticate, such as certificates or Windows authentication.* – iRon Jul 23 '21 at 07:11

2 Answers2

1

Once PsCredential is captured with Get-Credential cmdlet:

$Credential = Get-Credential

The $Credential variable has two properties:

  1. Username
  2. Password

The type of Password property is SecureString:

$Credential.Password.GetType()  --> SecureString

so it is encrypted. Now we can use these two cmdlets to transfer/save the password as a normal string without revealing the actual password:

  1. ConvertFrom-SecureString
  2. ConvertTo-SecureString

Let's say that we have provided Username as myuser and Password as mypassword to the prompt after Get-Credential popup.

This below will convert the password to encrypted String object:

$Credential.Password | ConverFrom-SecureString

# Output

 01000000d08c9ddf0115d1118c7a00c04fc297eb01000000f9ae07907bbc82458b25fd92b2dae62e0000000002000000000003660000c000000010000000e1d56e48520215b461e09a24c53375660000000004800000a0000000100000008eeb7cf1234bac1806b5b624f4dcef0a18000000c8062d946ab9605dfcaaba9a20d7a7486c54010451c2dd0714000000ce000956b190d080f2c5eae9e159a78916d88e98

you can now save this output to a file and read it with Get-Content later or save it to a PowerShell variable:

$EncryptedPassword = $Credential.Password | ConverFrom-SecureString

you can convert this normal String back to SecureString using `ConvertTo-SecureString:

$Password = $EncryptedPassword | ConvertTo-SecureString

and then construct new PsCredential object:

$CredentialNew = New-Object System.Management.Automation.PsCredential('myuser', $Password)

I hope this is what you are after.

Dilshad Abduwali
  • 1,388
  • 7
  • 26
  • 47
  • You got me wrong. My question was about how to prevent a user from typing such statements in the script that would attempt at "unsecuring" the password – puneet Aug 23 '19 at 10:55
  • I think you got me wrong. The above method does not require anyone to type the password in the script. Above is two separate steps. First you manually get the password encrypted and save it in a file. Then you script reads the encrypted password from the file and decrypts strait to `PsCredential` object. So the script (using second part of the above answer) does not ask anyone to type any password, it just reads the file that make no sense to anyone other than the cmdlet `ConverTo-SecureString`. – Dilshad Abduwali Aug 24 '19 at 11:24
0

Just for clarity: $credential.GetNetworkCredential().Password will only work under the account that registered the password.

With regards to the question, you could as well asked:

"How do I prevent users from writing down their passwords?"

enter image description here

Answer

don't use passwords (aka the SecureString class) but authenticate the user (account) based mechanisms as e.g. single sign on.

Quote from SecureString shouldn't be used:

The general approach of dealing with credentials is to avoid them and instead rely on other means to authenticate, such as certificates or Windows authentication.

iRon
  • 20,463
  • 10
  • 53
  • 79