Detection rules › Kusto
Hijack Execution Flow - DLL Side-Loading
This detection tries to identify all DLLs loaded by "high integrity" processes and cross-checks the DLL paths against FileCreate/FileModify events of the same DLL by a medium integrity process. Of course, we need to do some magic to filter out false positives as much as possible. So any FileCreate/FileModify done by "NT Authoriy\System" and the "RID 500" users aren't interesting. Also, we only want to see the FileCreate/FileModify actions which are performed with a default or limited token elevation. If done with a full elevated token, the user is apparently admin already.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Stealth | T1574.002 Hijack Execution Flow: DLL Side-Loading |
Event coverage
| Provider | Event/ActionType | Title |
|---|---|---|
| Sysmon | Event ID 7 | Image loaded |
| Defender-DeviceImageLoadEvents | any | Image load (any) |
Rule body kusto
id: 3084b487-fad6-4000-9544-6085b9657290
name: Hijack Execution Flow - DLL Side-Loading
description: |
This detection tries to identify all DLLs loaded by "high integrity" processes and cross-checks the DLL paths against FileCreate/FileModify events of the same DLL by a medium integrity process.
Of course, we need to do some magic to filter out false positives as much as possible. So any FileCreate/FileModify done by "NT Authoriy\System" and the "RID 500" users aren't interesting.
Also, we only want to see the FileCreate/FileModify actions which are performed with a default or limited token elevation. If done with a full elevated token, the user is apparently admin already.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceFileEvents
- DeviceImageLoadEvents
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
- PrivilegeEscalation
- DefenseEvasion
relevantTechniques:
- T1574.002
query: |
let imls = materialize(
DeviceImageLoadEvents
| where InitiatingProcessIntegrityLevel in ("High", "System") and FileName !endswith ".exe"
| project FolderPath=tolower(FolderPath), InitiatingProcessFileName, InitiatingProcessIntegrityLevel, DeviceId, DeviceName
| distinct FolderPath, InitiatingProcessFileName, InitiatingProcessIntegrityLevel, DeviceId, DeviceName
);
imls
| join (
DeviceFileEvents
| where FolderPath in~ ((imls | project FolderPath)) and ActionType in ("FileCreated", "FileModified") and
InitiatingProcessIntegrityLevel !in ("High", "System", "") and InitiatingProcessAccountSid != "S-1-5-18" and
InitiatingProcessTokenElevation in ("TokenElevationTypeDefault", "TokenElevationTypeLimited") and InitiatingProcessAccountSid !endswith "-500"
| extend FolderPath=tolower(FolderPath)
) on FolderPath, DeviceId, DeviceName
| project-away FolderPath1
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: DeviceName
- entityType: Account
fieldMappings:
- identifier: Sid
columnName: InitiatingProcessAccountSid
- identifier: Name
columnName: InitiatingProcessAccountName
- identifier: NTDomain
columnName: InitiatingProcessAccountDomain
- entityType: Process
fieldMappings:
- identifier: CommandLine
columnName: InitiatingProcessCommandLine
version: 1.0.1
kind: Scheduled
Stages and Predicates
Stage 0: let
let imls = materialize(<inlined as stages below>);
The stages below define let imls (the rule's main pipeline source).
Stage 1: source
DeviceImageLoadEvents
Stage 2: where
| where InitiatingProcessIntegrityLevel in ("High", "System") and FileName !endswith ".exe"
Stage 3: project
| project FolderPath=tolower(FolderPath), InitiatingProcessFileName, InitiatingProcessIntegrityLevel, DeviceId, DeviceName
Stage 4: distinct
| distinct FolderPath, InitiatingProcessFileName, InitiatingProcessIntegrityLevel, DeviceId, DeviceName
The stages below run on imls (the outer pipeline).
Stage 5: join
imls
| join (
DeviceFileEvents
| where FolderPath in~ ((imls | project FolderPath)) and ActionType in ("FileCreated", "FileModified") and
InitiatingProcessIntegrityLevel !in ("High", "System", "") and InitiatingProcessAccountSid != "S-1-5-18" and
InitiatingProcessTokenElevation in ("TokenElevationTypeDefault", "TokenElevationTypeLimited") and InitiatingProcessAccountSid !endswith "-500"
| extend FolderPath=tolower(FolderPath)
) on FolderPath, DeviceId, DeviceName
Stage 6: project-away
| project-away FolderPath1
Exclusions
Top-level NOT(...) conjuncts: predicates this rule actively suppresses.
| Field | Kind | Excluded values |
|---|---|---|
FileName | ends_with | .exe |
InitiatingProcessAccountSid | ends_with | -500 |
InitiatingProcessIntegrityLevel | in | High, System |
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.
| Field | Kind | Values |
|---|---|---|
ActionType | in |
|
InitiatingProcessAccountSid | ne |
|
InitiatingProcessIntegrityLevel | in |
|
InitiatingProcessTokenElevation | in |
|
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.
| Field | Source |
|---|---|
DeviceId | project |
DeviceName | project |
FolderPath | project |
InitiatingProcessFileName | project |
InitiatingProcessIntegrityLevel | project |