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: bc0eca2e-db50-44e6-8fa3-b85f91ff5ee7
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:
- ThreatIntelligenceIndicator
- connectorId: ThreatIntelligenceTaxii
dataTypes:
- ThreatIntelligenceIndicator
- connectorId: MicrosoftDefenderThreatIntelligence
dataTypes:
- ThreatIntelligenceIndicator
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;
ThreatIntelligenceIndicator
| where isnotempty(FileHashValue)
| where TimeGenerated > ago(ioc_lookBack)
| where FileHashValue in (Hashes)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| where Active == true and ExpirationDateTime > now()
| where Description !contains_cs "State: inactive;" and Description !contains_cs "State: falsepos;"
| join kind=innerunique (DeviceFileEvents_) on $left.FileHashValue == $right.FileHashValue
| where TimeGenerated < ExpirationDateTime
| summarize TimeGenerated = arg_max(TimeGenerated, *) by IndicatorId, DeviceId
| project TimeGenerated, TrafficLightProtocolLevel, Description, ActivityGroupNames, IndicatorId, ThreatType, FileHashValue, FileHashType, ExpirationDateTime, ConfidenceScore, 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.1
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
let Hashes
Stage 3: source
ThreatIntelligenceIndicator
Stage 4: where
where isnotempty(FileHashValue)
Stage 5: where
where TimeGenerated > ago(1209600s)
Stage 6: where
where FileHashValue =~ "Hashes"
Stage 7: summarize
summarize LatestIndicatorTime by IndicatorId
Stage 8: where
where Active =~ true
Stage 9: where
where not (Description contains "State: falsepos;") and not (Description contains "State: inactive;")
Stage 10: join
join kind=innerunique (DeviceFileEvents_) on FileHashValue, FileHashValue
Stage 11: where
where TimeGenerated < ExpirationDateTime
Stage 12: summarize
summarize TimeGenerated by IndicatorId, DeviceId
Stage 13: project
project ActionType, ActivityGroupNames, ConfidenceScore, Description, DeviceId, DeviceName, ExpirationDateTime, FileHashType, FileHashValue, FolderPath, IndicatorId, MachineGroup, RequestAccountDomain, RequestAccountName, RequestAccountSid, ThreatType, TimeGenerated, TrafficLightProtocolLevel
Stage 14: 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 |
|---|---|---|---|
Active | eq |
| field:"Active" kind:eq value:"true" |
FileHashValue | in |
| field:"FileHashValue" kind:in value:"Hashes" |
FileHashValue | is_not_null | field:"FileHashValue" kind:is_not_null | |
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:"ExpirationDateTime" |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ActionType | project |
ActivityGroupNames | project |
ConfidenceScore | project |
Description | project |
DeviceId | project |
DeviceName | project |
ExpirationDateTime | project |
FileHashType | project |
FileHashValue | project |
FolderPath | project |
IndicatorId | project |
MachineGroup | project |
RequestAccountDomain | project |
RequestAccountName | project |
RequestAccountSid | project |
ThreatType | project |
TimeGenerated | project |
TrafficLightProtocolLevel | project |
timestamp | extend |