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.4
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 = ExpandedParameters.Value
  | 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 = ExpandedParameters.Value
  | 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;
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 = ExpandedParameters.Value
    | 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 = ExpandedParameters.Value
    | 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
severity: Medium
triggerThreshold: 0
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.  
status: Available
triggerOperator: gt
tactics:
- Collection
- Exfiltration
entityMappings:
- fieldMappings:
  - columnName: UserId
    identifier: FullName
  - columnName: AccountName
    identifier: Name
  - columnName: AccountUPNSuffix
    identifier: UPNSuffix
  entityType: Account
- fieldMappings:
  - columnName: IPAddress
    identifier: Address
  entityType: IP
requiredDataConnectors:
- connectorId: Office365
  dataTypes:
  - OfficeActivity (Exchange)
- connectorId: AzureActiveDirectory
  dataTypes:
  - EnrichedMicrosoft365AuditLogs
id: edcfc2e0-3134-434c-8074-9101c530d419
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
queryFrequency: 1h
version: 2.1.4
kind: Scheduled
{
  "$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 = ExpandedParameters.Value\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 TimeGenerated, RedirectTo, IPAddress = tostring(ClientIPValues[0]), Port = tostring(ClientIPValues[1]), UserId, From, Operation, RuleName, Parameters\n  | extend AccountName = tostring(split(UserId, \"@\")[0]),\n           AccountUPNSuffix = tostring(split(UserId, \"@\")[1]);\n// EnrichedMicrosoft365AuditLogs Query\nlet 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 = ExpandedParameters.Value\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 TimeGenerated, RedirectTo, IPAddress = tostring(ClientIPValues[0]), Port = tostring(ClientIPValues[1]), UserId, From, Operation, RuleName, Parameters = tostring(AdditionalProps.Parameters), UserAgent\n  | extend AccountName = tostring(split(UserId, \"@\")[0]),\n           AccountUPNSuffix = tostring(split(UserId, \"@\")[1]);\n// Combine both queries\nunion isfuzzy=true officeActivityQuery, enrichedLogsQuery\n| summarize arg_min(TimeGenerated, *) by RuleName, RedirectTo\n| project TimeGenerated, RedirectTo, IPAddress, Port, UserId, From, Operation, RuleName, Parameters, AccountName, 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.4",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}