Microsoft Sentinel Analytic Rules
cloudbrothers.infoAzure Sentinel RepoToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

TI map IP entity to OfficeActivity

Back
Idf50280e5-5eb1-4e95-99fd-9d584a987bdd
RulenameTI map IP entity to OfficeActivity
DescriptionThis query maps any IP indicators of compromise (IOCs) from threat intelligence (TI), by searching for matches in OfficeActivity.
SeverityMedium
TacticsCommandAndControl
TechniquesT1071
Required data connectorsMicrosoftDefenderThreatIntelligence
Office365
ThreatIntelligence
ThreatIntelligenceTaxii
KindScheduled
Query frequency1h
Query period14d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Threat Intelligence (NEW)/Analytic Rules/IPEntity_OfficeActivity.yaml
Version1.4.5
Arm templatef50280e5-5eb1-4e95-99fd-9d584a987bdd.json
Deploy To Azure
let dt_lookBack = 1h; // Look back 1 hour for OfficeActivity events
let ioc_lookBack = 14d; // Look back 14 days for threat intelligence indicators
let OfficeActivity_ = materialize(OfficeActivity
  | where isnotempty(ClientIP)
  | where TimeGenerated >= ago(dt_lookBack)
  | extend ClientIPValues = extract_all(@'\[?(::ffff:)?(?P<IPAddress>(\d+\.\d+\.\d+\.\d+)|[^\]%]+)(%\d+)?\]?([-:](?P<Port>\d+))?', dynamic(["IPAddress", "Port"]), ClientIP)[0]
  | extend IPAddress = iff(array_length(ClientIPValues) > 0, tostring(ClientIPValues[0]), '')
  | project-rename OfficeActivity_TimeGenerated = TimeGenerated);
let ActivityIPs = OfficeActivity_ | summarize IPs = make_list(IPAddress);
// Fetch threat intelligence indicators related to IP addresses
let IP_Indicators = materialize(ThreatIntelIndicators
//extract key part of kv pair
     | extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
     | where isnotempty(IndicatorType) and IndicatorType == "ipv4-addr"
     | extend NetworkSourceIP = toupper(ObservableValue)
     | extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
  | where isnotempty(NetworkSourceIP)
  | where TimeGenerated >= ago(ioc_lookBack)
  | extend TI_ipEntity = NetworkSourceIP
  | where TI_ipEntity in (ActivityIPs)
  | extend Url = iff(ObservableKey == "url:value", ObservableValue, "")
  | summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id
  | where  IsActive == true and ValidUntil > now()
  | extend Description = tostring(parse_json(Data).description)
  | where Description !contains_cs "State: inactive;" and Description !contains_cs "State: falsepos;");
IP_Indicators
   | project-reorder *, Tags, TrafficLightProtocolLevel, NetworkSourceIP, Type, TI_ipEntity
// Use innerunique to keep performance fast and result set low, as we only need one match to indicate potential malicious activity that needs investigation
| join kind=innerunique (OfficeActivity_)
  on $left.TI_ipEntity == $right.IPAddress
// Filter out OfficeActivity events that occurred after the expiration of the corresponding indicator
| where OfficeActivity_TimeGenerated < ValidUntil
// Group the results by IndicatorId and keep the OfficeActivity event with the latest timestamp
| summarize OfficeActivity_TimeGenerated = arg_max(OfficeActivity_TimeGenerated, *) by Id
// Select the desired output fields
| extend Description = tostring(parse_json(Data).description)
| extend ActivityGroupNames = extract(@"ActivityGroup:(\S+)", 1, tostring(parse_json(Data).labels))
| project OfficeActivity_TimeGenerated, Description, ActivityGroupNames, Id, ValidUntil, Confidence, TI_ipEntity, ClientIP, UserId, Operation, ResultStatus, RecordType, OfficeObjectId, NetworkSourceIP, Type, Url
| extend timestamp = OfficeActivity_TimeGenerated, Name = tostring(split(UserId, '@', 0)[0]), UPNSuffix = tostring(split(UserId, '@', 1)[0])
name: TI map IP entity to OfficeActivity
relevantTechniques:
- T1071
requiredDataConnectors:
- dataTypes:
  - ThreatIntelligenceIndicator
  connectorId: ThreatIntelligence
- dataTypes:
  - ThreatIntelligenceIndicator
  connectorId: ThreatIntelligenceTaxii
- dataTypes:
  - ThreatIntelligenceIndicator
  connectorId: MicrosoftDefenderThreatIntelligence
- dataTypes:
  - OfficeActivity
  connectorId: Office365
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Threat Intelligence (NEW)/Analytic Rules/IPEntity_OfficeActivity.yaml
query: |
  let dt_lookBack = 1h; // Look back 1 hour for OfficeActivity events
  let ioc_lookBack = 14d; // Look back 14 days for threat intelligence indicators
  let OfficeActivity_ = materialize(OfficeActivity
    | where isnotempty(ClientIP)
    | where TimeGenerated >= ago(dt_lookBack)
    | extend ClientIPValues = extract_all(@'\[?(::ffff:)?(?P<IPAddress>(\d+\.\d+\.\d+\.\d+)|[^\]%]+)(%\d+)?\]?([-:](?P<Port>\d+))?', dynamic(["IPAddress", "Port"]), ClientIP)[0]
    | extend IPAddress = iff(array_length(ClientIPValues) > 0, tostring(ClientIPValues[0]), '')
    | project-rename OfficeActivity_TimeGenerated = TimeGenerated);
  let ActivityIPs = OfficeActivity_ | summarize IPs = make_list(IPAddress);
  // Fetch threat intelligence indicators related to IP addresses
  let IP_Indicators = materialize(ThreatIntelIndicators
  //extract key part of kv pair
       | extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
       | where isnotempty(IndicatorType) and IndicatorType == "ipv4-addr"
       | extend NetworkSourceIP = toupper(ObservableValue)
       | extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
    | where isnotempty(NetworkSourceIP)
    | where TimeGenerated >= ago(ioc_lookBack)
    | extend TI_ipEntity = NetworkSourceIP
    | where TI_ipEntity in (ActivityIPs)
    | extend Url = iff(ObservableKey == "url:value", ObservableValue, "")
    | summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id
    | where  IsActive == true and ValidUntil > now()
    | extend Description = tostring(parse_json(Data).description)
    | where Description !contains_cs "State: inactive;" and Description !contains_cs "State: falsepos;");
  IP_Indicators
     | project-reorder *, Tags, TrafficLightProtocolLevel, NetworkSourceIP, Type, TI_ipEntity
  // Use innerunique to keep performance fast and result set low, as we only need one match to indicate potential malicious activity that needs investigation
  | join kind=innerunique (OfficeActivity_)
    on $left.TI_ipEntity == $right.IPAddress
  // Filter out OfficeActivity events that occurred after the expiration of the corresponding indicator
  | where OfficeActivity_TimeGenerated < ValidUntil
  // Group the results by IndicatorId and keep the OfficeActivity event with the latest timestamp
  | summarize OfficeActivity_TimeGenerated = arg_max(OfficeActivity_TimeGenerated, *) by Id
  // Select the desired output fields
  | extend Description = tostring(parse_json(Data).description)
  | extend ActivityGroupNames = extract(@"ActivityGroup:(\S+)", 1, tostring(parse_json(Data).labels))
  | project OfficeActivity_TimeGenerated, Description, ActivityGroupNames, Id, ValidUntil, Confidence, TI_ipEntity, ClientIP, UserId, Operation, ResultStatus, RecordType, OfficeObjectId, NetworkSourceIP, Type, Url
  | extend timestamp = OfficeActivity_TimeGenerated, Name = tostring(split(UserId, '@', 0)[0]), UPNSuffix = tostring(split(UserId, '@', 1)[0])  
tactics:
- CommandAndControl
description: |
    This query maps any IP indicators of compromise (IOCs) from threat intelligence (TI), by searching for matches in OfficeActivity.
entityMappings:
- fieldMappings:
  - columnName: UserId
    identifier: FullName
  - columnName: Name
    identifier: Name
  - columnName: UPNSuffix
    identifier: UPNSuffix
  entityType: Account
- fieldMappings:
  - columnName: TI_ipEntity
    identifier: Address
  entityType: IP
- fieldMappings:
  - columnName: Url
    identifier: Url
  entityType: URL
queryFrequency: 1h
triggerOperator: gt
version: 1.4.5
queryPeriod: 14d
kind: Scheduled
severity: Medium
triggerThreshold: 0
id: f50280e5-5eb1-4e95-99fd-9d584a987bdd
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "workspace": {
      "type": "String"
    }
  },
  "resources": [
    {
      "apiVersion": "2024-01-01-preview",
      "id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/f50280e5-5eb1-4e95-99fd-9d584a987bdd')]",
      "kind": "Scheduled",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/f50280e5-5eb1-4e95-99fd-9d584a987bdd')]",
      "properties": {
        "alertRuleTemplateName": "f50280e5-5eb1-4e95-99fd-9d584a987bdd",
        "customDetails": null,
        "description": "This query maps any IP indicators of compromise (IOCs) from threat intelligence (TI), by searching for matches in OfficeActivity.\n",
        "displayName": "TI map IP entity to OfficeActivity",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "UserId",
                "identifier": "FullName"
              },
              {
                "columnName": "Name",
                "identifier": "Name"
              },
              {
                "columnName": "UPNSuffix",
                "identifier": "UPNSuffix"
              }
            ]
          },
          {
            "entityType": "IP",
            "fieldMappings": [
              {
                "columnName": "TI_ipEntity",
                "identifier": "Address"
              }
            ]
          },
          {
            "entityType": "URL",
            "fieldMappings": [
              {
                "columnName": "Url",
                "identifier": "Url"
              }
            ]
          }
        ],
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Threat Intelligence (NEW)/Analytic Rules/IPEntity_OfficeActivity.yaml",
        "query": "let dt_lookBack = 1h; // Look back 1 hour for OfficeActivity events\nlet ioc_lookBack = 14d; // Look back 14 days for threat intelligence indicators\nlet OfficeActivity_ = materialize(OfficeActivity\n  | where isnotempty(ClientIP)\n  | where TimeGenerated >= ago(dt_lookBack)\n  | extend ClientIPValues = extract_all(@'\\[?(::ffff:)?(?P<IPAddress>(\\d+\\.\\d+\\.\\d+\\.\\d+)|[^\\]%]+)(%\\d+)?\\]?([-:](?P<Port>\\d+))?', dynamic([\"IPAddress\", \"Port\"]), ClientIP)[0]\n  | extend IPAddress = iff(array_length(ClientIPValues) > 0, tostring(ClientIPValues[0]), '')\n  | project-rename OfficeActivity_TimeGenerated = TimeGenerated);\nlet ActivityIPs = OfficeActivity_ | summarize IPs = make_list(IPAddress);\n// Fetch threat intelligence indicators related to IP addresses\nlet IP_Indicators = materialize(ThreatIntelIndicators\n//extract key part of kv pair\n     | extend IndicatorType = replace(@\"\\[|\\]|\\\"\"\", \"\", tostring(split(ObservableKey, \":\", 0)))\n     | where isnotempty(IndicatorType) and IndicatorType == \"ipv4-addr\"\n     | extend NetworkSourceIP = toupper(ObservableValue)\n     | extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)\n  | where isnotempty(NetworkSourceIP)\n  | where TimeGenerated >= ago(ioc_lookBack)\n  | extend TI_ipEntity = NetworkSourceIP\n  | where TI_ipEntity in (ActivityIPs)\n  | extend Url = iff(ObservableKey == \"url:value\", ObservableValue, \"\")\n  | summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id\n  | where  IsActive == true and ValidUntil > now()\n  | extend Description = tostring(parse_json(Data).description)\n  | where Description !contains_cs \"State: inactive;\" and Description !contains_cs \"State: falsepos;\");\nIP_Indicators\n   | project-reorder *, Tags, TrafficLightProtocolLevel, NetworkSourceIP, Type, TI_ipEntity\n// Use innerunique to keep performance fast and result set low, as we only need one match to indicate potential malicious activity that needs investigation\n| join kind=innerunique (OfficeActivity_)\n  on $left.TI_ipEntity == $right.IPAddress\n// Filter out OfficeActivity events that occurred after the expiration of the corresponding indicator\n| where OfficeActivity_TimeGenerated < ValidUntil\n// Group the results by IndicatorId and keep the OfficeActivity event with the latest timestamp\n| summarize OfficeActivity_TimeGenerated = arg_max(OfficeActivity_TimeGenerated, *) by Id\n// Select the desired output fields\n| extend Description = tostring(parse_json(Data).description)\n| extend ActivityGroupNames = extract(@\"ActivityGroup:(\\S+)\", 1, tostring(parse_json(Data).labels))\n| project OfficeActivity_TimeGenerated, Description, ActivityGroupNames, Id, ValidUntil, Confidence, TI_ipEntity, ClientIP, UserId, Operation, ResultStatus, RecordType, OfficeObjectId, NetworkSourceIP, Type, Url\n| extend timestamp = OfficeActivity_TimeGenerated, Name = tostring(split(UserId, '@', 0)[0]), UPNSuffix = tostring(split(UserId, '@', 1)[0])\n",
        "queryFrequency": "PT1H",
        "queryPeriod": "P14D",
        "severity": "Medium",
        "subTechniques": [],
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "CommandAndControl"
        ],
        "techniques": [
          "T1071"
        ],
        "templateVersion": "1.4.5",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}