Detection rules › Kusto

ADWS Connection from Process Injection Target

Group by
DeviceId, DeviceName, FileName, InitiatingProcessFileName, InitiatingProcessId, ProcessId
Author
FalconForce
Source
github.com/FalconForceTeam/FalconFriday

The query first collects all network connections to the Active Directory Web Services (ADWS) service. It then searches for processes that inject into a process that makes a connection to ADWS. This can be used to detect process injection into a process that is used to query Active Directory.

MITRE ATT&CK coverage

References

Event coverage

Rule body kusto

let timeframe = 2*1h;
let ADWSConnections=(
    DeviceNetworkEvents
    | where ingestion_time() >= ago(timeframe)
    | where ActionType == "ConnectionSuccess"
    | where RemotePort == 9389
    | extend InitiatingProcessFileName=tolower(InitiatingProcessFileName)
);
let ADWSFileNames=materialize(
    ADWSConnections
    | distinct InitiatingProcessFileName
);
// Look for processes that inject into a process that makes an ADWS connection.
let InjectorProcesses=materialize(
    DeviceEvents
    | where ingestion_time() >= ago(timeframe)
    | where not(isempty(FileName))
    // Look for actions associated with process injection.
    | where ActionType in~ ("CreateRemoteThreadApiCall", "MemoryRemoteProtect","NtAllocateVirtualMemoryApiCall", "NtAllocateVirtualMemoryRemoteApiCall", "NtMapViewOfSectionRemoteApiCall", "NtProtectVirtualMemoryApiCall", "SetThreadContextRemoteApiCall", "QueueUserApcRemoteApiCall")
    | where FileName in~ (ADWSFileNames)
    | where not(InitiatingProcessFolderPath startswith @"c:\program files\vmware\vmware tools" and InitiatingProcessFileName =~ "vmtoolsd.exe")
    | where not(InitiatingProcessFolderPath =~ @"C:\Windows\System32" and InitiatingProcessFileName =~ "csrss.exe")
    | where not(InitiatingProcessFolderPath =~ @"C:\Windows\System32\csrss.exe")
    | where not(InitiatingProcessFolderPath startswith @"c:\program files\microsoft azure ad sync\" and InitiatingProcessFileName =~ "miiserver.exe")
    | extend FileName=tolower(FileName)
    | lookup kind=inner ADWSConnections on DeviceId, $left.ProcessId == $right.InitiatingProcessId, $left.FileName == $right.InitiatingProcessFileName
    // Begin environment-specific filter.
    // End environment-specific filter.
    | project DeviceId, ProcessId, FileName, InjectorProcessId=InitiatingProcessId, InjectorFileName=InitiatingProcessFileName, InjectorActionType=ActionType
);
// Find ADWS connections from processes that were injected into by another process.
ADWSConnections
| lookup kind=inner InjectorProcesses on DeviceId, $left.InitiatingProcessId == $right.ProcessId, $left.InitiatingProcessFileName == $right.FileName
| summarize arg_min(Timestamp,*), InjectionMethods=make_set(InjectorActionType) by DeviceId, DeviceName, InitiatingProcessId, InitiatingProcessFileName
| extend HostName=tostring(split(DeviceName,".")[0]),DnsDomain=iif(DeviceName contains ".", substring(DeviceName, indexof(DeviceName, ".") + 1, strlen(DeviceName)),"")
// Begin environment-specific filter.
// End environment-specific filter.

Stages and Predicates

Parameters

let timeframe = 2*1h;

Let binding: ADWSFileNames

let ADWSFileNames = materialize(
    ADWSConnections
    | distinct InitiatingProcessFileName
);

Derived from ADWSConnections.

Let binding: InjectorProcesses

let InjectorProcesses = materialize(
    DeviceEvents
    | where ingestion_time() >= ago(timeframe)
    | where not(isempty(FileName))
    | where ActionType in~ ("CreateRemoteThreadApiCall", "MemoryRemoteProtect","NtAllocateVirtualMemoryApiCall", "NtAllocateVirtualMemoryRemoteApiCall", "NtMapViewOfSectionRemoteApiCall", "NtProtectVirtualMemoryApiCall", "SetThreadContextRemoteApiCall", "QueueUserApcRemoteApiCall")
    | where FileName in~ (ADWSFileNames)
    | where not(InitiatingProcessFolderPath startswith @"c:\program files\vmware\vmware tools" and InitiatingProcessFileName =~ "vmtoolsd.exe")
    | where not(InitiatingProcessFolderPath =~ @"C:\Windows\System32" and InitiatingProcessFileName =~ "csrss.exe")
    | where not(InitiatingProcessFolderPath =~ @"C:\Windows\System32\csrss.exe")
    | where not(InitiatingProcessFolderPath startswith @"c:\program files\microsoft azure ad sync\" and InitiatingProcessFileName =~ "miiserver.exe")
    | extend FileName=tolower(FileName)
    | lookup kind=inner ADWSConnections on DeviceId, $left.ProcessId == $right.InitiatingProcessId, $left.FileName == $right.InitiatingProcessFileName
    | project DeviceId, ProcessId, FileName, InjectorProcessId=InitiatingProcessId, InjectorFileName=InitiatingProcessFileName, InjectorActionType=ActionType
);

Derived from timeframe, ADWSConnections, ADWSFileNames.

The stages below define let ADWSConnections (the rule's main pipeline source).

Stage 1: source

DeviceNetworkEvents

Stage 2: where

| where ingestion_time() >= ago(timeframe)

Stage 3: where

| where ActionType == "ConnectionSuccess"

Stage 4: where

| where RemotePort == 9389

Stage 5: extend

| extend InitiatingProcessFileName=tolower(InitiatingProcessFileName)

The stages below run on ADWSConnections (the outer pipeline).

Stage 6: kusto:lookup

ADWSConnections
| lookup kind=inner InjectorProcesses on DeviceId, $left.InitiatingProcessId == $right.ProcessId, $left.InitiatingProcessFileName == $right.FileName

Stage 7: summarize

| summarize arg_min(Timestamp,*), InjectionMethods=make_set(InjectorActionType) by DeviceId, DeviceName, InitiatingProcessId, InitiatingProcessFileName

Stage 8: extend

| extend HostName=tostring(split(DeviceName,".")[0]),DnsDomain=iif(DeviceName contains ".", substring(DeviceName, indexof(DeviceName, ".") + 1, strlen(DeviceName)),"")

Indicators

Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.

FieldKindValues
ActionTypeeq
  • ConnectionSuccess transforms: cased corpus 9 (kusto 9)
RemotePorteq
  • 9389 transforms: cased corpus 5 (kusto 2, sigma 1, elastic 1, splunk 1)

Output fields

Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.

FieldSource
DeviceIdsummarize
DeviceNamesummarize
InitiatingProcessFileNamesummarize
InitiatingProcessIdsummarize
InjectionMethodssummarize
DnsDomainextend
HostNameextend