Windows.System.IsClrProcess

Identify running processes that host the .NET Common Language Runtime (CLR) and highlight suspicious processes where CLR visibility may be reduced, downgraded, or disabled.

Capablities include:

  • CLR capable process discovery by finding mscoree.dll import
  • Brief CLR Rundown ETW collection to determine events active
  • Runs Mem2Disk targetting commonly CLR patched DLLs
  • Checks COMPlus_ETWEnabled environment variable
  • Collects CLR.dll VersionInformation
  • CommandLine visibility

NOTE: for Mem2Disk capability, the artifact expects the imported name: Windows.Memory.Mem2Disk.


name: Windows.System.IsClrProcess
author: Matt Green - @mgreen27
description: |
  Identify running processes that host the .NET Common Language Runtime (CLR) and
  highlight suspicious processes where CLR visibility may be reduced, downgraded, 
  or disabled.

  Capablities include:
  - CLR capable process discovery by finding `mscoree.dll` import
  - Brief CLR Rundown ETW collection to determine events active
  - Runs Mem2Disk targetting commonly CLR patched DLLs
  - Checks COMPlus_ETWEnabled environment variable
  - Collects CLR.dll VersionInformation
  - CommandLine visibility

  NOTE: for Mem2Disk capability, the artifact expects the imported name: Windows.Memory.Mem2Disk.
  
parameters:
- name: ProcessRegex
  default: .
  type: regex
- name: PidRegex
  default: .
  type: regex
- name: ExePathRegex
  default: .
  type: regex
- name: CommandLineRegex
  default: .
  type: regex
- name: UsernameRegex
  default: .
  type: regex
- name: CheckForPatches
  type: bool
- name: ModuleRegEx
  default: \\(clr|amsi|ntdll|kernel32|Mp0av.dll|wldap32)\.dll$
  type: regex

sources:
  - precondition: SELECT OS From info() where OS = 'windows'

    query: |
        LET clr_processes <= SELECT Pid, Ppid, TokenIsElevated, Name, Exe, CommandLine,Username,
                parse_pe(file=Exe) as PEInfo
            FROM pslist()
            WHERE Name =~ ProcessRegex
                AND Pid =~ PidRegex
                AND Exe =~ ExePathRegex
                AND CommandLine =~ CommandLineRegex
                AND Username =~ UsernameRegex
                AND ( PEInfo.Imports =~ 'mscoree' 
                    OR PEInfo.Directories.DotNet_Directory )
                
        LET clr_details <= SELECT
                System.ProcessID AS ProcessID,
                dict(
                    ClrInstanceID=EventData.ClrInstanceID,
                    ClrVersion=format(
                        format='%s.%s.%s.%s',
                            args=[
                                EventData.VMMajorVersion, 
                                EventData.VMMinorVersion,
                                EventData.VMBuildNumber, 
                                EventData.VMQfeNumber]),
                    RuntimeDllPath=EventData.RuntimeDllPath
                        ) as ClrDetails
        FROM watch_etw(description="CLR Rundown Provider",
                       guid="{A669021C-C450-4609-A035-5AF59AF4DF18}",
                       any=0x40,
                       timeout=20 )
        WHERE ProcessID =~ join(array=clr_processes.Pid,sep='|')
            AND System.ID = 187 
        
        LET find_clr(pid) = SELECT * FROM clr_details WHERE ProcessID = pid
        
        LET results <= SELECT *, 
                find_clr(pid=Pid).ClrDetails as ClrDetails
            FROM clr_processes
        
        LET find_dll(pid,modulename) = SELECT dict(ModulePath=ExePath) + parse_pe(file=ExePath).VersionInformation as VersionInformation
                FROM modules(pid=Pid)
            WHERE lowcase(string=ModuleName) = lowcase(string=modulename)
        
        
        LET find_functions(pid) = SELECT regex_replace(
                    source=split(string=Functions,sep_string='+')[0],
                    re='''\!0x.+$''',
                    replace="!0xHexAddr"
                        ) as Functions
            FROM flatten(query={ 
                SELECT Differences.Func As Functions 
                FROM Artifact.Windows.Memory.Mem2Disk(
                                PidFilter="^" + pid + "$",
                                ModuleRegEx=ModuleRegEx 
                            )
            })
            GROUP BY Functions
        LET find_modules(pid) = SELECT Path 
            FROM Artifact.Windows.Memory.Mem2Disk(
                                PidFilter="^" + pid + "$",
                                ModuleRegEx=ModuleRegEx 
                            )
            GROUP BY Path
            
        LET check_complus(pid) = SELECT 
                if(condition = Env.COMPlus_ETWEnabled="0",
                        then = TRUE,
                        else = FALSE ) as EnvDisabled
            FROM Artifact.Windows.Memory.ProcessInfo(PidRegex=pid)

        
        SELECT  
            Pid, Ppid, Name, Exe, CommandLine,
            Username, TokenIsElevated,
            filter(list=PEInfo.Imports,regex='mscoree')[0] as MscoreeImport,
            if(condition=ClrDetails,
                then=ClrDetails[0],
                else='ETW disabled or Potential downgraded CLR') as Functions,
            find_dll(pid=Pid,modulename="clr.dll")[0].VersionInformation as CLRmodule,
            if(condition= version(function="describe_address") != NULL,
                    then= find_functions(pid=str(str=Pid)).Functions,
                    else= find_modules(pid=str(str=Pid)).Path  
                        ) as PotentialPatches,
            check_complus(pid=str(str=Pid))[0].EnvDisabled as EnvDisabled
        FROM results