id: afa4cb9e-6fec-4742-a17f-f494b54c01e7
name: TI map Domain entity to Web Session Events (ASIM Web Session schema)
description: |
  'This rule identifies Web Sessions for which the target URL hostname is a known IoC. This rule uses the [Advanced Security Information Model (ASIM)](https:/aka.ms/AboutASIM) and supports any web session source that complies with ASIM.'
severity: Medium
requiredDataConnectors:
  - connectorId: SquidProxy
    dataTypes:
      - SquidProxy_CL
  - connectorId: Zscaler
    dataTypes:
      - CommonSecurityLog
  - 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 HAS_ANY_MAX = 10000;
    let dt_lookBack = 1h;
    let ioc_lookBack = 14d;
    //Create a list of TLDs in our threat feed for later validation
    let DOMAIN_TI=ThreatIntelIndicators
    // Picking up only IOC's that contain the entities we want
    | extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
    | where isnotempty(IndicatorType) and IndicatorType == "domain-name"
    | extend DomainName = tolower(ObservableValue)
    | where isnotempty(DomainName)
    | extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
    | extend IndicatorId = tostring(split(Id, "--")[2])
    | extend Url = iff(ObservableKey == "url:value", ObservableValue, "")
    | where TimeGenerated >= ago(ioc_lookBack)
    | summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id
    | where IsActive == true and ValidUntil > now();
    let DOMAIN_TI_list= todynamic(toscalar(DOMAIN_TI | summarize NIoCs = dcount(DomainName), Domains = make_set(DomainName) 
      | project Domains=iff(NIoCs > HAS_ANY_MAX, dynamic([]), Domains) ));
    DOMAIN_TI
    | project-reorder *, IsActive, Tags, TrafficLightProtocolLevel, DomainName, Type
    // using innerunique to keep perf fast and result set low, we only need one match to indicate potential malicious activity that needs to be investigated
    | join kind=innerunique (
        _Im_WebSession(starttime=ago(dt_lookBack), url_has_any= DOMAIN_TI_list )
        //Extract domain patterns from syslog message
        | extend domain = tostring(parse_url(Url)["Host"])
        | where isnotempty(domain)
        | extend tld = tostring(split(domain, '.')[-1])
        | extend Event_TimeGenerated = TimeGenerated
    ) on $left.DomainName==$right.domain
    | where Event_TimeGenerated < ValidUntil
    | summarize Event_TimeGenerated  = arg_max(Event_TimeGenerated , *) by IndicatorId, domain
    | extend Description = tostring(parse_json(Data).description)
    | extend ActivityGroupNames = extract(@"ActivityGroup:(\S+)", 1, tostring(parse_json(Data).labels))
    | project Event_TimeGenerated, Description, ActivityGroupNames, IndicatorId, ValidUntil, Confidence, domain, SrcIpAddr, Url     
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SrcIpAddr
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: Url
customDetails:
  EventTime: Event_TimeGenerated
  IoCDescription: Description
  ActivityGroupNames: ActivityGroupNames
  IndicatorId: IndicatorId
  ThreatType: ThreatType
  IoCExpirationTime: ExpirationDateTime
  IoCConfidenceScore: ConfidenceScore
alertDetailsOverride:
  alertDisplayNameFormat: A web request from {{SrcIpAddr}} to hostname  {{domain}} matched an IoC
  alertDescriptionFormat: A client with address {{SrcIpAddr}} requested the URL {{Url}}, whose hostname is a known indicator of compromise of {{ThreatType}}. Consult the threat intelligence blade for more information on the indicator.
version: 1.0.7
kind: Scheduled
