Windows/TestHarnesses/T1218.007_Msiexec/InvokeMSI.Tests.ps1

Set-StrictMode -Version Latest

$TestScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
$ModuleRoot = Resolve-Path "$TestScriptRoot\..\..\..\"
$ModuleManifest = "$ModuleRoot\AtomicTestHarnesses.psd1"

Remove-Module [A]tomicTestHarnesses
Import-Module $ModuleManifest -Force -ErrorAction Stop

Describe 'Invoke-ATHMSI' {
    BeforeAll {
        $Help = Get-Help -Name Invoke-ATHMSI -Full

        $ExpectedTechniqueID = $null

        if ($Help.Synopsis.Split("`r`n")[-1] -match '^(?-i:Technique ID: )(?<TechniqueID>\S+) (?<TechniqueDescription>\(.+\))$') {
            $ExpectedTechniqueID = $Matches['TechniqueID']
        }

        $FixedTestGuid = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
    }

    Context 'Validating error conditions' -Tag 'Unit', 'T1218.007' {
        It 'should fail to install MSI when an empty script content is supplied' {
            { Invoke-ATHMSI -ScriptContent $ScriptContent -ErrorAction Stop } | Should -Throw
        }
    }

    Context 'Expected artifacts and behaviors when exercising the attack technique' -Tag 'Technique', 'T1218.007' {

        It 'should run the MSI file and launch VBScript code' {
            $Result = Invoke-ATHMSI -TestGuid $FixedTestGuid -DeleteMsi

            $Result | Should -Not -BeNullOrEmpty

            $Result

            $Result.TechniqueID                   | Should -BeExactly $ExpectedTechniqueID
            $Result.TestSuccess                   | Should -BeTrue
            $Result.TestGuid                      | Should -BeExactly $FixedTestGuid
            $Result.TestCommand                   | Should -Not -BeNullOrEmpty
            $Result.ExecutionType                 | Should -BeExactly 'Msiexec'
            $Result.MsiAction                     | Should -BeExactly 'Install'
            $Result.MsiCustomAction               | Should -BeExactly 'Script'
            $Result.MsiScriptEngine               | Should -BeExactly 'VBScript'
            $Result.MsiScriptContent              | Should -Not -BeNullOrEmpty
            $Result.MsiFilePath                   | Should -Match 'Test\.msi$'
            $Result.MsiFileHash                   | Should -Not -BeNullOrEmpty
            $Result.MsiExecProcessId              | Should -Not -BeNullOrEmpty
            $Result.MsiExecProcessCommandLine     | Should -Not -BeNullOrEmpty
            $Result.RunnerProcessId               | Should -Not -BeNullOrEmpty
            $Result.RunnerProcessName             | Should -Not -BeNullOrEmpty
            $Result.RunnerChildProcessId          | Should -Not -BeNullOrEmpty
            $Result.RunnerChildProcessName        | Should -BeExactly 'powershell.exe'
            $Result.RunnerChildProcessCommandLine | Should -Not -BeNullOrEmpty
        }

        It 'should run the MSI file and launch VBScript code under the MSI action - Advertise' {
            $Result = Invoke-ATHMSI -ScriptEngine JScript -ExecutionType Msiexec -MSIAction Advertise -TestGuid $FixedTestGuid -DeleteMsi

            $Result | Should -Not -BeNullOrEmpty

            $Result

            $Result.TechniqueID                   | Should -BeExactly $ExpectedTechniqueID
            $Result.TestSuccess                   | Should -BeTrue
            $Result.TestGuid                      | Should -BeExactly $FixedTestGuid
            $Result.TestCommand                   | Should -Not -BeNullOrEmpty
            $Result.ExecutionType                 | Should -BeExactly 'Msiexec'
            $Result.MsiAction                     | Should -BeExactly 'Advertise'
            $Result.MsiCustomAction               | Should -BeExactly 'Script'
            $Result.MsiScriptEngine               | Should -BeExactly 'JScript'
            $Result.MsiScriptContent              | Should -Not -BeNullOrEmpty
            $Result.MsiFilePath                   | Should -Match 'Test\.msi$'
            $Result.MsiFileHash                   | Should -Not -BeNullOrEmpty
            $Result.MsiExecProcessId              | Should -Not -BeNullOrEmpty
            $Result.MsiExecProcessCommandLine     | Should -Not -BeNullOrEmpty
            $Result.RunnerProcessId               | Should -Not -BeNullOrEmpty
            $Result.RunnerProcessName             | Should -Not -BeNullOrEmpty
            $Result.RunnerChildProcessId          | Should -Not -BeNullOrEmpty
            $Result.RunnerChildProcessName        | Should -BeExactly 'powershell.exe'
            $Result.RunnerChildProcessCommandLine | Should -Not -BeNullOrEmpty
        }

        It 'should run the MSI file with WMI under the MSI action - Install and launch executable code' {
            $Result = Invoke-ATHMSI -Exe -ExecutionType WMI -MSIAction Install -TestGuid $FixedTestGuid -DeleteMsi

            $Result | Should -Not -BeNullOrEmpty

            $Result

            $Result.TechniqueID                       | Should -BeExactly $ExpectedTechniqueID
            $Result.TestSuccess                       | Should -BeTrue
            $Result.TestGuid                          | Should -BeExactly $FixedTestGuid
            $Result.TestCommand                       | Should -Not -BeNullOrEmpty
            $Result.ExecutionType                     | Should -BeExactly 'WMI'
            $Result.MsiAction                         | Should -BeExactly 'Install'
            $Result.MsiCustomAction                   | Should -BeExactly 'Exe'
            $Result.MsiScriptEngine                   | Should -BeNull
            $Result.MsiScriptContent                  | Should -BeNull
            $Result.MsiFilePath                       | Should -Match 'Test\.msi$'
            $Result.MsiFileHash                       | Should -Not -BeNullOrEmpty
            $Result.MsiExecProcessId                  | Should -BeNull
            $Result.MsiExecProcessCommandLine         | Should -BeNull
            $Result.RunnerProcessId                   | Should -Not -BeNullOrEmpty
            $Result.RunnerProcessName                 | Should -Not -BeNullOrEmpty
            $Result.RunnerChildProcessId              | Should -Not -BeNullOrEmpty
            $Result.RunnerChildProcessName            | Should -Match 'MSI[A-Z0-9]{2,4}\.tmp'
            $Result.RunnerChildProcessCommandLine     | Should -Not -BeNullOrEmpty
        }

        It 'should run the MSI file with msiexec, and launch x86 DLL code' {
            $Result = Invoke-ATHMSI -Dll -MSIAction Admin -TestGuid $FixedTestGuid -DeleteMsi

            $Result | Should -Not -BeNullOrEmpty

            $Result

            $Result.TechniqueID                       | Should -BeExactly $ExpectedTechniqueID
            $Result.TestSuccess                       | Should -BeTrue
            $Result.TestGuid                          | Should -BeExactly $FixedTestGuid
            $Result.TestCommand                       | Should -Not -BeNullOrEmpty
            $Result.ExecutionType                     | Should -BeExactly 'Msiexec'
            $Result.MsiAction                         | Should -BeExactly 'Admin'
            $Result.MsiCustomAction                   | Should -BeExactly 'Dll'
            $Result.MsiScriptEngine                   | Should -BeNull
            $Result.MsiScriptContent                  | Should -BeNull
            $Result.MsiFilePath                       | Should -Match 'Test\.msi$'
            $Result.MsiFileHash                       | Should -Not -BeNullOrEmpty
            $Result.MsiExecProcessId                  | Should -Not -BeNullOrEmpty
            $Result.MsiExecProcessCommandLine         | Should -Not -BeNullOrEmpty
            $Result.RunnerProcessId                   | Should -Not -BeNullOrEmpty
            $Result.RunnerChildProcessId              | Should -Not -BeNullOrEmpty
            $Result.RunnerChildProcessName            | Should -BeExactly 'powershell.exe'
            $Result.RunnerChildProcessCommandLine     | Should -Not -BeNullOrEmpty
        }

        It 'should run the MSI file with WMI under the Advertise MSI action, and launch DLL code' {
            $Result = Invoke-ATHMSI -Dll -ExecutionType WMI -MSIAction Advertise -TestGuid $FixedTestGuid -DeleteMsi

            $Result | Should -Not -BeNullOrEmpty

            $Result

            $Result.TechniqueID                       | Should -BeExactly $ExpectedTechniqueID
            $Result.TestSuccess                       | Should -BeTrue
            $Result.TestGuid                          | Should -BeExactly $FixedTestGuid
            $Result.TestCommand                       | Should -Not -BeNullOrEmpty
            $Result.ExecutionType                     | Should -BeExactly 'WMI'
            $Result.MsiAction                         | Should -BeExactly 'Advertise'
            $Result.MsiCustomAction                   | Should -BeExactly 'Dll'
            $Result.MsiScriptEngine                   | Should -BeNull
            $Result.MsiScriptContent                  | Should -BeNull
            $Result.MsiFilePath                       | Should -Match 'Test\.msi$'
            $Result.MsiFileHash                       | Should -Not -BeNullOrEmpty
            $Result.MsiExecProcessId                  | Should -BeNull
            $Result.MsiExecProcessCommandLine         | Should -BeNull
            $Result.RunnerProcessId                   | Should -Not -BeNullOrEmpty
            $Result.RunnerChildProcessId              | Should -Not -BeNullOrEmpty
            $Result.RunnerChildProcessName            | Should -BeExactly 'powershell.exe'
            $Result.RunnerChildProcessCommandLine     | Should -Not -BeNullOrEmpty
        }

        It 'should run the MSI file with WMI under the MSI action - Admin and launch executable code' {
            $Result = Invoke-ATHMSI -Exe -ExecutionType WMI -MSIAction Admin -TestGuid $FixedTestGuid -DeleteMsi

            $Result | Should -Not -BeNullOrEmpty

            $Result

            $Result.TechniqueID                       | Should -BeExactly $ExpectedTechniqueID
            $Result.TestSuccess                       | Should -BeTrue
            $Result.TestGuid                          | Should -BeExactly $FixedTestGuid
            $Result.TestCommand                       | Should -Not -BeNullOrEmpty
            $Result.ExecutionType                     | Should -BeExactly 'WMI'
            $Result.MsiAction                         | Should -BeExactly 'Admin'
            $Result.MsiCustomAction                   | Should -BeExactly 'Exe'
            $Result.MsiScriptEngine                   | Should -BeNull
            $Result.MsiScriptContent                  | Should -BeNull
            $Result.MsiFilePath                       | Should -Match 'Test\.msi$'
            $Result.MsiFileHash                       | Should -Not -BeNullOrEmpty
            $Result.MsiExecProcessId                  | Should -BeNull
            $Result.MsiExecProcessCommandLine         | Should -BeNull
            $Result.RunnerProcessId                   | Should -Not -BeNullOrEmpty
            $Result.RunnerChildProcessId              | Should -Not -BeNullOrEmpty
            $Result.RunnerChildProcessName            | Should -Match 'MSI[A-Z0-9]{2,4}\.tmp'
            $Result.RunnerChildProcessCommandLine     | Should -Not -BeNullOrEmpty
        }

        It 'should run the MSI file with COM, and launch x86 DLL code' {
            $Result = Invoke-ATHMSI -Dll -ExecutionType COM -DLLArchitecture x64 -TestGuid $FixedTestGuid -DeleteMsi

            $Result | Should -Not -BeNullOrEmpty

            $Result

            $Result.TechniqueID                       | Should -BeExactly $ExpectedTechniqueID
            $Result.TestSuccess                       | Should -BeTrue
            $Result.TestGuid                          | Should -BeExactly $FixedTestGuid
            $Result.TestCommand                       | Should -Not -BeNullOrEmpty
            $Result.ExecutionType                     | Should -BeExactly 'COM'
            $Result.MsiAction                         | Should -BeExactly 'Install'
            $Result.MsiCustomAction                   | Should -BeExactly 'Dll'
            $Result.MsiScriptEngine                   | Should -BeNull
            $Result.MsiScriptContent                  | Should -BeNull
            $Result.MsiFilePath                       | Should -Match 'Test\.msi$'
            $Result.MsiFileHash                       | Should -Not -BeNullOrEmpty
            $Result.MsiExecProcessId                  | Should -BeNull
            $Result.MsiExecProcessCommandLine         | Should -BeNull
            $Result.RunnerProcessId                   | Should -Not -BeNullOrEmpty
            $Result.RunnerChildProcessId              | Should -Not -BeNullOrEmpty
            $Result.RunnerChildProcessName            | Should -BeExactly 'powershell.exe'
            $Result.RunnerChildProcessCommandLine     | Should -Not -BeNullOrEmpty
        }

        It 'should run the MSI file with COM, launch Executable code with MSI action - Admin' {
            $Result = Invoke-ATHMSI -Dll -ExecutionType COM -DLLArchitecture x64 -MsiAction Admin -TestGuid $FixedTestGuid -DeleteMsi

            $Result | Should -Not -BeNullOrEmpty

            $Result

            $Result.TechniqueID                       | Should -BeExactly $ExpectedTechniqueID
            $Result.TestSuccess                       | Should -BeTrue
            $Result.TestGuid                          | Should -BeExactly $FixedTestGuid
            $Result.TestCommand                       | Should -Not -BeNullOrEmpty
            $Result.ExecutionType                     | Should -BeExactly 'COM'
            $Result.MsiAction                         | Should -BeExactly 'Admin'
            $Result.MsiCustomAction                   | Should -BeExactly 'Dll'
            $Result.MsiScriptEngine                   | Should -BeNull
            $Result.MsiScriptContent                  | Should -BeNull
            $Result.MsiFilePath                       | Should -Match 'Test\.msi$'
            $Result.MsiFileHash                       | Should -Not -BeNullOrEmpty
            $Result.MsiExecProcessId                  | Should -BeNull
            $Result.MsiExecProcessCommandLine         | Should -BeNull
            $Result.RunnerProcessId                   | Should -Not -BeNullOrEmpty
            $Result.RunnerChildProcessId              | Should -Not -BeNullOrEmpty
            $Result.RunnerChildProcessName            | Should -BeExactly 'powershell.exe'
            $Result.RunnerChildProcessCommandLine     | Should -Not -BeNullOrEmpty
        }

        It 'should run the MSI file with COM, with MSI action - Admin, and launch Executable code' {
            $Result = Invoke-ATHMSI -Exe -ExecutionType COM -MsiAction Admin -TestGuid $FixedTestGuid -DeleteMsi

            $Result | Should -Not -BeNullOrEmpty

            $Result

            $Result.TechniqueID                       | Should -BeExactly $ExpectedTechniqueID
            $Result.TestSuccess                       | Should -BeTrue
            $Result.TestGuid                          | Should -BeExactly $FixedTestGuid
            $Result.TestCommand                       | Should -Not -BeNullOrEmpty
            $Result.ExecutionType                     | Should -BeExactly 'COM'
            $Result.MsiAction                         | Should -BeExactly 'Admin'
            $Result.MsiCustomAction                   | Should -BeExactly 'Exe'
            $Result.MsiScriptEngine                   | Should -BeNull
            $Result.MsiScriptContent                  | Should -BeNull
            $Result.MsiFilePath                       | Should -Match 'Test\.msi$'
            $Result.MsiFileHash                       | Should -Not -BeNullOrEmpty
            $Result.MsiExecProcessId                  | Should -BeNull
            $Result.MsiExecProcessCommandLine         | Should -BeNull
            $Result.RunnerProcessId                   | Should -Not -BeNullOrEmpty
            $Result.RunnerChildProcessId              | Should -Not -BeNullOrEmpty
            $Result.RunnerChildProcessName            | Should -Match 'MSI[A-Z0-9]{2,4}\.tmp'
            $Result.RunnerChildProcessCommandLine     | Should -Not -BeNullOrEmpty
        }

        It 'should run the MSI file with Win32API, with MSI action - Install, and launch script code' {
            $Result = Invoke-ATHMSI -ExecutionType Win32API -MsiAction Install -TestGuid $FixedTestGuid -DeleteMsi

            $Result | Should -Not -BeNullOrEmpty

            $Result

            $Result.TechniqueID                       | Should -BeExactly $ExpectedTechniqueID
            $Result.TestSuccess                       | Should -BeTrue
            $Result.TestGuid                          | Should -BeExactly $FixedTestGuid
            $Result.TestCommand                       | Should -Not -BeNullOrEmpty
            $Result.ExecutionType                     | Should -BeExactly 'Win32API'
            $Result.MsiAction                         | Should -BeExactly 'Install'
            $Result.MsiCustomAction                   | Should -BeExactly 'Script'
            $Result.MsiScriptEngine                   | Should -BeExactly 'VBScript'
            $Result.MsiScriptContent                  | Should -Not -BeNullOrEmpty
            $Result.MsiFilePath                       | Should -Match 'Test\.msi$'
            $Result.MsiFileHash                       | Should -Not -BeNullOrEmpty
            $Result.MsiExecProcessId                  | Should -BeNull
            $Result.MsiExecProcessCommandLine         | Should -BeNull
            $Result.RunnerProcessId                   | Should -Not -BeNullOrEmpty
            $Result.RunnerChildProcessId              | Should -Not -BeNullOrEmpty
            $Result.RunnerChildProcessName            | Should -Match 'powershell.exe'
            $Result.RunnerChildProcessCommandLine     | Should -Not -BeNullOrEmpty
        }

        It 'should run the MSI file with Win32API, with MSI action - Advertise, and launch an embedded Exe' {
            $Result = Invoke-ATHMSI -Exe -ExecutionType Win32API -MsiAction Advertise -TestGuid $FixedTestGuid -DeleteMsi

            $Result | Should -Not -BeNullOrEmpty

            $Result

            $Result.TechniqueID                       | Should -BeExactly $ExpectedTechniqueID
            $Result.TestSuccess                       | Should -BeTrue
            $Result.TestGuid                          | Should -BeExactly $FixedTestGuid
            $Result.TestCommand                       | Should -Not -BeNullOrEmpty
            $Result.ExecutionType                     | Should -BeExactly 'Win32API'
            $Result.MsiAction                         | Should -BeExactly 'Advertise'
            $Result.MsiCustomAction                   | Should -BeExactly 'Exe'
            $Result.MsiScriptEngine                   | Should -BeNull
            $Result.MsiScriptContent                  | Should -BeNull
            $Result.MsiFilePath                       | Should -Match 'Test\.msi$'
            $Result.MsiFileHash                       | Should -Not -BeNullOrEmpty
            $Result.MsiExecProcessId                  | Should -BeNull
            $Result.MsiExecProcessCommandLine         | Should -BeNull
            $Result.RunnerProcessId                   | Should -Not -BeNullOrEmpty
            $Result.RunnerChildProcessId              | Should -Not -BeNullOrEmpty
            $Result.RunnerChildProcessName            | Should -Match 'MSI[A-Z0-9]{2,4}\.tmp'
            $Result.RunnerChildProcessCommandLine     | Should -Not -BeNullOrEmpty
        }
    }
}