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
relevantTechniques:
- T1114
- T1020
name: GSA Enriched Office 365 - Mail Redirect via ExO Transport Rule
requiredDataConnectors:
- dataTypes:
  - OfficeActivity (Exchange)
  connectorId: Office365
- dataTypes:
  - EnrichedMicrosoft365AuditLogs
  connectorId: AzureActiveDirectory
entityMappings:
- fieldMappings:
  - identifier: FullName
    columnName: UserId
  - identifier: Name
    columnName: AccountName
  - identifier: UPNSuffix
    columnName: AccountUPNSuffix
  entityType: Account
- fieldMappings:
  - identifier: Address
    columnName: IPAddress
  entityType: IP
triggerThreshold: 0
id: edcfc2e0-3134-434c-8074-9101c530d419
tactics:
- Collection
- Exfiltration
version: 2.1.5
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Global Secure Access/Analytic Rules/Office 365 - Mail_redirect_via_ExO_transport_rule.yaml
queryPeriod: 1h
kind: Scheduled
queryFrequency: 1h
severity: Medium
status: Available
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.  
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  
triggerOperator: gt
{
  "$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"
    }
  ]
}