dissect.target icon indicating copy to clipboard operation
dissect.target copied to clipboard

CIM: `consumerbindings()` Only scans default `subscription` namespace

Open nbareil opened this issue 1 month ago • 0 comments

Summary

The consumerbindings method in the Windows CIM plugin currently only inspects the default subscription namespace:

https://github.com/fox-it/dissect.target/blob/69c908a42bf3e988b7e0e18bd16910c7389c77d2/dissect/target/plugins/os/windows/cim.py#L122

As a result, it misses legitimate or malicious __FilterToConsumerBinding instances created in other namespaces (e.g. root/another).

This anti-forensic TTP enables the threat actor to remain invisible on most security tools.

Steps to Reproduce

You can reproduce the issue by creating a WMI event binding in a non-standard namespace:

1. Create Event Filter in a custom namespace
   $FilterArgs = @{
       Name = 'Pentestlab-WMI'
       EventNameSpace = 'root/another'
       QueryLanguage = 'WQL'
       Query = "SELECT \* FROM __InstanceCreationEvent WITHIN 3 WHERE TargetInstance ISA 'Win32_Process' AND TargetInstance.Name = 'msedge.exe'"
   }
   $Filter = Get-CimInstance -Namespace 'root/another' -ClassName __EventFilter |
       Where-Object {
           $_.Name -eq 'Pentestlab-WMI' -and
           $_.Query -match "msedge.exe"
       }

1. Create Command Line Consumer
   $ConsumerArgs = @{
       Name = 'Pentestlab-WMI'
       CommandLineTemplate = "$($Env:SystemRoot)
   System32
   shell64.exe"
   }
   $Consumer = New-CimInstance -Namespace 'root/default' -ClassName CommandLineEventConsumer -Property $ConsumerArgs

1. Create Filter-to-Consumer Binding in the same custom namespace
   $FilterToConsumerArgs = @{
       Filter = [Ref] $Filter
       Consumer = [Ref] $Consumer
   }
   $FilterToConsumerBinding = New-CimInstance -Namespace 'root/another' -ClassName __FilterToConsumerBinding -Property $FilterToConsumerArgs

Attached is the resulting WMI repository: sample.zip

Test Script

Using the test script reproduce_cim_error.py:

% python /tmp/reproduce_cim_error.py /tmp/sample.zip
Extracted files: ['MAPPING1.MAP', 'MAPPING2.MAP', 'MAPPING3.MAP', 'OBJECTS.DATA', 'INDEX.BTR']
CIM repository opened successfully
Found 0 consumer bindings

Expected Behavior

The output should list bindings found in all namespaces, not just the default one. For example:

% python /tmp/reproduce_cim_error.py /tmp/sample.zip
Extracted files: ['MAPPING1.MAP', 'MAPPING2.MAP', 'MAPPING3.MAP', 'OBJECTS.DATA', 'INDEX.BTR']
CIM repository opened successfully
Found 3 consumer bindings
Filter: SCM Event Log Filter, Consumer: <dissect.cim.cim.Instance object at 0x10255c550>
Filter: Pentestlab-WMI, Consumer: <dissect.cim.cim.Instance object at 0x1025582b0>
Filter: Pentestlab-WMI, Consumer: <dissect.cim.cim.Instance object at 0x102610dd0>

Suggested Fix

Iterate over *all available namespaces* rather than assuming subscription as the default. This could be done by dynamically discovering namespaces in the plugin logic.

nbareil avatar Nov 01 '25 14:11 nbareil