-
-
Save LM-CT/a920de2d0d6bcaff4d9d4fd037c39ae8 to your computer and use it in GitHub Desktop.
If you use Sysmon and enabled FileDelete events started with Sysmon 11, you probably came up with the issue of instantly growing hidden archive. For those who have not solved the problem yet, I came up with a PowerShell cmdlet (run as SYSTEM) based on the article https://blog.nviso.eu/2022/06/30/enforcing-a-sysmon-archive-quota/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Requires -RunAsAdministrator | |
<# | |
.Synopsis | |
Generates Sysmon Archive file quota for `File Delete` events to help managing the size. | |
.DESCRIPTION | |
Based on: https://blog.nviso.eu/2022/06/30/enforcing-a-sysmon-archive-quota/ | |
.INPUTS | |
None. Cmdlet does not accept pipe values. | |
.OUTPUTS | |
None if succeeds, exception if fails. | |
.EXAMPLE | |
New-SysmonArchiveQuota -Force | |
.EXAMPLE | |
New-SysmonArchiveQuota -ArchiveFolder Sysmon -Size 2 -Unit GB -Force | |
.LINK | |
https://gist.github.com/zbalkan/17fbe38864a900a2f1eeac2088c5d49e | |
#> | |
function New-SysmonArchiveQuota | |
{ | |
[CmdletBinding(SupportsShouldProcess=$true, | |
PositionalBinding=$false, | |
HelpUri = 'https://gist.github.com/zbalkan/17fbe38864a900a2f1eeac2088c5d49e', | |
ConfirmImpact='Medium')] | |
Param | |
( | |
# Folder name where Sysmon stores archive files | |
[Parameter(Mandatory=$false, | |
HelpMessage="Folder name where Sysmon stores archive files")] | |
[ValidateLength(0,120)] | |
[String] | |
$ArchiveFolder = 'Sysmon', | |
# Size as a `float` | |
[Parameter(Mandatory=$false)] | |
[ValidateScript({($_ -gt 0)})] | |
[float] | |
$Size = 1.0, | |
# "TB", "GB", "MB" or "KB" | |
[Parameter(Mandatory=$false)] | |
[ValidateSet("TB", "GB", "MB", "KB")] | |
$Unit = 'GB', | |
# Overwrite current quota configuration | |
[Parameter(Mandatory=$false)] | |
[switch] | |
$Force | |
) | |
Begin | |
{ | |
# Check if SYSTEM user | |
$sid = ([System.Security.Principal.WindowsIdentity]::GetCurrent()).User.Value | |
if ($sid -ne "S-1-5-18") | |
{ | |
Write-Error "The script must be run as SYSTEM. Use 'psexec -is powershell' then run the script." | |
exit 1 # Return ERROR exit code to set $LASTEXITCODE | |
} | |
$DelaySeconds = 10 | |
$LimitSizeWithUnit = "$($Size)$($Unit)" | |
} | |
Process | |
{ | |
foreach ($Drive in (Get-PSDrive -PSProvider FileSystem | Select-Object -Property Root)) | |
{ | |
$Archive = Join-Path -Path $Drive.Root.Remove(3) -ChildPath $ArchiveFolder | |
Write-Verbose "Sysmon Archive path: $Archive" | |
if ($pscmdlet.ShouldProcess($Archive, "Create archive size quota")) | |
{ | |
$FilterName = "SysmonArchiveWatcher_$($Drive.Root.Remove(1))" | |
Write-Verbose "New WMI filter: $FilterName" | |
$ConsumerName = "SysmonArchiveCleaner_$($Drive.Root.Remove(1))" | |
Write-Verbose "New WMI event: $ConsumerName" | |
$PreviousBindings = @(Get-CimInstance -Namespace root/subscription -ClassName __FilterToConsumerBinding | Where-Object {$_.Consumer.Name -Like $ConsumerName} ) | |
if ($PreviousBindings.Count -ne 0){ | |
if($Force) | |
{ | |
Write-Verbose "Removing previous filter-event binding." | |
$PreviousBindings | ForEach-Object { Remove-CimInstance $_ } | |
} | |
else | |
{ | |
Write-Warning "The WMI filter-event binding already exists. Skipping." | |
continue; | |
} | |
} | |
$PreviousFilters = @(Get-CimInstance -Namespace root/subscription -ClassName __EventFilter | Where-Object -Property Name -EQ $FilterName) | |
if ($PreviousFilters.Count -ne 0){ | |
if($Force) | |
{ | |
Write-Verbose "Removing previous WMI filter." | |
$PreviousFilters | ForEach-Object { Remove-CimInstance $_ } | |
} | |
else | |
{ | |
Write-Warning "The WMI filter $FilterName already exists. Skipping." | |
continue; | |
} | |
} | |
$PreviousConsumers = @(Get-CimInstance -Namespace root/subscription -ClassName CommandLineEventConsumer | Where-Object -Property Name -EQ $ConsumerName) | |
if ($PreviousConsumers.Count -ne 0){ | |
if($Force) | |
{ | |
Write-Verbose "Removing previous WMI event." | |
$PreviousConsumers | ForEach-Object { Remove-CimInstance $_ } | |
} | |
else | |
{ | |
Write-Warning "The consumer $ConsumerName already exists. Skipping." | |
continue; | |
} | |
} | |
# Create a WMI filter for files being created within the Sysmon archive. | |
Write-Verbose "Creating WMI Filter $FilterName" | |
$Query = "SELECT * FROM __InstanceCreationEvent WITHIN $DelaySeconds WHERE TargetInstance ISA 'CIM_DataFile' AND TargetInstance.Drive='$($Drive.Root.Remove(2))' AND TargetInstance.Path='\\$($ArchiveFolder)' GROUP WITHIN $DelaySeconds" | |
Write-Verbose "Query: $Query" | |
$Filter = New-CimInstance -Namespace root/subscription -ClassName __EventFilter -Property @{ | |
Name = $FilterName; | |
EventNameSpace = 'root\cimv2'; | |
QueryLanguage = "WQL"; | |
Query = $Query | |
} | |
Write-Verbose "Creating WMI consumer event $ConsumerName" | |
# Create a command line consumer which will clean up the Sysmon archive folder until the quota is reached. | |
$Consumer = New-CimInstance -Namespace root/subscription -ClassName CommandLineEventConsumer -Property @{ | |
Name = $ConsumerName; | |
ExecutablePath = (Get-Command PowerShell).Source; | |
CommandLineTemplate = "-NoLogo -NoProfile -NonInteractive -WindowStyle Hidden -Command `"`$Archived = Get-ChildItem -Path '$Archive' -File | Where-Object {`$_.LinkType -ne 'HardLink'} | Sort-Object -Property LastAccessTimeUtc; `$MonitoredSize = (`$Archived | Measure-Object -Sum -Property Length).Sum; for(`$Index = 0; (`$Index -lt `$Archived.Count) -and (`$MonitoredSize -gt $LimitSizeWithUnit); `$Index++){ try {`$Archived[`$Index] | Remove-Item -Force -ErrorAction Stop; `$MonitoredSize -= `$Archived[`$Index].Length} catch {}}`"" | |
} | |
# Create a WMI binding from the filter to the consumer. | |
Write-Verbose "Creating WMI binding between filter $FilterName and consumer $ConsumerName" | |
$Binding = New-CimInstance -Namespace root/subscription -ClassName __FilterToConsumerBinding -Property @{ | |
Filter = [Ref]$Filter; | |
Consumer = [Ref]$Consumer; | |
} | |
} | |
} | |
} | |
End | |
{ | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment