Powershell prompt for password convert securestring to plain text

From Svendsen Tech PowerShell Wiki
Jump to: navigation, search

To prompt a user for a password, you can use the cmdlet Read-Host with the parameter -AsSecureString. The problem I ran into was when I wanted to use it in conjunction with a COM method call that needed the password in plain text. Specifically, I wanted to specify alternate credentials for mapping a network drive. The cmdlet New-PSDrive doesn't accept the -Credential parameter in PowerShell v2.



I searched the web for a while, but didn't find a way to convert the secure string back into plain text. A helpful person I came across had done it and had the magic incantation required, which I am now making easily available for others who might not run into this same helpful person. He said he thought he found it buried deep in an Exchange cmdlet somewhere. Another helpful person later made me aware that this left an unfreed pointer, so make sure you check out the example function for properly handling the pointer.

A Generic Function for Converting Security.SecureString Objects to Plain Text Strings

This is a simple function that takes a System.Security.SecureString object (a secure string) and returns it in plain text (as a string). It also makes sure to clean up (free) the password pointer.

function ConvertFrom-SecureToPlain {
    
    param( [Parameter(Mandatory=$true)][System.Security.SecureString] $SecurePassword)
    
    # Create a "password pointer"
    $PasswordPointer = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecurePassword)
    
    # Get the plain text version of the password
    $PlainTextPassword = [Runtime.InteropServices.Marshal]::PtrToStringAuto($PasswordPointer)
    
    # Free the pointer
    [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($PasswordPointer)
    
    # Return the plain text password
    $PlainTextPassword
    
}

Getting the password out of a Get-Credential object

This demonstrates how you can get the password out of a PowerShell credentials object that you get from Get-Credential in your own session.

PS C:\> $cred = Get-Credential

cmdlet Get-Credential at command pipeline position 1
Supply values for the following parameters:
Credential

PS C:\> $cred | ft -AutoSize

UserName                           Password
--------                           --------
dummy\someuser System.Security.SecureString


PS C:\> $cred.GetNetworkCredential().Password
SomeSecretPassword!

Demonstration of Converting SecureString to Plain Text

As you can see below, the [Runtime.InteropServices.Marshal] class is where the most relevant stuff seems to be. This guy uses some methods that seem to be Unicode-specific from the same class in a related article.

NB! Be aware that you should not normally use this one-liner, as it leaves an unfreed pointer; instead see the example function above.

PS C:\> $SecureString = Read-Host -AsSecureString 'Enter password'
Enter password: *********
PS C:\> $SecureString
System.Security.SecureString
PS C:\> ConvertFrom-SecureString $SecureString
01000000db8c9ddf0115d1118c7a00c04fc297eb01000000d4478e8a047a754d9cd017c4de5a0dc0000000000200000000001066000000010000200
000009f238a1b84e20c6aa93a6ec17ac1c95cbf8b234736543d56f50fc6c02489c77c000000000e8000000002000020000000cd43b30ee5f08e540d
5ec52000296d395a4989b7da6a31e4b0033ea19324d31320000000be0fccca5dc003b1991418b5f72bde3baaadf110ecedd807eb5a133c8894bbee4
0000000d7e003be710ba69f56f32f30a88d55730354c7ef23f37d683de6bb2ed8a01836bf6cb63e3b09c62aa3ec23fe96aec7a34c8e9af5116e0f9a
38783cae0856b2a4
PS C:\> [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureString))
PazzW0rd!

Example Script for Mapping a Network Drive With Alternate Credentials

This demonstrates how you could check if a server is available for mapping network drives against. The script will exit if a connection can not be made. It doesn't do anything useful now if a successful drive mapping is performed; it's just an example.

Set-StrictMode -Version Latest

function ConvertFrom-SecureToPlain {
    
    param( [Parameter(Mandatory=$true)][System.Security.SecureString] $SecurePassword)
    
    # Create a "password pointer".
    $PasswordPointer = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecurePassword)
    
    # Get the plain text version of the password.
    $PlainTextPassword = [Runtime.InteropServices.Marshal]::PtrToStringAuto($PasswordPointer)
    
    # Free the pointer.
    [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($PasswordPointer)
    
    # Return the plain text password.
    $PlainTextPassword
    
}

$Share = '\\2008r2esxi\admin$'
$DriveLetter = 'X:'

$Username = Read-Host 'Please enter a username and domain in the form DOMAIN\USERNAME'
$Password = Read-Host -AsSecureString 'Please enter your password (it will not be displayed)'

$PlainTextPassword = ConvertFrom-SecureToPlain $Password

# Try to map the drive and hide errors (will be displayed later).
$Drive = New-Object -ComObject WScript.Network
$ErrorActionPreference = 'SilentlyContinue'
$Drive.MapNetworkDrive($DriveLetter, $Share, 'false', $Username, $PlainTextPassword) | Out-Null

# Check if it failed, exit if it did, and print the error.
if (-not $?) {
    
    "Failed to map $Share to drive ${DriveLetter}: $($error[0].ToString())"
    exit 1
    
}

# Make errors visible again.
$ErrorActionPreference = 'Continue'

# If we get here, the mapping was successful
"Successfully mapped $Share to drive letter $DriveLetter"

# ... more stuff