Detection rules › Panther

Panther rules: microsoft365

Microsoft365 Brute Force Login by User

#
Severity
medium
Log types
Microsoft365.Audit.AzureActiveDirectory
Reference
learn.microsoft.com
Source
github.com/panther-labs/panther-analysis

A Microsoft365 user was denied login access several times

MITRE ATT&CK coverage

TacticTechniques
Credential Access

Telemetry coverage

PlatformRecord / event type
Microsoft 365UserLoginFailed

Rules detecting the same action

These rules filter on the same operation.

Detection logic

from panther_msft_helpers import m365_alert_context


def rule(event):
    return event.get("Operation", "") == "UserLoginFailed"


def title(event):
    return (
        f"Microsoft365: [{event.get('UserId', '<user-not-found>')}] "
        "may be undergoing a Brute Force Attack."
    )


def alert_context(event):
    return m365_alert_context(event)

Rule specification

AnalysisType: rule
Description: A Microsoft365 user was denied login access several times
DisplayName: "Microsoft365 Brute Force Login by User"
Enabled: true
Filename: microsoft365_brute_force_login_by_user.py
Reports:
  MITRE ATT&CK:
    - TA0006:T1110 # Credential Access - Brute Force
Runbook: Analyze the IP they came from and actions taken before/after.
Reference: https://learn.microsoft.com/en-us/microsoft-365/troubleshoot/authentication/access-denied-when-connect-to-office-365
Severity: Medium
DedupPeriodMinutes: 60
LogTypes:
  - Microsoft365.Audit.AzureActiveDirectory
RuleID: "Microsoft365.Brute.Force.Login.by.User"
Threshold: 10

Stages and Predicates

Fires on Microsoft365.Audit.AzureActiveDirectory events when the condition below holds.

Condition

  • Operation is UserLoginFailed
Alert cadence
alerts after 10 matches within 1h

Indicators

These rows show field, operator, and value matches.

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

FieldSource
operationOperation
organization_idOrganizationId
client_ipClientIp
extended_propertiesExtendedProperties
modified_propertiesModifiedProperties
applicationApplication
actorActor
UserId

Response runbook

Analyze the IP they came from and actions taken before/after.

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "Actor": [
    {
      "ID": "012345-abcde-543-xyz",
      "Type": 0
    },
    {
      "ID": "sample.user@yourorg.onmicrosoft.com",
      "Type": 5
    }
  ],
  "ActorContextId": "123-abc-xyz-567",
  "ActorIpAddress": "1.2.3.4",
  "ApplicationId": "123-abc-sfa-321",
  "AzureActiveDirectoryEventType": 1,
  "ClientIP": "1.2.3.4",
  "CreationTime": "2022-12-12 15:57:57",
  "ExtendedProperties": [
    {
      "Name": "ResultStatusDetail",
      "Value": "Success"
    },
    {
      "Name": "UserAgent",
      "Value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"
    },
    {
      "Name": "UserAuthenticationMethod",
      "Value": "1"
    },
    {
      "Name": "RequestType",
      "Value": "Login:login"
    }
  ],
  "Id": "abc-def-123",
  "InterSystemsId": "987-432-123",
  "IntraSystemId": "aaa-bbb-ccc",
  "LogonError": "InvalidUserNameOrPassword",
  "ObjectId": "aa-11-22-bb",
  "Operation": "UserLoginFailed",
  "OrganizationId": "11-aa-22-bb",
  "RecordType": 15,
  "ResultStatus": "Success",
  "SupportTicketId": "",
  "Target": [
    {
      "ID": "11-22-33",
      "Type": 0
    }
  ],
  "TargetContextId": "11-22-33-44",
  "UserId": "sample.user@yourorg.onmicrosoft.com",
  "UserKey": "012345-abcde-543-xyz",
  "UserType": 0,
  "Workload": "AzureActiveDirectory"
}

Microsoft365 External Document Sharing

#
Severity
low
Log types
Microsoft365.Audit.SharePoint
Reference
support.microsoft.com
Source
github.com/panther-labs/panther-analysis

Document shared externally

MITRE ATT&CK coverage

TacticTechniques
Collection

Telemetry coverage

PlatformRecord / event type
Microsoft 365AnonymousLinkCreated

Rules detecting the same action

These rules filter on the same operation.

Detection logic

import re
from fnmatch import fnmatch

from panther_base_helpers import EMAIL_REGEX
from panther_msft_helpers import m365_alert_context

ALLOWED_DOMAINS = ["mycompany.com", "alloweddomain.com"]  # should be in lowercase

ALLOWED_USERS = ["exception@outsider.com"]  # should be in lowercase

ALLOWED_PATHS = ["*/External/*", "External/*"]


def allowed_path(relative_url):
    for path in ALLOWED_PATHS:
        if fnmatch(relative_url, path):
            return True
    return False


def rule(event):
    if event.get("Operation", "") == "AnonymousLinkCreated":
        return not allowed_path(event.get("SourceRelativeUrl"))
    if event.get("Operation", "") == "AddedToSecureLink":
        if allowed_path(event.get("SourceRelativeUrl")):
            return False
        target = event.get("TargetUserOrGroupName", "")
        if target.lower() in ALLOWED_USERS:
            return False
        if re.fullmatch(EMAIL_REGEX, target):
            if target.split("@")[1].lower() not in ALLOWED_DOMAINS:
                return True
    return False


def title(event):
    return (
        f"Microsoft365: [{event.get('SourceRelativeUrl')}] "
        "has been shared with external users by "
        f"[{event.get('UserId', '<user-not-found>')}]"
    )


def alert_context(event):
    return m365_alert_context(event)

Rule specification

AnalysisType: rule
Description: Document shared externally
DisplayName: "Microsoft365 External Document Sharing"
Enabled: true
Filename: microsoft365_external_sharing.py
Reports:
  MITRE ATT&CK:
    - TA0009:T1039 # Collection - Data from Network Shared Drive
Runbook: Check the document metadata to ensure it is not a sensitive document.
Reference: https://support.microsoft.com/en-us/topic/manage-sharing-with-external-users-in-microsoft-365-small-business-2951a85f-c970-4375-aa4f-6b0d7035fe35#:~:text=Top%20of%20Page-,Turn%20external%20sharing%20on%20or%20off,-The%20ability%20to
Severity: Low
DedupPeriodMinutes: 60
LogTypes:
  - Microsoft365.Audit.SharePoint
RuleID: "Microsoft365.External.Document.Sharing"
Threshold: 1

Stages and Predicates

Fires on Microsoft365.Audit.SharePoint events when any of the conditions below holds.

Condition

  • any of:
    • all of:
      • Operation is AnonymousLinkCreated
      • SourceRelativeUrl does not match the pattern */External/*
      • SourceRelativeUrl does not match the pattern External/*
    • all of:
      • Operation is not AnonymousLinkCreated
      • Operation is AddedToSecureLink
      • SourceRelativeUrl does not match the pattern */External/*
      • SourceRelativeUrl does not match the pattern External/*
      • TargetUserOrGroupName is not one of exception@outsider.com (case-insensitive)

This rule also runs imperative logic the parser cannot express as a filter. The conditions above are the structured part it could extract.

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Operationeq
  • AddedToSecureLink
  • AnonymousLinkCreated
field:"Operation" kind:eq
Operationne
  • AnonymousLinkCreated
field:"Operation" kind:ne value:"AnonymousLinkCreated"

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

FieldSource
operationOperation
organization_idOrganizationId
client_ipClientIp
extended_propertiesExtendedProperties
modified_propertiesModifiedProperties
applicationApplication
actorActor
SourceRelativeUrl
UserId

Response runbook

Check the document metadata to ensure it is not a sensitive document.

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "AppAccessContext": {
    "AADSessionId": "aa-bb-cc",
    "CorrelationId": "dd-ee-ff"
  },
  "ClientIP": "1.2.3.4",
  "CreationTime": "2022-12-12 19:31:41",
  "EventData": "<Type>Edit</Type><MembersCanShareApplied>False</MembersCanShareApplied>",
  "EventSource": "SharePoint",
  "Id": "111-aa-234",
  "ItemType": "File",
  "ObjectId": "https://yourorg.sharepoint.com/personal/user_yourorg/Documents/importantsecrets.docx",
  "Operation": "AddedToSecureLink",
  "OrganizationId": "11-22-abc",
  "RecordType": 14,
  "Site": "aa-bb-dd-ee-ff",
  "SiteUrl": "https://yourorg.sharepoint.com/personal/user_yourorg",
  "SourceFileExtension": "docx",
  "SourceFileName": "importantsecrets.docx",
  "SourceRelativeUrl": "Documents/importantsecrets.docx",
  "TargetUserOrGroupName": "OUTSIDER@EXTERNAL.IO",
  "TargetUserOrGroupType": "Guest",
  "UserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
  "UserId": "my.user@yourorg.onmicrosoft.com",
  "UserKey": "i:0h.f|membership|12345@live.com",
  "UserType": 0,
  "Workload": "OneDrive"
}

Microsoft365 MFA Disabled

#
Severity
low
Log types
Microsoft365.Audit.AzureActiveDirectory
Reference
learn.microsoft.com
Source
github.com/panther-labs/panther-analysis

A user's MFA has been removed

MITRE ATT&CK coverage

Telemetry coverage

PlatformRecord / event type
Microsoft 365Update user.

Detection logic

import json

from panther_msft_helpers import m365_alert_context


def rule(event):
    if event.get("Operation", "") == "Update user.":
        modified_properties = event.get("ModifiedProperties", [])
        for prop in modified_properties:
            if prop.get("Name", "") == "StrongAuthenticationMethod":
                new_value = prop.get("NewValue")
                old_value = prop.get("OldValue")
                if isinstance(new_value, str):
                    new_value = json.loads(new_value)
                if isinstance(old_value, str):
                    old_value = json.loads(old_value)

                if old_value and not new_value:
                    return True
                break
    return False


def title(event):
    return "Microsoft365: MFA Removed on " f"[{event.get('ObjectId', '')}]"


def alert_context(event):
    return m365_alert_context(event)

Rule specification

AnalysisType: rule
Description: A user's MFA has been removed
DisplayName: "Microsoft365 MFA Disabled"
Enabled: true
Filename: microsoft365_mfa_disabled.py
Reports:
  MITRE ATT&CK:
    - TA003:T1556 # Persistence - Modify Authentication Process
    - TA005:T1556 # Defense Evansion - Modify Authentication Process
    - TA006:T1556 # Credential Access - Modify Authentication Process
Runbook: Depending on company policy, either suggest or require the user re-enable two step verification.
Reference: https://learn.microsoft.com/en-us/microsoft-365/admin/security-and-compliance/set-up-multi-factor-authentication?view=o365-worldwide
Severity: Low
DedupPeriodMinutes: 60
LogTypes:
  - Microsoft365.Audit.AzureActiveDirectory
RuleID: "Microsoft365.MFA.Disabled"
Threshold: 1

Stages and Predicates

Fires on Microsoft365.Audit.AzureActiveDirectory events when the condition below holds.

Condition

  • Operation is Update user.

This rule also runs imperative logic the parser cannot express as a filter. The conditions above are the structured part it could extract.

Indicators

These rows show field, operator, and value matches.

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

FieldSource
operationOperation
organization_idOrganizationId
client_ipClientIp
extended_propertiesExtendedProperties
modified_propertiesModifiedProperties
applicationApplication
actorActor
ObjectId

Response runbook

Depending on company policy, either suggest or require the user re-enable two step verification.

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "Actor": [
    {
      "ID": "Azure MFA StrongAuthenticationService",
      "Type": 1
    },
    {
      "ID": "ABC-123",
      "Type": 2
    },
    {
      "ID": "ServicePrincipal_123-abc",
      "Type": 2
    },
    {
      "ID": "321-cba",
      "Type": 2
    },
    {
      "ID": "ServicePrincipal",
      "Type": 2
    }
  ],
  "ActorContextId": "123-abc-456",
  "AzureActiveDirectoryEventType": 1,
  "CreationTime": "2022-12-12 17:28:35",
  "ExtendedProperties": [
    {
      "Name": "additionalDetails",
      "Value": "{\"UserType\":\"Member\"}"
    },
    {
      "Name": "extendedAuditEventCategory",
      "Value": "User"
    }
  ],
  "Id": "123-abc-123",
  "InterSystemsId": "abc-123-321",
  "IntraSystemId": "aa-bbb-333",
  "ModifiedProperties": [
    {
      "Name": "StrongAuthenticationMethod",
      "NewValue": "[]",
      "OldValue": "[{\"Default\": true,\"MethodType\": 7}]"
    },
    {
      "Name": "Included Updated Properties",
      "NewValue": "StrongAuthenticationMethod",
      "OldValue": ""
    },
    {
      "Name": "TargetId.UserType",
      "NewValue": "Member",
      "OldValue": ""
    }
  ],
  "ObjectId": "sample.user@yourorg.onmicrosoft.com",
  "Operation": "Update user.",
  "OrganizationId": "111-222-333",
  "RecordType": 8,
  "ResultStatus": "Success",
  "SupportTicketId": "",
  "Target": [
    {
      "ID": "User_111-222-bbb",
      "Type": 2
    },
    {
      "ID": "111-aa-bbb-321",
      "Type": 2
    },
    {
      "ID": "User",
      "Type": 2
    },
    {
      "ID": "sample.user@yourorg.onmicrosoft.com",
      "Type": 5
    },
    {
      "ID": "123abcdef",
      "Type": 3
    }
  ],
  "TargetContextId": "aaa-bb-222",
  "UserId": "ServicePrincipal_aa-bb-ccc",
  "UserKey": "Not Available",
  "UserType": 4,
  "Workload": "AzureActiveDirectory"
}