Detection rules › Kusto
TI Map File Hash to DeviceFileEvents Event
Identifies a match in DeviceFileEvents Event data from any FileHash IOC from TI
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Telemetry coverage
| Platform | Record / event type |
|---|---|
| Microsoft Defender for Endpoint | DeviceFileEvents action any: File activity |
Rule body
id: d6f04915-4471-4cb3-b163-a8b72997cf72
name: TI Map File Hash to DeviceFileEvents Event
description: |
'Identifies a match in DeviceFileEvents Event data from any FileHash IOC from TI'
severity: Medium
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceFileEvents
- connectorId: ThreatIntelligence
dataTypes:
- ThreatIntelIndicators
- connectorId: ThreatIntelligenceTaxii
dataTypes:
- ThreatIntelIndicators
- connectorId: MicrosoftDefenderThreatIntelligence
dataTypes:
- ThreatIntelIndicators
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1071
query: |
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let DeviceFileEvents_ = (union
(DeviceFileEvents | where TimeGenerated > ago(dt_lookBack) | where isnotempty(SHA1) | extend FileHashValue = SHA1),
(DeviceFileEvents | where TimeGenerated > ago(dt_lookBack) | where isnotempty(SHA256) | extend FileHashValue = SHA256));
let Hashes = DeviceFileEvents_ | distinct FileHashValue;
ThreatIntelIndicators
//extract key part of kv pair
| extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
| where IndicatorType == "file"
| extend FileHashType = replace("'", "", substring(ObservableKey, indexof(ObservableKey, "hashes.") + 7, strlen(ObservableKey) - indexof(ObservableKey, "hashes.") - 7))
| extend FileHashValue = ObservableValue
| extend IndicatorId = tostring(split(Id, "--")[2])
| where isnotempty(FileHashValue)
| where TimeGenerated > ago(ioc_lookBack)
// | where FileHashValue in (Hashes)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
| where IsActive and (ValidUntil > now() or isempty(ValidUntil))
| extend Description = tostring(parse_json(Data).description)
| where Description !contains_cs "State: inactive;" and Description !contains_cs "State: falsepos;"
| project-reorder *, FileHashType, FileHashValue, Type
| join kind=innerunique (DeviceFileEvents_) on $left.FileHashValue == $right.FileHashValue
| where TimeGenerated < ValidUntil
| summarize TimeGenerated = arg_max(TimeGenerated, *) by IndicatorId, DeviceId
| extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
| extend Description = tostring(parse_json(Data).description)
| extend ActivityGroupNames = extract(@"ActivityGroup:(\S+)", 1, tostring(parse_json(Data).labels))
| project TimeGenerated, TrafficLightProtocolLevel, Description, ActivityGroupNames, IndicatorId, FileHashValue, FileHashType, ValidUntil, Confidence, ActionType, DeviceId, DeviceName, FolderPath, RequestAccountDomain, RequestAccountName, RequestAccountSid, MachineGroup
| extend timestamp = TimeGenerated
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: RequestAccountName
- identifier: Sid
columnName: RequestAccountSid
- identifier: NTDomain
columnName: RequestAccountDomain
- entityType: FileHash
fieldMappings:
- identifier: Value
columnName: FileHashValue
- identifier: Algorithm
columnName: FileHashType
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: DeviceName
version: 1.0.4
kind: Scheduled
Stages and Predicates
Parameters
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
Let binding: DeviceFileEvents_
let DeviceFileEvents_ = (union
(DeviceFileEvents | where TimeGenerated > ago(dt_lookBack) | where isnotempty(SHA1) | extend FileHashValue = SHA1),
(DeviceFileEvents | where TimeGenerated > ago(dt_lookBack) | where isnotempty(SHA256) | extend FileHashValue = SHA256));
Let binding: Hashes
let Hashes = DeviceFileEvents_ | distinct FileHashValue;
Stage 1: source
let DeviceFileEvents_
Stage 2: source
ThreatIntelIndicators
Stage 3: extend
extend IndicatorType
Stage 4: where
where IndicatorType =~ "file"
Stage 5: extend (3 consecutive steps)
extend FileHashType, FileHashValue, IndicatorId
Stage 6: where
where isnotempty(FileHashValue)
Stage 7: where
where TimeGenerated > ago(1209600s)
Stage 8: summarize
summarize LatestIndicatorTime by Id, ObservableValue
Stage 9: where
where (isempty(ValidUntil) or ValidUntil > now())
Stage 10: extend
extend Description
Stage 11: where
where not (Description contains "State: falsepos;") and not (Description contains "State: inactive;")
Stage 12: project-reorder
project-reorder
Stage 13: join
join kind=innerunique (DeviceFileEvents_) on FileHashValue, FileHashValue
Stage 14: where
where TimeGenerated < ValidUntil
Stage 15: summarize
summarize TimeGenerated by IndicatorId, DeviceId
Stage 16: extend (3 consecutive steps)
extend ActivityGroupNames, Description, TrafficLightProtocolLevel
Stage 17: project
project ActionType, ActivityGroupNames, Confidence, Description, DeviceId, DeviceName, FileHashType, FileHashValue, FolderPath, IndicatorId, MachineGroup, RequestAccountDomain, RequestAccountName, RequestAccountSid, TimeGenerated, TrafficLightProtocolLevel, ValidUntil
Stage 18: extend
extend timestamp
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
Description | contains | State: falsepos; | excludes:Description field:"Description" value:"State: falsepos;" |
Description | contains | State: inactive; | excludes:Description field:"Description" value:"State: inactive;" |
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
FileHashValue | is_not_null | field:"FileHashValue" kind:is_not_null | |
IndicatorType | eq |
| field:"IndicatorType" kind:eq value:"file" |
SHA1 | is_not_null | field:"sha1" kind:is_not_null | |
SHA256 | is_not_null | field:"sha256" kind:is_not_null | |
TimeGenerated | cross_field_compare |
| field:"TimeGenerated" kind:cross_field_compare value:"ValidUntil" |
ValidUntil | is_null | field:"ValidUntil" kind:is_null |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ActionType | project |
ActivityGroupNames | project |
Confidence | project |
Description | project |
DeviceId | project |
DeviceName | project |
FileHashType | project |
FileHashValue | project |
FolderPath | project |
IndicatorId | project |
MachineGroup | project |
RequestAccountDomain | project |
RequestAccountName | project |
RequestAccountSid | project |
TimeGenerated | project |
TrafficLightProtocolLevel | project |
ValidUntil | project |
timestamp | extend |