27

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?

David Anderson
  • 7,796
  • 2
  • 26
  • 27
Shaggydog
  • 3,165
  • 6
  • 30
  • 46

6 Answers6

45

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
  • 19,762
  • 4
  • 61
  • 78
majkinetor
  • 8,354
  • 9
  • 51
  • 69
  • I think instead of Install-Module you mean Import-Module? – shufler Jul 27 '16 at 18:24
  • 1
    No, You need to install-module first: https://www.powershellgallery.com/packages/CredentialManager/2.0 – majkinetor Jul 27 '16 at 18:35
  • Oh, sorry. Derp. https://technet.microsoft.com/en-us/library/dn807162.aspx I don't know why I couldn't find that earlier. I was wrong! Thank you :) – shufler Jul 27 '16 at 21:15
  • 3
    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
  • 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
10

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
  • 135,435
  • 9
  • 130
  • 184
  • 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
  • 4
    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
  • 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
4

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,798
  • 6
  • 31
  • 63
js2010
  • 17,785
  • 4
  • 45
  • 50
  • Sadly, a year after announcement (02/2020) it is still in (pre) alpha phase, miles from ready-to-production code. Responsible MS team doesn't handle the amount of work which the facing with a new version of PowerShell, PowerShellGet or this particular module. – KUTlime Jan 26 '21 at 17:34
  • 1
    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
  • 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
4

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.

edit: added missing semicolon to code snippet as noted by @alazyworkaholic

fussmonkey
  • 594
  • 4
  • 19
Dragoon
  • 463
  • 3
  • 12
3

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
  • 7,796
  • 2
  • 26
  • 27
  • 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
2

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,050
  • 1
  • 9
  • 16