125

I'm working in PowerShell and I have code that successfully converts a user entered password into plain text:

$SecurePassword = Read-Host -AsSecureString  "Enter password" | convertfrom-securestring | out-file C:\Users\tmarsh\Documents\securePassword.txt

I've been tried several ways to convert it back, but none of them seem to work properly. Most recently, I've tried with the following:

$PlainPassword = Get-Content C:\Users\tmarsh\Documents\securePassword.txt

#convert the SecureString object to plain text using PtrToString and SecureStringToBSTR
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($PlainPassword)
$PlainPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR) #this is an important step to keep things secure

This gives me an error as well.

Cannot convert argument "s", with value: "01000000d08c9ddf0115d1118c7a00c04fc297eb0100000026a5b6067d53fd43801a9ef3f8ef9e43000000000200000000000366000
0c0000000100000008118fdea02bfb57d0dda41f9748a05f10000000004800000a000000010000000c50f5093f3b87fbf9ee57cbd17267e0a10000000833d1d712cef01497872a3457bc8
bc271400000038c731cb8c47219399e4265515e9569438d8e8ed", for "SecureStringToBSTR" to type "System.Security.SecureString": "Cannot convert the "01000000
d08c9ddf0115d1118c7a00c04fc297eb0100000026a5b6067d53fd43801a9ef3f8ef9e430000000002000000000003660000c0000000100000008118fdea02bfb57d0dda41f9748a05f10
000000004800000a000000010000000c50f5093f3b87fbf9ee57cbd17267e0a10000000833d1d712cef01497872a3457bc8bc271400000038c731cb8c47219399e4265515e9569438d8e8
ed" value of type "System.String" to type "System.Security.SecureString"."
At C:\Users\tmarsh\Documents\Scripts\Local Admin Script\PlainTextConverter1.ps1:14 char:1
+ $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($PlainPassw ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument

Cannot find an overload for "PtrToStringAuto" and the argument count: "1".
At C:\Users\tmarsh\Documents\Scripts\Local Admin Script\PlainTextConverter1.ps1:15 char:1
+ $PlainPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodCountCouldNotFindBest

Cannot convert argument "s", with value: "", for "ZeroFreeBSTR" to type "System.IntPtr": "Cannot convert null to type "System.IntPtr"."
At C:\Users\tmarsh\Documents\Scripts\Local Admin Script\PlainTextConverter1.ps1:16 char:1
+ [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR) #this is an important ste ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument

Password is:  01000000d08c9ddf0115d1118c7a00c04fc297eb0100000026a5b6067d53fd43801a9ef3f8ef9e430000000002000000000003660000c0000000100000008118fdea02bfb57d0dda41f97
48a05f10000000004800000a000000010000000c50f5093f3b87fbf9ee57cbd17267e0a10000000833d1d712cef01497872a3457bc8bc271400000038c731cb8c47219399e4265515e9569
438d8e8ed

Does anyone know of a way that will work for this?

Robert Dyjas
  • 4,979
  • 3
  • 19
  • 34
tmarsh
  • 1,355
  • 2
  • 9
  • 4

4 Answers4

159

You are close, but the parameter you pass to SecureStringToBSTR must be a SecureString. You appear to be passing the result of ConvertFrom-SecureString, which is an encrypted standard string. So call ConvertTo-SecureString on this before passing to SecureStringToBSTR.

$SecurePassword = ConvertTo-SecureString $PlainPassword -AsPlainText -Force
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecurePassword)
$UnsecurePassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)
MatthewG
  • 8,583
  • 2
  • 25
  • 27
  • 5
    I'm glad it works. Be careful with your string, now, it is an unsecured string variable containing presumably something important like a password - it is no longer secure in your process memory, etc. – MatthewG Feb 05 '15 at 20:46
  • 27
    Acording to the [Marshal.SecureStringToBSTR](https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.securestringtobstr(v=vs.110).aspx) documentation: _Because this method allocates the unmanaged memory required for a string, always free the BSTR when finished by calling the ZeroFreeBSTR method_. So, you have to execute the following in the end: `[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)`. – Rosberg Linhares Nov 01 '17 at 14:14
  • @RosbergLinhares - Since we're (presumably) powershell focused, is there any reason you couldn't just `$BSTR = $null`? – Orangutech Feb 28 '19 at 18:40
  • 3
    @Orangutech You can't only set the variable to `$null`, because here we are dealing with unmanaged objects. You won't get an error immediately, but I think that you may have problems as time goes by. – Rosberg Linhares Mar 01 '19 at 00:19
  • It's just leaking memory. It doesn't really matter if you do it once, but if you do it thousands of times you will suddenly find yourself short on memory. – poizan42 Jan 16 '20 at 09:28
  • 6
    Aside from the memory leak resulting from the missing call to `ZeroFreeBSTR()`, as stated, the use of `PtrToStringAuto()` was always conceptually flawed, and - now that PowerShell is cross-platform - fails on Unix-like platforms. It should always have been `PtrToStringBSTR()` - see [this answer](https://stackoverflow.com/a/60406968/45375). – mklement0 Jun 29 '20 at 01:53
  • Does not work with Powershell 7 Core! https://github.com/PowerShell/PowerShell/issues/11953 – Gill-Bates Nov 30 '21 at 09:56
  • This means that it is not possible to convert the output of "ConvertFrom-SecureString" to a plain text of the original password, correct? – tarekahf Jan 31 '22 at 16:12
124

You can use PSCredential.GetNetworkCredential() :

$UnsecurePassword = (New-Object PSCredential 0, $SecurePassword).GetNetworkCredential().Password
Nicolas Melay
  • 1,612
  • 1
  • 13
  • 12
79

The easiest way to convert back it in PowerShell

[System.Net.NetworkCredential]::new("", $SecurePassword).Password
  • 2
    Indeed, no need to go through PSCredential. – Nicolas Melay Dec 21 '19 at 04:52
  • I like this approach. FYI for compatibility junkies, this constructor overload taking `SecureString` was introduced in .Net Framework 4.0. On PowerShell v2 I tried `(New-Object -TypeName System.Net.NetworkCredential -ArgumentList "u",$SecureString).Password` but unfortunately the `SecureString` is silently converted to a `String`. The call seems to succeed, but the `Password` property is then the literal value "System.Security.SecureString". Be careful. – John Rees Sep 16 '20 at 23:20
  • This is even better, since you can leave the user argument blank (whereas if you try that with PScredential, it has a hissy fit). – Mike Loux Apr 09 '21 at 14:16
  • 2
    @MikeLoux then just use 0 (zero) as the user argument, it's even shorter than en empty string, and works with both constructors : `(New-Object PSCredential 0,$SecureString).GetNetworkCredential().Password` or `(New-Object System.Net.NetworkCredential 0,$SecureString).Password` – Nicolas Melay Apr 05 '22 at 13:58
38

In PS 7, you can use ConvertFrom-SecureString and -AsPlainText:

#Requires -Version 7.0 

$UnsecurePassword = ConvertFrom-SecureString -SecureString $SecurePassword -AsPlainText

https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/ConvertFrom-SecureString?view=powershell-7#parameters

ConvertFrom-SecureString
           [-SecureString] <SecureString>
           [-AsPlainText]
           [<CommonParameters>]
Macke
  • 24,812
  • 7
  • 82
  • 118
  • 9
    This was driving me crazy. I was trying to use this syntax in v5 to no avail. – Tony May 19 '20 at 15:00
  • 3
    I have gotten so spoiled using PS7, that I forget that the majority of the environments I support only have PS5. "What do you mean I can't do th--oh, right. Too new. Dammit." – Mike Loux Apr 09 '21 at 13:12
  • 4
    Use `#Requires -Version 7.0` for your script consumers to have a proper error message. From https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_requires#-version-nn – v.karbovnichy Jul 29 '21 at 09:33