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

NRT Malicious Inbox Rule

Back
Idb79f6190-d104-4691-b7db-823e05980895
RulenameNRT Malicious Inbox Rule
DescriptionOften times after the initial compromise the attackers create inbox rules to delete emails that contain certain keywords.

This is done so as to limit ability to warn compromised users that they’ve been compromised. Below is a sample query that tries to detect this.

Reference: https://www.reddit.com/r/sysadmin/comments/7kyp0a/recent_phishing_attempts_my_experience_and_what/
SeverityMedium
TacticsPersistence
DefenseEvasion
TechniquesT1098
T1078
Required data connectorsOffice365
KindNRT
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Detections/OfficeActivity/NRT_Malicious_Inbox_Rule.yaml
Version1.0.2
Arm templateb79f6190-d104-4691-b7db-823e05980895.json
Deploy To Azure
let Keywords = dynamic(["helpdesk", " alert", " suspicious", "fake", "malicious", "phishing", "spam", "do not click", "do not open", "hijacked", "Fatal"]);
OfficeActivity
| where OfficeWorkload =~ "Exchange"
| where Parameters has "Deleted Items" or Parameters has "Junk Email"  or Parameters has "DeleteMessage"
| extend Events=todynamic(Parameters)
| parse Events  with * "SubjectContainsWords" SubjectContainsWords '}'*
| parse Events  with * "BodyContainsWords" BodyContainsWords '}'*
| parse Events  with * "SubjectOrBodyContainsWords" SubjectOrBodyContainsWords '}'*
| where SubjectContainsWords has_any (Keywords)
 or BodyContainsWords has_any (Keywords)
 or SubjectOrBodyContainsWords has_any (Keywords)
| extend ClientIPAddress = case( ClientIP has ".", tostring(split(ClientIP,":")[0]), ClientIP has "[", tostring(trim_start(@'[[]',tostring(split(ClientIP,"]")[0]))), ClientIP )
| extend Keyword = iff(isnotempty(SubjectContainsWords), SubjectContainsWords, (iff(isnotempty(BodyContainsWords),BodyContainsWords,SubjectOrBodyContainsWords )))
| extend RuleDetail = case(OfficeObjectId contains '/' , tostring(split(OfficeObjectId, '/')[-1]) , tostring(split(OfficeObjectId, '\\')[-1]))
| summarize count(), StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by  UserId, ClientIPAddress, ResultStatus, Keyword, OriginatingServer, OfficeObjectId, RuleDetail
version: 1.0.2
severity: Medium
metadata:
  categories:
    domains:
    - Security - Threat Protection
  author:
    name: Pete Bryan
  support:
    tier: Community
  source:
    kind: Community
relevantTechniques:
- T1098
- T1078
name: NRT Malicious Inbox Rule
entityMappings:
- fieldMappings:
  - columnName: UserId
    identifier: FullName
  entityType: Account
- fieldMappings:
  - columnName: OriginatingServer
    identifier: FullName
  entityType: Host
- fieldMappings:
  - columnName: ClientIPAddress
    identifier: Address
  entityType: IP
kind: NRT
tactics:
- Persistence
- DefenseEvasion
description: |
  'Often times after the initial compromise the attackers create inbox rules to delete emails that contain certain keywords.
   This is done so as to limit ability to warn compromised users that they've been compromised. Below is a sample query that tries to detect this.
  Reference: https://www.reddit.com/r/sysadmin/comments/7kyp0a/recent_phishing_attempts_my_experience_and_what/'  
requiredDataConnectors:
- dataTypes:
  - OfficeActivity
  connectorId: Office365
id: b79f6190-d104-4691-b7db-823e05980895
query: |
  let Keywords = dynamic(["helpdesk", " alert", " suspicious", "fake", "malicious", "phishing", "spam", "do not click", "do not open", "hijacked", "Fatal"]);
  OfficeActivity
  | where OfficeWorkload =~ "Exchange"
  | where Parameters has "Deleted Items" or Parameters has "Junk Email"  or Parameters has "DeleteMessage"
  | extend Events=todynamic(Parameters)
  | parse Events  with * "SubjectContainsWords" SubjectContainsWords '}'*
  | parse Events  with * "BodyContainsWords" BodyContainsWords '}'*
  | parse Events  with * "SubjectOrBodyContainsWords" SubjectOrBodyContainsWords '}'*
  | where SubjectContainsWords has_any (Keywords)
   or BodyContainsWords has_any (Keywords)
   or SubjectOrBodyContainsWords has_any (Keywords)
  | extend ClientIPAddress = case( ClientIP has ".", tostring(split(ClientIP,":")[0]), ClientIP has "[", tostring(trim_start(@'[[]',tostring(split(ClientIP,"]")[0]))), ClientIP )
  | extend Keyword = iff(isnotempty(SubjectContainsWords), SubjectContainsWords, (iff(isnotempty(BodyContainsWords),BodyContainsWords,SubjectOrBodyContainsWords )))
  | extend RuleDetail = case(OfficeObjectId contains '/' , tostring(split(OfficeObjectId, '/')[-1]) , tostring(split(OfficeObjectId, '\\')[-1]))
  | summarize count(), StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by  UserId, ClientIPAddress, ResultStatus, Keyword, OriginatingServer, OfficeObjectId, RuleDetail  
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/OfficeActivity/NRT_Malicious_Inbox_Rule.yaml
{
  "$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/b79f6190-d104-4691-b7db-823e05980895')]",
      "kind": "NRT",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/b79f6190-d104-4691-b7db-823e05980895')]",
      "properties": {
        "alertRuleTemplateName": "b79f6190-d104-4691-b7db-823e05980895",
        "customDetails": null,
        "description": "'Often times after the initial compromise the attackers create inbox rules to delete emails that contain certain keywords.\n This is done so as to limit ability to warn compromised users that they've been compromised. Below is a sample query that tries to detect this.\nReference: https://www.reddit.com/r/sysadmin/comments/7kyp0a/recent_phishing_attempts_my_experience_and_what/'\n",
        "displayName": "NRT Malicious Inbox Rule",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "UserId",
                "identifier": "FullName"
              }
            ]
          },
          {
            "entityType": "Host",
            "fieldMappings": [
              {
                "columnName": "OriginatingServer",
                "identifier": "FullName"
              }
            ]
          },
          {
            "entityType": "IP",
            "fieldMappings": [
              {
                "columnName": "ClientIPAddress",
                "identifier": "Address"
              }
            ]
          }
        ],
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Detections/OfficeActivity/NRT_Malicious_Inbox_Rule.yaml",
        "query": "let Keywords = dynamic([\"helpdesk\", \" alert\", \" suspicious\", \"fake\", \"malicious\", \"phishing\", \"spam\", \"do not click\", \"do not open\", \"hijacked\", \"Fatal\"]);\nOfficeActivity\n| where OfficeWorkload =~ \"Exchange\"\n| where Parameters has \"Deleted Items\" or Parameters has \"Junk Email\"  or Parameters has \"DeleteMessage\"\n| extend Events=todynamic(Parameters)\n| parse Events  with * \"SubjectContainsWords\" SubjectContainsWords '}'*\n| parse Events  with * \"BodyContainsWords\" BodyContainsWords '}'*\n| parse Events  with * \"SubjectOrBodyContainsWords\" SubjectOrBodyContainsWords '}'*\n| where SubjectContainsWords has_any (Keywords)\n or BodyContainsWords has_any (Keywords)\n or SubjectOrBodyContainsWords has_any (Keywords)\n| extend ClientIPAddress = case( ClientIP has \".\", tostring(split(ClientIP,\":\")[0]), ClientIP has \"[\", tostring(trim_start(@'[[]',tostring(split(ClientIP,\"]\")[0]))), ClientIP )\n| extend Keyword = iff(isnotempty(SubjectContainsWords), SubjectContainsWords, (iff(isnotempty(BodyContainsWords),BodyContainsWords,SubjectOrBodyContainsWords )))\n| extend RuleDetail = case(OfficeObjectId contains '/' , tostring(split(OfficeObjectId, '/')[-1]) , tostring(split(OfficeObjectId, '\\\\')[-1]))\n| summarize count(), StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by  UserId, ClientIPAddress, ResultStatus, Keyword, OriginatingServer, OfficeObjectId, RuleDetail\n",
        "severity": "Medium",
        "subTechniques": [],
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "DefenseEvasion",
          "Persistence"
        ],
        "techniques": [
          "T1078",
          "T1098"
        ],
        "templateVersion": "1.0.2"
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}