Convert a secure string to plain text

155,185

Solution 1

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)

Solution 2

You may also use PSCredential.GetNetworkCredential() :

$SecurePassword = Get-Content C:\Users\tmarsh\Documents\securePassword.txt | ConvertTo-SecureString
$UnsecurePassword = (New-Object PSCredential "user",$SecurePassword).GetNetworkCredential().Password

Solution 3

The easiest way to convert back it in PowerShell

[System.Net.NetworkCredential]::new("", $SecurePassword).Password

Solution 4

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

#Requires -Version 7.0 

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

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

ConvertFrom-SecureString
           [-SecureString] <SecureString>
           [-AsPlainText]
           [<CommonParameters>]
Share:
155,185

Related videos on Youtube

tmarsh
Author by

tmarsh

Updated on July 30, 2021

Comments

  • tmarsh
    tmarsh almost 3 years

    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?

  • MatthewG
    MatthewG over 9 years
    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.
  • Maximilian Burszley
    Maximilian Burszley about 7 years
    I've tested both methods and they are both still correct.
  • Rosberg Linhares
    Rosberg Linhares over 6 years
    Acording to the Marshal.SecureStringToBSTR 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).
  • Jim
    Jim about 6 years
    Upvoted this solution because it is more Powershelly.
  • marsze
    marsze over 5 years
    Use System.Management.Automation.PSCredential in older PS versions when the short type name is not recognized.
  • Orangutech
    Orangutech over 5 years
    @RosbergLinhares - Since we're (presumably) powershell focused, is there any reason you couldn't just $BSTR = $null?
  • Rosberg Linhares
    Rosberg Linhares over 5 years
    @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.
  • AgoHH
    AgoHH over 4 years
    Shorter: [PSCredential]::new(0, $SecurePassword).GetNetworkCredential().Password
  • Nicolas Melay
    Nicolas Melay over 4 years
    Indeed, no need to go through PSCredential.
  • poizan42
    poizan42 over 4 years
    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.
  • K. Frank
    K. Frank over 4 years
    Shorter: [System.Net.NetworkCredential]::new("", $SecurePassword).Password
  • Tony
    Tony about 4 years
    This was driving me crazy. I was trying to use this syntax in v5 to no avail.
  • mklement0
    mklement0 almost 4 years
    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.
  • John Rees
    John Rees almost 4 years
    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.
  • Mike Loux
    Mike Loux about 3 years
    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
    Mike Loux about 3 years
    This is even better, since you can leave the user argument blank (whereas if you try that with PScredential, it has a hissy fit).
  • v.karbovnichy
    v.karbovnichy almost 3 years
    Use #Requires -Version 7.0 for your script consumers to have a proper error message. From docs.microsoft.com/en-us/powershell/module/…
  • Gill-Bates
    Gill-Bates over 2 years
    Does not work with Powershell 7 Core! github.com/PowerShell/PowerShell/issues/11953
  • tarekahf
    tarekahf over 2 years
    This means that it is not possible to convert the output of "ConvertFrom-SecureString" to a plain text of the original password, correct?
  • Nicolas Melay
    Nicolas Melay about 2 years
    @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