1
votes

Please lay your expert opinion on the security of these two scripts:

[Note: The output of these two scripts will be pipelined. They will not be assigned to any variable."]

First :

Function GetFrom-SecureString([SecureString]$SecureString) {
  [IntPtr]$valuePtr = [IntPtr]::Zero
  try {
    $valuePtr = [Runtime.InteropServices.Marshal]::SecureStringToGlobalAllocUnicode($SecureString)
    return [Runtime.InteropServices.Marshal]::PtrToStringUni($valuePtr);
  }
 finally {
    [Runtime.InteropServices.Marshal]::ZeroFreeGlobalAllocUnicode($valuePtr);
    }
 }

Second :

Function Decode-SecureString([SecureString]$SecureString){
try{
$bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString)
$length = [Runtime.InteropServices.Marshal]::ReadInt32($bstr, -4)
for ( $i = 0; $i -lt $length; ++$i ) {
  [CHAR][Runtime.InteropServices.Marshal]::ReadByte($bstr, $i)
  }
}
finally{
    if ( $bstr -ne [IntPtr]::Zero ) {
      [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
    }
}
}

I know that its not secure to use $cred.GetNetworkCredential().Password because it converts the securestring to the memory first, and many have pointed out to me to use marshalling. So, I have constructed the above two scripts as trial and I would like to know if there's anything reckless maneuver in these two scripts.

Edit:

I have updated my second string to clean up the BSTR by implementing :

finally{
    if ( $bstr -ne [IntPtr]::Zero ) {
  [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) #Frees the BSTR.
    }
}

However, the returned values captured in the pipeline seem to be not at all secure since it is in the memory too until its cleaned up.

[Note: I haven't yet programmed the script which will accept the pipelined values.]

The very point of using a securestring seems to be destroyed by decrypting it back to plaintext. As if we should never decrypt the securestring. Correct me if I am wrong.

1
Please explain with more details what you mean by "safer". - vonPryz
They both still convert the SecureString to memory... It's impossible to not have that happen. Your strategy should be ensuring it is removed from memory as soon as its no longer needed. To that end, I would suggest option 1, but as it's a function you're presumably capturing the output somewhere, so the returned value will probably exist somewhere until it is garbage collected - arco444
@arco444 Your strategy should be ensuring it is removed from memory as soon as its no longer needed. To that end, I would suggest option 1 Option 1 create immutable managed string object. How you suggest to remove it from memory? - user4003407
The problem with decrypting a SecureString is that you can't forcibly overwrite the plain-text String copy in memory after you do so (it's a managed String object). That's a limitation you're going to have to live with, if you really do need to decrypt a SecureString to plain-text. - Bill_Stewart

1 Answers

0
votes

The chosen decryption technique does not matter, since you're still converting to plaintext.

Even when the secret is in memory only for a short period of time, what's preventing me from using debugging techniques to read it from memory?

  • PowerShell has an integrated debugger that can step into code and inspect variables (Wait-Debugger).

  • Script output using variables, parameters or pipeline can be intercepted by overriding/redefining commandlets, using aliassses or other mocking techniques.

  • You are also vulnerable for unexpected disclosures, for example when the script you pass the secret into is logging it when using -Verbose etc..

Using the pipeline or a direct variable doesn't matter in this aspet. Neither is inherently safer, both require / result in having the secret in plain text in memory.

I would look for an option to specify the secret value encrypted to the target, bypassing the need for decryption at all.