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

GSA Enriched Office 365 - Mail Redirect via ExO Transport Rule

Back
Idedcfc2e0-3134-434c-8074-9101c530d419
RulenameGSA Enriched Office 365 - Mail Redirect via ExO Transport Rule
DescriptionIdentifies when an Exchange Online transport rule is configured to forward emails.

This could indicate an adversary mailbox configured to collect mail from multiple user accounts.
SeverityMedium
TacticsCollection
Exfiltration
TechniquesT1114
T1020
Required data connectorsAzureActiveDirectory
Office365
KindScheduled
Query frequency1h
Query period1h
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Global Secure Access/Analytic Rules/Office 365 - Mail_redirect_via_ExO_transport_rule.yaml
Version2.1.5
Arm templateedcfc2e0-3134-434c-8074-9101c530d419.json
Deploy To Azure
// OfficeActivity Query
let officeActivityQuery = OfficeActivity
    | where OfficeWorkload == "Exchange"
    | where Operation in~ ("New-TransportRule", "Set-TransportRule")
    | mv-apply DynamicParameters = todynamic(Parameters) on (
        summarize ParsedParameters = make_bag(pack(tostring(DynamicParameters.Name), DynamicParameters.Value))
    )
    | extend RuleName = case(
        Operation =~ "Set-TransportRule", OfficeObjectId,
        Operation =~ "New-TransportRule", ParsedParameters.Name,
        "Unknown"
    )
    | mv-expand ExpandedParameters = todynamic(Parameters)
    | where ExpandedParameters.Name in~ ("BlindCopyTo", "RedirectMessageTo") and isnotempty(ExpandedParameters.Value)
    | extend RedirectTo = tostring(ExpandedParameters.Value)  // Cast to string
    | extend ClientIPValues = extract_all(@'\[?(::ffff:)?(?P<IPAddress>(\d+\.\d+\.\d+\.\d+)|[^\]]+)\]?([-:](?P<Port>\d+))?', dynamic(["IPAddress", "Port"]), ClientIP)[0]
    | extend From = ParsedParameters.From
    | project
        TimeGenerated,
        RedirectTo,
        IPAddress = tostring(ClientIPValues[0]),
        Port = tostring(ClientIPValues[1]),
        UserId,
        From,
        Operation,
        RuleName,
        Parameters
    | extend
        AccountName = tostring(split(UserId, "@")[0]),
        AccountUPNSuffix = tostring(split(UserId, "@")[1]);
    // EnrichedMicrosoft365AuditLogs Query
    let enrichedLogsQuery = EnrichedMicrosoft365AuditLogs
    | where Workload == "Exchange"
    | where Operation in~ ("New-TransportRule", "Set-TransportRule")
    | extend AdditionalProps = parse_json(AdditionalProperties)
    | mv-apply DynamicParameters = todynamic(AdditionalProps.Parameters) on (
        summarize ParsedParameters = make_bag(pack(tostring(DynamicParameters.Name), DynamicParameters.Value))
    )
    | extend RuleName = case(
        Operation =~ "Set-TransportRule", ObjectId,
        Operation =~ "New-TransportRule", ParsedParameters.Name,
        "Unknown"
    )
    | mv-expand ExpandedParameters = todynamic(AdditionalProps.Parameters)
    | where ExpandedParameters.Name in~ ("BlindCopyTo", "RedirectMessageTo") and isnotempty(ExpandedParameters.Value)
    | extend RedirectTo = tostring(ExpandedParameters.Value)  // Cast to string
    | extend ClientIPValues = extract_all(@'\[?(::ffff:)?(?P<IPAddress>(\d+\.\d+\.\d+\.\d+)|[^\]]+)\]?([-:](?P<Port>\d+))?', dynamic(["IPAddress", "Port"]), ClientIp)[0]
    | extend From = ParsedParameters.From
    | extend UserAgent = tostring(AdditionalProps.UserAgent)
    | project
        TimeGenerated,
        RedirectTo,
        IPAddress = tostring(ClientIPValues[0]),
        Port = tostring(ClientIPValues[1]),
        UserId,
        From,
        Operation,
        RuleName,
        Parameters = tostring(AdditionalProps.Parameters),
        UserAgent
    | extend
        AccountName = tostring(split(UserId, "@")[0]),
        AccountUPNSuffix = tostring(split(UserId, "@")[1]);
    // Combine both queries
    union isfuzzy=true officeActivityQuery, enrichedLogsQuery
    | summarize arg_min(TimeGenerated, *) by RuleName, RedirectTo
    | project
        TimeGenerated,
        RedirectTo,
        IPAddress,
        Port,
        UserId,
        From,
        Operation,
        RuleName,
        Parameters,
        AccountName,
        AccountUPNSuffix
        | order by TimeGenerated desc
severity: Medium
id: edcfc2e0-3134-434c-8074-9101c530d419
queryFrequency: 1h
queryPeriod: 1h
relevantTechniques:
- T1114
- T1020
triggerOperator: gt
status: Available
tactics:
- Collection
- Exfiltration
kind: Scheduled
description: |
  Identifies when an Exchange Online transport rule is configured to forward emails.
  This could indicate an adversary mailbox configured to collect mail from multiple user accounts.  
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Global Secure Access/Analytic Rules/Office 365 - Mail_redirect_via_ExO_transport_rule.yaml
requiredDataConnectors:
- dataTypes:
  - OfficeActivity (Exchange)
  connectorId: Office365
- dataTypes:
  - EnrichedMicrosoft365AuditLogs
  connectorId: AzureActiveDirectory
entityMappings:
- entityType: Account
  fieldMappings:
  - columnName: UserId
    identifier: FullName
  - columnName: AccountName
    identifier: Name
  - columnName: AccountUPNSuffix
    identifier: UPNSuffix
- entityType: IP
  fieldMappings:
  - columnName: IPAddress
    identifier: Address
query: |
  // OfficeActivity Query
  let officeActivityQuery = OfficeActivity
      | where OfficeWorkload == "Exchange"
      | where Operation in~ ("New-TransportRule", "Set-TransportRule")
      | mv-apply DynamicParameters = todynamic(Parameters) on (
          summarize ParsedParameters = make_bag(pack(tostring(DynamicParameters.Name), DynamicParameters.Value))
      )
      | extend RuleName = case(
          Operation =~ "Set-TransportRule", OfficeObjectId,
          Operation =~ "New-TransportRule", ParsedParameters.Name,
          "Unknown"
      )
      | mv-expand ExpandedParameters = todynamic(Parameters)
      | where ExpandedParameters.Name in~ ("BlindCopyTo", "RedirectMessageTo") and isnotempty(ExpandedParameters.Value)
      | extend RedirectTo = tostring(ExpandedParameters.Value)  // Cast to string
      | extend ClientIPValues = extract_all(@'\[?(::ffff:)?(?P<IPAddress>(\d+\.\d+\.\d+\.\d+)|[^\]]+)\]?([-:](?P<Port>\d+))?', dynamic(["IPAddress", "Port"]), ClientIP)[0]
      | extend From = ParsedParameters.From
      | project
          TimeGenerated,
          RedirectTo,
          IPAddress = tostring(ClientIPValues[0]),
          Port = tostring(ClientIPValues[1]),
          UserId,
          From,
          Operation,
          RuleName,
          Parameters
      | extend
          AccountName = tostring(split(UserId, "@")[0]),
          AccountUPNSuffix = tostring(split(UserId, "@")[1]);
      // EnrichedMicrosoft365AuditLogs Query
      let enrichedLogsQuery = EnrichedMicrosoft365AuditLogs
      | where Workload == "Exchange"
      | where Operation in~ ("New-TransportRule", "Set-TransportRule")
      | extend AdditionalProps = parse_json(AdditionalProperties)
      | mv-apply DynamicParameters = todynamic(AdditionalProps.Parameters) on (
          summarize ParsedParameters = make_bag(pack(tostring(DynamicParameters.Name), DynamicParameters.Value))
      )
      | extend RuleName = case(
          Operation =~ "Set-TransportRule", ObjectId,
          Operation =~ "New-TransportRule", ParsedParameters.Name,
          "Unknown"
      )
      | mv-expand ExpandedParameters = todynamic(AdditionalProps.Parameters)
      | where ExpandedParameters.Name in~ ("BlindCopyTo", "RedirectMessageTo") and isnotempty(ExpandedParameters.Value)
      | extend RedirectTo = tostring(ExpandedParameters.Value)  // Cast to string
      | extend ClientIPValues = extract_all(@'\[?(::ffff:)?(?P<IPAddress>(\d+\.\d+\.\d+\.\d+)|[^\]]+)\]?([-:](?P<Port>\d+))?', dynamic(["IPAddress", "Port"]), ClientIp)[0]
      | extend From = ParsedParameters.From
      | extend UserAgent = tostring(AdditionalProps.UserAgent)
      | project
          TimeGenerated,
          RedirectTo,
          IPAddress = tostring(ClientIPValues[0]),
          Port = tostring(ClientIPValues[1]),
          UserId,
          From,
          Operation,
          RuleName,
          Parameters = tostring(AdditionalProps.Parameters),
          UserAgent
      | extend
          AccountName = tostring(split(UserId, "@")[0]),
          AccountUPNSuffix = tostring(split(UserId, "@")[1]);
      // Combine both queries
      union isfuzzy=true officeActivityQuery, enrichedLogsQuery
      | summarize arg_min(TimeGenerated, *) by RuleName, RedirectTo
      | project
          TimeGenerated,
          RedirectTo,
          IPAddress,
          Port,
          UserId,
          From,
          Operation,
          RuleName,
          Parameters,
          AccountName,
          AccountUPNSuffix
          | order by TimeGenerated desc  
version: 2.1.5
name: GSA Enriched Office 365 - Mail Redirect via ExO Transport Rule
triggerThreshold: 0
{
  "$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/edcfc2e0-3134-434c-8074-9101c530d419')]",
      "kind": "Scheduled",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/edcfc2e0-3134-434c-8074-9101c530d419')]",
      "properties": {
        "alertRuleTemplateName": "edcfc2e0-3134-434c-8074-9101c530d419",
        "customDetails": null,
        "description": "Identifies when an Exchange Online transport rule is configured to forward emails.\nThis could indicate an adversary mailbox configured to collect mail from multiple user accounts.\n",
        "displayName": "GSA Enriched Office 365 - Mail Redirect via ExO Transport Rule",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "UserId",
                "identifier": "FullName"
              },
              {
                "columnName": "AccountName",
                "identifier": "Name"
              },
              {
                "columnName": "AccountUPNSuffix",
                "identifier": "UPNSuffix"
              }
            ]
          },
          {
            "entityType": "IP",
            "fieldMappings": [
              {
                "columnName": "IPAddress",
                "identifier": "Address"
              }
            ]
          }
        ],
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Global Secure Access/Analytic Rules/Office 365 - Mail_redirect_via_ExO_transport_rule.yaml",
        "query": "// OfficeActivity Query\nlet officeActivityQuery = OfficeActivity\n    | where OfficeWorkload == \"Exchange\"\n    | where Operation in~ (\"New-TransportRule\", \"Set-TransportRule\")\n    | mv-apply DynamicParameters = todynamic(Parameters) on (\n        summarize ParsedParameters = make_bag(pack(tostring(DynamicParameters.Name), DynamicParameters.Value))\n    )\n    | extend RuleName = case(\n        Operation =~ \"Set-TransportRule\", OfficeObjectId,\n        Operation =~ \"New-TransportRule\", ParsedParameters.Name,\n        \"Unknown\"\n    )\n    | mv-expand ExpandedParameters = todynamic(Parameters)\n    | where ExpandedParameters.Name in~ (\"BlindCopyTo\", \"RedirectMessageTo\") and isnotempty(ExpandedParameters.Value)\n    | extend RedirectTo = tostring(ExpandedParameters.Value)  // Cast to string\n    | extend ClientIPValues = extract_all(@'\\[?(::ffff:)?(?P<IPAddress>(\\d+\\.\\d+\\.\\d+\\.\\d+)|[^\\]]+)\\]?([-:](?P<Port>\\d+))?', dynamic([\"IPAddress\", \"Port\"]), ClientIP)[0]\n    | extend From = ParsedParameters.From\n    | project\n        TimeGenerated,\n        RedirectTo,\n        IPAddress = tostring(ClientIPValues[0]),\n        Port = tostring(ClientIPValues[1]),\n        UserId,\n        From,\n        Operation,\n        RuleName,\n        Parameters\n    | extend\n        AccountName = tostring(split(UserId, \"@\")[0]),\n        AccountUPNSuffix = tostring(split(UserId, \"@\")[1]);\n    // EnrichedMicrosoft365AuditLogs Query\n    let enrichedLogsQuery = EnrichedMicrosoft365AuditLogs\n    | where Workload == \"Exchange\"\n    | where Operation in~ (\"New-TransportRule\", \"Set-TransportRule\")\n    | extend AdditionalProps = parse_json(AdditionalProperties)\n    | mv-apply DynamicParameters = todynamic(AdditionalProps.Parameters) on (\n        summarize ParsedParameters = make_bag(pack(tostring(DynamicParameters.Name), DynamicParameters.Value))\n    )\n    | extend RuleName = case(\n        Operation =~ \"Set-TransportRule\", ObjectId,\n        Operation =~ \"New-TransportRule\", ParsedParameters.Name,\n        \"Unknown\"\n    )\n    | mv-expand ExpandedParameters = todynamic(AdditionalProps.Parameters)\n    | where ExpandedParameters.Name in~ (\"BlindCopyTo\", \"RedirectMessageTo\") and isnotempty(ExpandedParameters.Value)\n    | extend RedirectTo = tostring(ExpandedParameters.Value)  // Cast to string\n    | extend ClientIPValues = extract_all(@'\\[?(::ffff:)?(?P<IPAddress>(\\d+\\.\\d+\\.\\d+\\.\\d+)|[^\\]]+)\\]?([-:](?P<Port>\\d+))?', dynamic([\"IPAddress\", \"Port\"]), ClientIp)[0]\n    | extend From = ParsedParameters.From\n    | extend UserAgent = tostring(AdditionalProps.UserAgent)\n    | project\n        TimeGenerated,\n        RedirectTo,\n        IPAddress = tostring(ClientIPValues[0]),\n        Port = tostring(ClientIPValues[1]),\n        UserId,\n        From,\n        Operation,\n        RuleName,\n        Parameters = tostring(AdditionalProps.Parameters),\n        UserAgent\n    | extend\n        AccountName = tostring(split(UserId, \"@\")[0]),\n        AccountUPNSuffix = tostring(split(UserId, \"@\")[1]);\n    // Combine both queries\n    union isfuzzy=true officeActivityQuery, enrichedLogsQuery\n    | summarize arg_min(TimeGenerated, *) by RuleName, RedirectTo\n    | project\n        TimeGenerated,\n        RedirectTo,\n        IPAddress,\n        Port,\n        UserId,\n        From,\n        Operation,\n        RuleName,\n        Parameters,\n        AccountName,\n        AccountUPNSuffix\n        | order by TimeGenerated desc\n",
        "queryFrequency": "PT1H",
        "queryPeriod": "PT1H",
        "severity": "Medium",
        "status": "Available",
        "subTechniques": [],
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "Collection",
          "Exfiltration"
        ],
        "techniques": [
          "T1020",
          "T1114"
        ],
        "templateVersion": "2.1.5",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}