diff --git a/Get-EdrChokerDefenseStatus.ps1 b/Get-EdrChokerDefenseStatus.ps1 index 01e6245..48070d4 100644 --- a/Get-EdrChokerDefenseStatus.ps1 +++ b/Get-EdrChokerDefenseStatus.ps1 @@ -16,7 +16,7 @@ $MaxThrottleBps = 1048576 $ExactTargets = @( 'amsvc', 'cb', 'cbdefense', 'cetasevc', 'cntaosmgr', 'cramtray', 'crsvc', 'cylancesvc', 'cybereasonav', 'cyserver', 'cyveraservice', 'cyvrfsflt', - 'eiconnector', 'ekrn', 'elastic-agent', 'elastic-endpoint', 'endpointbasecamp', + 'eiconnector', 'ekrn', 'elastic-agent', 'elastic-defend', 'elastic-endpoint', 'endpointbasecamp', 'executionpreventionsvc', 'filebeat', 'fortiedr', 'hurukai', 'logprocessorservice', 'mpdefendercoreservice', 'msmpeng', 'mssense', 'ntrtscan', 'pccntmon', 'qualysagent', 'sensecncproxy', 'senseir', 'sensendr', 'sensesampleuploader', 'sentinelagent', @@ -56,17 +56,46 @@ function Test-KnownSecurityTarget { return $false } +function Get-PolicyAppPath { + param($Policy) + $p = [string]$Policy.AppPathNameMatchCondition + if ([string]::IsNullOrWhiteSpace($p)) { $p = [string]$Policy.AppPathName } + return $p +} + +function Get-PolicyThrottle { + param($Policy) + foreach ($name in @('ThrottleRateAction', 'ThrottleRateActionBitsPerSecond')) { + if ($null -ne $Policy.$name -and [uint64]$Policy.$name -gt 0) { + return [uint64]$Policy.$name + } + } + $formatted = [string]$Policy.ThrottleRate + if ($formatted -match '^(\d+)') { + return [uint64]$Matches[1] + } + return [uint64]0 +} + function Test-MaliciousQosPolicy { param($Policy) - $appPath = [string]$Policy.AppPathNameMatchCondition + $appPath = Get-PolicyAppPath -Policy $Policy if ([string]::IsNullOrWhiteSpace($appPath)) { return $false } - $throttle = [uint64]$Policy.ThrottleRateAction + $throttle = Get-PolicyThrottle -Policy $Policy if ($throttle -eq 0) { return $false } if (Test-KnownSecurityTarget -AppPath $appPath) { return $true } if ($throttle -le $MaxThrottleBps) { return $true } return $false } +function Get-AllNetQosPolicies { + $all = @() + foreach ($store in @('ActiveStore', 'GPO:localhost')) { + $all += @(Get-NetQosPolicy -PolicyStore $store -ErrorAction SilentlyContinue) + } + return $all +} + Write-Host '=== WMI subscription (root\subscription) ===' -ForegroundColor Cyan Get-WmiObject -Namespace $WmiNs -Class __IntervalTimerInstruction -ErrorAction SilentlyContinue | Where-Object { $_.TimerId -eq $TimerId } | @@ -76,6 +105,10 @@ Get-WmiObject -Namespace $WmiNs -Class __EventFilter -ErrorAction SilentlyContin Where-Object { $_.Name -eq $filterName } | Format-List Name, Query, EventNamespace +Get-WmiObject -Namespace $WmiNs -Class CommandLineEventConsumer -ErrorAction SilentlyContinue | + Where-Object { $_.Name -eq $consumerName } | + Format-List Name, CommandLineTemplate + Get-WmiObject -Namespace $WmiNs -Class ActiveScriptEventConsumer -ErrorAction SilentlyContinue | Where-Object { $_.Name -eq $consumerName } | Format-List Name, ScriptingEngine, KillTimeout @@ -85,13 +118,19 @@ Get-WmiObject -Namespace $WmiNs -Class __FilterToConsumerBinding -ErrorAction Si Format-List Filter, Consumer Write-Host '=== Policies matching hardened detection rules ===' -ForegroundColor Cyan -$all = @(Get-CimInstance -Namespace root/standardcimv2 -ClassName MSFT_NetQosPolicySettingData -ErrorAction SilentlyContinue) +$all = @(Get-AllNetQosPolicies) $malicious = @($all | Where-Object { Test-MaliciousQosPolicy -Policy $_ }) Write-Host "Count: $($malicious.Count) / $($all.Count) total QoS policies" if ($malicious.Count -gt 0) { - $malicious | Select-Object Name, AppPathNameMatchCondition, ThrottleRateAction, Owner | - Format-Table -AutoSize + $malicious | ForEach-Object { + [PSCustomObject]@{ + Name = $_.Name + AppPath = (Get-PolicyAppPath -Policy $_) + Throttle = (Get-PolicyThrottle -Policy $_) + Owner = $_.Owner + } + } | Format-Table -AutoSize } Write-Host '=== Recent remediation events (Application / EDRChokerDefense) ===' -ForegroundColor Cyan diff --git a/Install-EdrChokerWmiDefense.ps1 b/Install-EdrChokerWmiDefense.ps1 index 1631983..e2bb3c6 100644 --- a/Install-EdrChokerWmiDefense.ps1 +++ b/Install-EdrChokerWmiDefense.ps1 @@ -5,8 +5,8 @@ Registers a fileless permanent WMI subscription against EDRChoker QoS policies. .DESCRIPTION Timer-based subscription in root\subscription (no files on disk). Embedded VBScript - removes QoS policies that throttle known EDR/security agents (any rate > 0) or apply - aggressive throttling (<= 1 Mbps) to app-path policies — including paths without .exe. + enumerates QoS policies with WbemContext PolicyStore (ActiveStore + GPO:localhost); + plain ExecQuery misses ActiveStore policies created by New-NetQosPolicy / PowerShell. #> [CmdletBinding()] param( @@ -86,24 +86,11 @@ if (Get-WmiObject -Namespace $WmiNs -Class __EventFilter -Filter "Name='$FilterN $creatorSid = Get-LocalAdministratorsCreatorSid $wql = "SELECT * FROM __TimerEvent WHERE TimerID = '$TimerId'" -# Detection tiers (embedded — reinstall to update): -# 1) Known EDR/AV/SIEM process basename or vendor substring + any ThrottleRateAction > 0 -# 2) Any app-path policy with ThrottleRateAction > 0 and <= 1 Mbps (EDRChoker evasion with non-8 values) -# Path normalization strips .exe and uses the final path segment, so "C:\...\MsMpEng" still matches. $scriptText = @' On Error Resume Next Const MAX_THROTTLE_BPS = 1048576 -Dim qosSvc, policies, pol - -Set qosSvc = GetObject("winmgmts:\\.\root\standardcimv2") -Set policies = qosSvc.ExecQuery("SELECT * FROM MSFT_NetQosPolicySettingData") - -For Each pol In policies - If IsMaliciousPolicy(pol) Then RemoveMaliciousPolicy pol -Next - Sub WriteDefenseLog(eventId, entryType, message) Dim proc, pid, cmd, safe safe = message @@ -114,10 +101,24 @@ Sub WriteDefenseLog(eventId, entryType, message) proc.Create cmd, Null, Null, pid End Sub -Sub RemoveMaliciousPolicy(pol) +Function GetPolicyAppPath(pol) + Dim p + p = pol.AppPathNameMatchCondition + If Len(Trim(p)) = 0 Then p = pol.AppPathName + GetPolicyAppPath = p +End Function + +Function GetPolicyThrottle(pol) + Dim t + t = GetNumericThrottle(pol.ThrottleRateAction) + If t <= 0 Then t = GetNumericThrottle(pol.ThrottleRate) + GetPolicyThrottle = t +End Function + +Sub RemoveMaliciousPolicy(pol, storeName) Dim appPath, throttle, policyName, instanceId, tier, msg - appPath = pol.AppPathNameMatchCondition - throttle = GetNumericThrottle(pol.ThrottleRateAction) + appPath = GetPolicyAppPath(pol) + throttle = GetPolicyThrottle(pol) policyName = pol.Name instanceId = pol.InstanceID If MatchesKnownSecurityTarget(appPath) Then @@ -127,17 +128,30 @@ Sub RemoveMaliciousPolicy(pol) End If On Error Resume Next pol.Delete_ + If Err.Number <> 0 Then + Err.Clear + DeletePolicyViaPowerShell policyName, storeName + End If If Err.Number = 0 Then - msg = "action=remediate qos_policy=" & policyName & " target=" & appPath & " throttle_bps=" & throttle & " tier=" & tier & " instance_id=" & instanceId + msg = "action=remediate qos_policy=" & policyName & " target=" & appPath & " throttle_bps=" & throttle & " tier=" & tier & " store=" & storeName & " instance_id=" & instanceId WriteDefenseLog 1002, "Warning", msg Else - msg = "action=remediate_failed qos_policy=" & policyName & " target=" & appPath & " error=" & Err.Description + msg = "action=remediate_failed qos_policy=" & policyName & " target=" & appPath & " store=" & storeName & " error=" & Err.Description WriteDefenseLog 1003, "Error", msg Err.Clear End If On Error GoTo 0 End Sub +Sub DeletePolicyViaPowerShell(policyName, storeName) + Dim proc, pid, cmd, safeName, safeStore + safeName = Replace(policyName, "'", "''") + safeStore = Replace(storeName, "'", "''") + cmd = "powershell.exe -NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -Command ""Remove-NetQosPolicy -Name '" & safeName & "' -PolicyStore '" & safeStore & "' -Confirm:$false -ErrorAction SilentlyContinue""" + Set proc = GetObject("winmgmts:\\.\root\cimv2:Win32_Process") + proc.Create cmd, Null, Null, pid +End Sub + Function GetNumericThrottle(val) If IsNull(val) Or IsEmpty(val) Then GetNumericThrottle = 0 @@ -179,7 +193,7 @@ Function MatchesExactTarget(base) targets = Array( _ "amsvc", "cb", "cbdefense", "cetasevc", "cntaosmgr", "cramtray", "crsvc", _ "cylancesvc", "cybereasonav", "cyserver", "cyveraservice", "cyvrfsflt", _ - "eiconnector", "ekrn", "elastic-agent", "elastic-endpoint", "endpointbasecamp", _ + "eiconnector", "ekrn", "elastic-agent", "elastic-defend", "elastic-endpoint", "endpointbasecamp", _ "executionpreventionsvc", "filebeat", "fortiedr", "hurukai", "logprocessorservice", _ "mpdefendercoreservice", "msmpeng", "mssense", "ntrtscan", "pccntmon", "qualysagent", _ "sensecncproxy", "senseir", "sensendr", "sensesampleuploader", "sentinelagent", _ @@ -241,23 +255,44 @@ End Function Function IsMaliciousPolicy(pol) Dim appPath, throttle - appPath = pol.AppPathNameMatchCondition - throttle = GetNumericThrottle(pol.ThrottleRateAction) + IsMaliciousPolicy = False + appPath = GetPolicyAppPath(pol) + throttle = GetPolicyThrottle(pol) If Len(Trim(appPath)) = 0 Then Exit Function If throttle <= 0 Then Exit Function - ' Tier 1: any throttle on a known EDR/AV/SIEM target (catches non-8 bps evasion). If MatchesKnownSecurityTarget(appPath) Then IsMaliciousPolicy = True Exit Function End If - ' Tier 2: aggressive throttle on any app-path policy (EDRChoker-style abuse). If throttle <= MAX_THROTTLE_BPS Then IsMaliciousPolicy = True End If End Function + +Function GetPoliciesForStore(storeName) + Dim qosSvc, ctx + Set qosSvc = GetObject("winmgmts:\\.\root\standardcimv2") + Set ctx = CreateObject("WbemScripting.SWbemNamedValueSet") + ctx.Add "PolicyStore", storeName + Set GetPoliciesForStore = qosSvc.ExecQuery("SELECT * FROM MSFT_NetQosPolicySettingData", "WQL", , ctx) +End Function + +Sub RunRemediation + Dim stores, i, store, policies, pol + stores = Array("ActiveStore", "GPO:localhost") + For i = 0 To UBound(stores) + store = stores(i) + Set policies = GetPoliciesForStore(store) + For Each pol In policies + If IsMaliciousPolicy(pol) Then RemoveMaliciousPolicy pol, store + Next + Next +End Sub + +RunRemediation '@ $null = New-WmiSubscriptionObject -Component 'IntervalTimerInstruction' -ClassName __IntervalTimerInstruction -Properties @{ @@ -295,9 +330,9 @@ Write-Host 'Fileless WMI subscription installed (hardened detection, polls every Write-Host " Timer : $TimerId (${pollIntervalMs}ms)" Write-Host " Filter : $FilterName" Write-Host " Consumer : $ConsumerName (ActiveScriptEventConsumer / VBScript)" -Write-Host ' Detection : known EDR targets (any throttle) + app-path throttle <= 1 Mbps' +Write-Host ' Detection : WbemContext PolicyStore ActiveStore + GPO:localhost' Write-Host " Event log : $LogName / Source $LogSource (ID 1002 = remediate, 1003 = failed)" Write-Host '' -Write-Host 'Reinstall was required to update embedded VBScript if upgrading.' +Write-Host 'Reinstall required when upgrading embedded remediation logic.' Write-Host 'Verify: .\Get-EdrChokerDefenseStatus.ps1' Write-Host "SOC query : Get-WinEvent -FilterHashtable @{ LogName='$LogName'; ProviderName='$LogSource' } -MaxEvents 20" diff --git a/README.md b/README.md index d347065..0507bab 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ **Fileless WMI remediation for [EDRChoker](https://github.com/TwoSevenOneT/EDRChoker)** counters QoS abuse (`pacer.sys`) that throttles EDR agents to near-zero network bandwidth. -Registers a permanent subscription in `root\subscription` (no files on disk). A 5-second timer runs embedded VBScript that deletes malicious `MSFT_NetQosPolicySettingData` policies targeting known security products or aggressive app-path throttles. +Registers a permanent subscription in `root\subscription` (no files on disk). A 5-second timer runs embedded VBScript that enumerates QoS policies with **WbemContext `PolicyStore`** on **ActiveStore** and **GPO:localhost** — plain WMI `ExecQuery` misses ActiveStore policies created by `New-NetQosPolicy` / EDRChoker — and removes malicious app-path throttles targeting known security products or aggressive rates (≤ 1 Mbps). ## Scripts @@ -33,7 +33,7 @@ Each successful cleanup writes a **Warning** to the **Application** log under so | 1002 | Malicious QoS policy removed | | 1003 | Remediation failed | -**1002 example:** `action=remediate qos_policy=02zxnnzr target=elastic-endpoint.exe throttle_bps=8 tier=tier1-known-edr instance_id={...}` +**1002 example:** `action=remediate qos_policy=02zxnnzr target=elastic-endpoint.exe throttle_bps=8 tier=tier1-known-edr store=ActiveStore` ```powershell Get-WinEvent -FilterHashtable @{ LogName='Application'; ProviderName='EDRChokerDefense' } -MaxEvents 50 diff --git a/Uninstall-EdrChokerWmiDefense.ps1 b/Uninstall-EdrChokerWmiDefense.ps1 index 7bc959e..5ca6744 100644 --- a/Uninstall-EdrChokerWmiDefense.ps1 +++ b/Uninstall-EdrChokerWmiDefense.ps1 @@ -37,6 +37,7 @@ foreach ($binding in $bindings) { Write-Host 'Removed __FilterToConsumerBinding' } +Remove-WmiSubscriptionComponent -Class CommandLineEventConsumer -Filter "Name='$ConsumerName'" Remove-WmiSubscriptionComponent -Class ActiveScriptEventConsumer -Filter "Name='$ConsumerName'" Remove-WmiSubscriptionComponent -Class __EventFilter -Filter "Name='$FilterName'" Remove-WmiSubscriptionComponent -Class __IntervalTimerInstruction -Filter "TimerId='$TimerId'"