34

I'm using PowerShell 2.0 (necessary because of SP2010) On Windows Server 2008 R2. I need to retrieve credentials for a process from the Windows Credential Manager. I can't seem to make it work.

I was given this piece of code:

[Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,ContentType=WindowsRuntime]
(new-object Windows.Security.Credentials.PasswordVault).RetrieveAll() | % { $_.RetrievePassword(); $_ }

both lines of code throw errors

Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,ContentType=WindowsRuntime : Unable to find type [Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,ContentType=WindowsRuntime]: make sure that the assembly containing this type is loaded.

and

(new-object Windows.Security.Credentials.PasswordVault).RetrieveAll() | % {$_.RetrievePassword(); $_ } 

respectively. I've been trying to somehow import the PasswordVault class. So far Google has failed me, I haven't even been able to find out which assembly it resides in. What am I missing?

ᄂ ᄀ
  • 5,669
  • 6
  • 43
  • 57
Shaggydog
  • 3,456
  • 7
  • 33
  • 50
  • That piece of code assumes Windows Runtime which was introduced in Windows Server 2012. [PasswordVault](https://learn.microsoft.com/en-us/uwp/api/Windows.Security.Credentials.PasswordVault) is not supported on Windows Server 2008. I can't guide you further as I don't have Windows Server 2008 at hand. You would probably need to use alternative APIs, https://stackoverflow.com/a/67944064/ is one example. – ᄂ ᄀ Jun 23 '23 at 10:39

6 Answers6

47

In powershell5 type:

   Install-Module CredentialManager -force

Then

   New-StoredCredential -Target $url -Username $ENV:Username -Pass ....

and later

   Get-StoredCredential -Target .... 

Source code for the module is https://github.com/davotronic5000/PowerShell_Credential_Manager

Dennis
  • 20,275
  • 4
  • 64
  • 80
majkinetor
  • 8,730
  • 9
  • 54
  • 72
  • 4
    OP specifically asked for PS2, why is a PS5 answer being voted up so much? `Install-Module` is not a valid cmdlet in PS2 – dlkulp Aug 08 '17 at 18:03
  • 2
    You can install the module on PS2 nevertheless. `Install-Module` is only shortcut. – majkinetor Aug 15 '17 at 18:40
  • 1
    The module is no longer being maintained ("I am no longer working on this project or PowerShell much at all. If anyone else wants to take a fork and continue supporting this project. I would be happy to link to that project from here to guide people in the right direction."). – Zian Choy Sep 04 '21 at 22:36
  • Hi, I've created a new module forked from the CredentialManager module of Dave, just install TUN.CredentialManager instead of CredentialManager (so call "Install-Module TUN.CredentialManager -Force" ) and you'll get a PowerShell Core compatible version along with more than 512 byte password length support and internal use of secure string instead of storing the clear password (where possible according to cmdlet calls, see new parameters IncludeSecretPassword and ExcludeClearPassword). New website here: https://github.com/echalone/PowerShell_Credential_Manager – Markus Szumovski Aug 22 '22 at 11:08
  • @MarkusSzumovski you should probably inform the original owner so that he can update the description of his Github "I would be happy to link to that project from here to guide people in the right direction." https://github.com/davotronic5000/PowerShell_Credential_Manager – virgula24 Aug 28 '23 at 16:45
  • @virgula24 I have, he never responded ;) – Markus Szumovski Aug 30 '23 at 10:36
12

You'll need to access the Win32 API to interact with the Credential Manager.

CredMan.ps1 from the Technet scripting gallery nicely demonstrates this.

For simpler usage patterns, like just listing principals or adding new credentials, you can also use cmdkey, a built-in Windows Command-line utility for credential management

For reusing stored Credentials in PowerShell, this guy seems to have found a way to build a PSCredential from a Generic Credential handle from the Credential Store, using a technique similar to that of CredMan.ps1: Get-StoredCredential

Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • Thanks, just to make sure, before I devote time to studying it, it will allow me to retrieve the password, right? – Shaggydog Mar 17 '15 at 15:59
  • No, at least `cmdkey` won't. Why would you ever want to retrieve a password? – Mathias R. Jessen Mar 17 '15 at 16:00
  • 5
    Because I need to invoke a process remotely with credentials stored in Credential Manager. I thought this part was self-evident, why else would I need to access Credential Manager if not to use the credentials stored inside? – Shaggydog Mar 17 '15 at 16:08
  • 1
    Without having any practical experience with implementing such a solution, I would think the "proper" way to do so would be to export a token or a security context through an SSPI or similar, instead of fiddling with the password in plain-text directly – Mathias R. Jessen Mar 17 '15 at 16:17
  • 1
    I'm using an instance of System.Management.Automation.PSCredential in the script, so if you know of a way to get that out of the Credential Manager, I'm all ears. I mean eyes. – Shaggydog Mar 17 '15 at 16:21
  • cmdkey won't retreive the password, but CredMan.ps1 does because it uses the Credential Manager Win32 API. The password it retrieves is stored/displayed in plain-text, so after you get it you'll need to ConvertTo-SecureString -AsPlainText-Force – shufler Jul 27 '16 at 21:16
  • @MathiasR.Jessen hello Mathias, is there any restriction for Credential Manager over network? WinAPI CredWrite fails for me if I connect to windows remotely. I asked this here: https://security.stackexchange.com/questions/174185/does-windows-block-access-to-credential-manager-for-remote-connections – pinkpanther Nov 23 '17 at 18:46
9

If anybody just wants a code snippet so that they can distribute scripts without having to instruct the end users to install modules or include DLL files, this should do the trick.

$code = @"
using System.Text;
using System;
using System.Runtime.InteropServices;

namespace CredManager {
  [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  public struct CredentialMem
  {
    public int flags;
    public int type;
    public string targetName;
    public string comment;
    public System.Runtime.InteropServices.ComTypes.FILETIME lastWritten;
    public int credentialBlobSize;
    public IntPtr credentialBlob;
    public int persist;
    public int attributeCount;
    public IntPtr credAttribute;
    public string targetAlias;
    public string userName;
  }

  public class Credential {
    public string target;
    public string username;
    public string password;
    public Credential(string target, string username, string password) {
      this.target = target;
      this.username = username;
      this.password = password;
    }
  }

  public class Util
  {
    [DllImport("advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool CredRead(string target, int type, int reservedFlag, out IntPtr credentialPtr);

    public static Credential GetUserCredential(string target)
    {
      CredentialMem credMem;
      IntPtr credPtr;

      if (CredRead(target, 1, 0, out credPtr))
      {
        credMem = Marshal.PtrToStructure<CredentialMem>(credPtr);
        byte[] passwordBytes = new byte[credMem.credentialBlobSize];
        Marshal.Copy(credMem.credentialBlob, passwordBytes, 0, credMem.credentialBlobSize);
        Credential cred = new Credential(credMem.targetName, credMem.userName, Encoding.Unicode.GetString(passwordBytes));
        return cred;
      } else {
        throw new Exception("Failed to retrieve credentials");
      }
    }

    [DllImport("Advapi32.dll", SetLastError = true, EntryPoint = "CredWriteW", CharSet = CharSet.Unicode)]
    private static extern bool CredWrite([In] ref CredentialMem userCredential, [In] int flags);

    public static void SetUserCredential(string target, string userName, string password)
    {
      CredentialMem userCredential = new CredentialMem();

      userCredential.targetName = target;
      userCredential.type = 1;
      userCredential.userName = userName;
      userCredential.attributeCount = 0;
      userCredential.persist = 3;
      byte[] bpassword = Encoding.Unicode.GetBytes(password);
      userCredential.credentialBlobSize = (int)bpassword.Length;
      userCredential.credentialBlob = Marshal.StringToCoTaskMemUni(password);
      if (!CredWrite(ref userCredential, 0))
      {
        throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
      }
    }
  }
}
"@
Add-Type -TypeDefinition $code -Language CSharp
# How to store credentials
[CredManager.Util]::SetUserCredential("Application Name", "Username", "Password")
# How to retrieve credentials
[CredManager.Util]::GetUserCredential("Application Name")
# How to just get the password
[CredManager.Util]::GetUserCredential("Application Name").password

The above code was tested on PowerShell 7 and PowerShell 5, though you may need a somewhat recent version of the .Net framework if you're using the latter.

ᄂ ᄀ
  • 5,669
  • 6
  • 43
  • 57
Dragoon
  • 723
  • 6
  • 13
5

SecretManagement and SecretStore from Microsoft appear to be the official solution. They were officially released on March 25, 2021. The secret can be a credential.

Install-Module Microsoft.PowerShell.SecretManagement
Install-Module SecretManagement.JustinGrote.CredMan  # windows credential manager
Register-SecretVault SecretManagement.JustinGrote.CredMan
Set-Secret -Name TestSecret -Secret "TestSecret"
Get-Secret -Name TestSecret -AsPlainText
TestSecret
Zian Choy
  • 2,846
  • 6
  • 33
  • 64
js2010
  • 23,033
  • 6
  • 64
  • 66
  • 2
    The modules was officially released on March 25, 2021 (1.0) and updated (1.1) on July 13, 2021. KUTlime's comment no longer applies. – Zian Choy Sep 04 '21 at 22:39
  • 1
    Please also note that with `SecretManagement` `powershell` module you can't use arbitrary credential name, since this module prepends `ps:` in credential name. For example, you will be unable to store and retrieve `git:` credentials. – galeksandrp Nov 12 '21 at 20:13
  • Minimum PowerShell version: 5.1. How is it relevant to the question about PowerShell 2.0? – ᄂ ᄀ Jun 23 '23 at 10:30
4

According to the docs, the PasswordVault class isn't supported on Windows Server 2008 R2.

Minimum supported server Windows Server 2012

https://msdn.microsoft.com/library/windows/apps/windows.security.credentials.passwordvault.aspx

David Anderson
  • 8,306
  • 2
  • 27
  • 27
  • 1
    Alright, point taken. Therefore my question is, how do I access Credential Manager on Windows Server 2008? There must be a way. – Shaggydog Mar 17 '15 at 15:52
1

I found a really good post, this code will print all usernames, resources and passwords

[Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,ContentType=WindowsRuntime]
$vault = New-Object Windows.Security.Credentials.PasswordVault
$vault.RetrieveAll() | % { $_.RetrievePassword();$_ }

Credits to Todd's post

LuisEnMarroquin
  • 1,609
  • 1
  • 14
  • 26
  • Mind the environment in the question before reposting good posts. How is it relevant to Windows Server 2008/PowerShell 2.0? – ᄂ ᄀ Jun 23 '23 at 10:47