Skip to content

Instantly share code, notes, and snippets.

@therealdreg
Forked from andshrew/README.md
Created April 15, 2025 18:59
Show Gist options
  • Save therealdreg/dc1bdca8ea3a0024753dbdffb2b59cd9 to your computer and use it in GitHub Desktop.
Save therealdreg/dc1bdca8ea3a0024753dbdffb2b59cd9 to your computer and use it in GitHub Desktop.
VMware Workstation VM encryption passwords saved in the the Windows Credential Manager

Retrieving VMware Workstation VM encryption passwords saved in the the Windows Credential Manager

When creating an encrypted VM, VMware Workstation gives you the option to remember the password. It does this by storing the password in the Windows Credential Manager.

VMware does not provide a way to retrieve this stored password, but it can be accessed via the Win32 CredReadW API function.

There are a number of PowerShell projects including PowerShell Credential Manager which provide access to this API, but in testing I found they were unable to correctly display the VMware password.

This PowerShell example has been tested using Windows PowerShell (v5.1) and PowerShell (v7) using VMware Workstation 17.5 on Windows 11 23H2. Windows Terminal is used as the host for command line apps. It has been written with the assistance of ChatGPT.

1. Locate Encrypted VMs GUID

Open the .vmx file for your VM and look for encryptedVM.guid. This is the name of the credential within Windows Credential Manager. For example:

encryptedVM.guid = "{833AB4F5-587E-4B11-9260-4DB13742FA7F}"

2. Retrieve the password via PowerShell

Open PowerShell or Windows PowerShell, copy and paste the below code. This provides an interface to the Win32 API via C#.

Add-Type @"
using System;
using System.Runtime.InteropServices;

public class Win32Cred
{
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct CREDENTIAL
    {
        public int Flags;
        public int Type;
        public IntPtr TargetName;
        public IntPtr Comment;
        public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
        public int CredentialBlobSize;
        public IntPtr CredentialBlob;
        public int Persist;
        public int AttributeCount;
        public IntPtr Attributes;
        public IntPtr TargetAlias;
        public IntPtr UserName;
    }

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

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern void CredFree(IntPtr buffer);

    public static string CredRead(string targetName, int type = 1)
    {
        IntPtr credPtr;
        if (CredReadW(targetName, type, 0, out credPtr))
        {
            CREDENTIAL cred = (CREDENTIAL)Marshal.PtrToStructure(credPtr, typeof(CREDENTIAL));
            string pass = Marshal.PtrToStringAnsi(cred.CredentialBlob, cred.CredentialBlobSize);
            CredFree(credPtr);
            return pass;
        }
        throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
    }
}
"@

Run the following command replacing {encryptedVM.guid} with the GUID identified in step 1:

[Win32Cred]::CredRead("{encryptedVM.guid}")

For example:

[Win32Cred]::CredRead("{833AB4F5-587E-4B11-9260-4DB13742FA7F}")

3. End of Procedure

The password should be output to the screen:

PS C:\Users\Andrew [Win32Cred]::CredRead("{833AB4F5-587E-4B11-9260-4DB13742FA7F}")
MySecretPassword

A note on Marshal.PtrToString

In this example I use Marshal.PtrToStringAnsi to convert the pointer to the password into a string.

This works in both Windows PowerShell and PowerShell, as it is a C# method available in both .NET Framework and modern .NET (Core).

Marshal.PtrToStringUni does not seem to work, but Marshal.PtrToStringUTF8 does and may be more flexible. This would require the use of PowerShell as this method is only implemented in modern .NET.

It may be preferable to use this, as not all characters will display using ANSI. For example, say your password was made up of emoji characters. Using the above example with Marshal.PtrToStringAnsi the password is going to be output as incorrect characters:

Marshal.PtrToStringAnsi Emoji Example

But using Marshal.PtrToStringUTF8 the password will be correctly displayed, and can be copied/pasted as required:

Marshal.PtrToStringUTF8 Emoji Example

So if this doesn't work for you, you may want to try swapping these functions in the above code, from:

string pass = Marshal.PtrToStringAnsi(cred.CredentialBlob, cred.CredentialBlobSize);

To:

string pass = Marshal.PtrToStringUTF8(cred.CredentialBlob, cred.CredentialBlobSize);

Remember, you must use PowerShell and not Windows PowerShell when using this method.

Here is the full code example using Marshal.PtrToStringUTF8:

Add-Type @"
using System;
using System.Runtime.InteropServices;

public class Win32Cred
{
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct CREDENTIAL
    {
        public int Flags;
        public int Type;
        public IntPtr TargetName;
        public IntPtr Comment;
        public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
        public int CredentialBlobSize;
        public IntPtr CredentialBlob;
        public int Persist;
        public int AttributeCount;
        public IntPtr Attributes;
        public IntPtr TargetAlias;
        public IntPtr UserName;
    }

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

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern void CredFree(IntPtr buffer);

    public static string CredRead(string targetName, int type = 1)
    {
        IntPtr credPtr;
        if (CredReadW(targetName, type, 0, out credPtr))
        {
            CREDENTIAL cred = (CREDENTIAL)Marshal.PtrToStructure(credPtr, typeof(CREDENTIAL));
            string pass = Marshal.PtrToStringUTF8(cred.CredentialBlob, cred.CredentialBlobSize);
            CredFree(credPtr);
            return pass;
        }
        throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
    }
}
"@

You then invoke it in the same was as in step 2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment