Detection rules › Kusto

RecordedFuture Threat Hunting Url All Actors

Severity
medium
Time window
1d
Group by
ObservableValue, lowerUrl
Source
github.com/Azure/Azure-Sentinel

'Recorded Future Threat Hunting Url correlation for all actors.'

MITRE ATT&CK coverage

Rule body

id: 3f6f0d1a-f2f9-4e01-881a-c55a4a71905b
name: RecordedFuture Threat Hunting Url All Actors
description: |
  'Recorded Future Threat Hunting Url correlation for all actors.'
severity: Medium
requiredDataConnectors:
  - connectorId: ThreatIntelligenceUploadIndicatorsAPI
    dataTypes:
      - ThreatIntelIndicators
queryFrequency: 15m
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Persistence
  - PrivilegeEscalation
  - DefenseEvasion
relevantTechniques:
  - T1098
  - T1078
customDetails:
  ActorInformation: RecordedFuturePortalLink
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 1h
    matchingMethod: AllEntities
alertDetailsOverride:
  alertDisplayNameFormat: '{{Description}}'
  alertDescriptionFormat: '*{{Description}}**\n\nCorrelation found on {{Url}} from the {{Type}} table.\n'
  alertDynamicProperties:
    - alertProperty: AlertLink
      value: RecordedFuturePortalLink
query: |
  let ioc_lookBack = 1d;
  // The source table (_Im_WebSession) is a ASIM parser table, but can be replaced by any infrastructure table containing Url data.
  // The following workbook: Recorded Future - Url Correlation will help researching available data and selecting tables and columns
  _Im_WebSession
  | where isnotempty(Url)
  | extend lowerUrl=tolower(Url)
  | join kind=inner (
  ThreatIntelIndicators
  // Only look for IOCs
  | where ObservableKey == 'url:value'
  | where isnotempty(ObservableValue)
  // Only look at Recorded Future Threat Hunt Indicators.
  | where Data.description startswith "Recorded Future - Threat Hunt"
  // Only work with the latest indicators
  | where TimeGenerated >= ago(ioc_lookBack)
  | summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id
  | where IsActive == true and ValidUntil > now()
  | extend lowerUrl=tolower(ObservableValue)
  ) on lowerUrl
  // select column from the source table to match with Recorded Future ThreatIntelIndicators $left.Url
  | mv-expand Label=Data.labels
  | extend RecordedFuturePortalLink = parse_json(tostring(Label)).RecordedFuturePortalLink
   | where isnotempty(RecordedFuturePortalLink)
   | summarize arg_max(TimeGenerated, *) by ObservableValue
  | project Url=ObservableValue, Description=Data.description, Type, TimeGenerated, RecordedFuturePortalLink
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: Url
version: 1.1.1
kind: Scheduled

Stages and Predicates

Stage 0: let

let ioc_lookBack = 1d;

Stage 1: source

_Im_WebSession

Stage 2: where

| where isnotempty(Url)

Stage 3: extend

| extend lowerUrl=tolower(Url)

Stage 4: join

| join kind=inner (
ThreatIntelIndicators
| where ObservableKey == 'url:value'
| where isnotempty(ObservableValue)
| where Data.description startswith "Recorded Future - Threat Hunt"
| where TimeGenerated >= ago(ioc_lookBack)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id
| where IsActive == true and ValidUntil > now()
| extend lowerUrl=tolower(ObservableValue)
) on lowerUrl

Stage 5: mv-expand

mv-expand Label

Stage 6: extend

extend RecordedFuturePortalLink

Stage 7: where

where isnotempty(RecordedFuturePortalLink)

Stage 8: summarize

summarize by ObservableValue

Stage 9: project

project Description, RecordedFuturePortalLink, TimeGenerated, Type, Url

Indicators

These rows show field, operator, and value matches.

Output fields

These fields are emitted when the rule matches.

FieldSource
Descriptionproject
RecordedFuturePortalLinkproject
TimeGeneratedproject
Typeproject
Urlproject