From a97eb8a98feba1e5a20e88fa9291e547df558b67 Mon Sep 17 00:00:00 2001 From: Ju Cho Date: Thu, 9 Jan 2025 11:58:39 -0500 Subject: [PATCH 01/23] feat: new example rules --- rules/my_local_rules/example_rule.py | 70 +++++++++++++++++++++++++++ rules/my_local_rules/example_rule.yml | 34 +++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 rules/my_local_rules/example_rule.py create mode 100644 rules/my_local_rules/example_rule.yml diff --git a/rules/my_local_rules/example_rule.py b/rules/my_local_rules/example_rule.py new file mode 100644 index 000000000..efc28cfcd --- /dev/null +++ b/rules/my_local_rules/example_rule.py @@ -0,0 +1,70 @@ +from panther_base_helpers import pattern_match + + +## Required +# +# The logic to determine if an alert should send. +# return True = Alert, False = Do not Alert +def rule(event): + return event.get("field") == "value" and event.deep_get("field", "nestedValue") + + +## Optional Functions +# +# Set custom alert titles, must return a string. +# If not defined, defaults to the rule display name or rule ID. +def title(event): + if pattern_match(event.get("field"), "string*"): + return f"This is my alert title {event.get('field')}" + return f"This is my fallback title {event.get('field')}" + + +# Set custom deduplication strings, must return a string. +# If not defined, defaults to the alert title. +def dedup(event): + return event.get("identity") + + +# Additional information append to an alert, must return a dictionary +def alert_context(event): + return { + "someField": event.get("someField"), + "someRandomValue": 4, # chosen by a dice roll, guaranteed to be random + } + + +## Override Functions +# +# Override the severity of an alert based on the contents of the events, +# must return one of the following strings "INFO", "LOW", "MEDIUM', "HIGH", "CRITICAL" +def severity(event): + if event.get("field") == "value": + return "INFO" + return "HIGH" + + +# Override the description of the alert, must return a string +def description(event): + return f"Some Alert Description {event.get('Something')}" + + +# Override the reference in the alert, must return a string +def reference(event): + return f"https://some.com/reference/{event.get('Something')}" + + +# Override the runbook in the alert, must return a string +def runbook(event): + return f"If this happens, do {event.get('Something')}" + + +# Override the destination(s) the alert is sent to, must return a list of strings corresponding to +# panther destinations +BAD_THINGS = [] + + +def destinations(event): + if event.get("Something") in BAD_THINGS: + return ["01234567-1edf-4edb-8f5b-0123456789a"] + # Suppress the alert + return [] diff --git a/rules/my_local_rules/example_rule.yml b/rules/my_local_rules/example_rule.yml new file mode 100644 index 000000000..3b611c7cb --- /dev/null +++ b/rules/my_local_rules/example_rule.yml @@ -0,0 +1,34 @@ +AnalysisType: rule +Filename: example_rule.py +RuleID: "LogFamily.LogType.DetectionName" +DisplayName: "Human Readable Detection Name" +Enabled: true +LogTypes: + - AWS.CloudTrail # https://docs.panther.com/data-onboarding/supported-logs +Tags: # (Optional) + - Tag +Reports: # (Optional) + MITRE ATT&CK: + - "TA0027:T1475" # Tactic ID:Technique ID (https://attack.mitre.org/tactics/enterprise/) + PCI: + - "1.2.3" # Check ID +Severity: Medium # "Info", "Low", "Medium", "High", "Critical" +Description: > # (Optional) + A description of the detection's impact and why it exists +Runbook: > # (Optional) + How to handle this particular alert +Reference: A URL to an explanation of this type of detection or attack # (Optional) +DedupPeriodMinutes: 15 # The amount of time in minutes for grouping alerts (Optional, defaults to 60) +Threshold: 1 # The minimum number of event matches prior to an alert sending (Optional, defaults to 1) +SummaryAttributes: # A list of fields in the event to create top 5 summaries for (Optional) + - fieldName + - FieldName:nestedFieldName +InlineFilters: # https://docs.panther.com/detections/rules/inline-filters#yaml-inlinefilter-syntax + - KeyPath: environment + Condition: StartsWith + Value: "Sandbox" +Tests: # Unit tests for the Detection. Best practice is to include a positive and negative case (Optional) + - Name: Name # The title of the test + ExpectedResult: false # If the sample event should generate an alert or not + Log: # object + { "JSON": "string" } From a9dffa1c2bf7057c8dd8a930767ebc4c13c5b1d6 Mon Sep 17 00:00:00 2001 From: Ju Cho Date: Thu, 9 Jan 2025 13:19:52 -0500 Subject: [PATCH 02/23] Removed my local rules --- global_helpers/panther_azure_helpers.py | 39 ++ global_helpers/panther_azure_helpers.yml | 5 + .../azure_invite_external_users.py | 40 ++ .../azure_invite_external_users.yml | 158 +++++++ .../azure_entraid_rules/azure_mfa_disabled.py | 54 +++ .../azure_mfa_disabled.yml | 220 ++++++++++ .../azure_policy_changed.py | 36 ++ .../azure_policy_changed.yml | 203 +++++++++ .../azure_role_changed_pim.py | 26 ++ .../azure_role_changed_pim.yml | 395 ++++++++++++++++++ rules/my_local_rules/example_rule.py | 70 ---- rules/my_local_rules/example_rule.yml | 34 -- 12 files changed, 1176 insertions(+), 104 deletions(-) create mode 100644 global_helpers/panther_azure_helpers.py create mode 100644 global_helpers/panther_azure_helpers.yml create mode 100644 rules/azure_entraid_rules/azure_invite_external_users.py create mode 100644 rules/azure_entraid_rules/azure_invite_external_users.yml create mode 100644 rules/azure_entraid_rules/azure_mfa_disabled.py create mode 100644 rules/azure_entraid_rules/azure_mfa_disabled.yml create mode 100644 rules/azure_entraid_rules/azure_policy_changed.py create mode 100644 rules/azure_entraid_rules/azure_policy_changed.yml create mode 100644 rules/azure_entraid_rules/azure_role_changed_pim.py create mode 100644 rules/azure_entraid_rules/azure_role_changed_pim.yml delete mode 100644 rules/my_local_rules/example_rule.py delete mode 100644 rules/my_local_rules/example_rule.yml diff --git a/global_helpers/panther_azure_helpers.py b/global_helpers/panther_azure_helpers.py new file mode 100644 index 000000000..0884c6554 --- /dev/null +++ b/global_helpers/panther_azure_helpers.py @@ -0,0 +1,39 @@ +from panther_base_helpers import deep_get, deep_walk + + +def azure_rule_context(event: dict): + return { + "operationName": event.get("operationName", default=""), + "category": deep_get(event, "properties", "category", default=""), + "actor_id": deep_get( + event, "properties", "initiatedBy", "user", "id", default=" + Global helpers for Azure detections \ No newline at end of file diff --git a/rules/azure_entraid_rules/azure_invite_external_users.py b/rules/azure_entraid_rules/azure_invite_external_users.py new file mode 100644 index 000000000..202aec5fa --- /dev/null +++ b/rules/azure_entraid_rules/azure_invite_external_users.py @@ -0,0 +1,40 @@ +from panther_azure_helpers import azure_rule_context +from panther_base_helpers import deep_walk + + +def rule(event): + result = event.deep_get("properties", "result", default="") + if result != "success": + return False + + if event.get("operationName") != "Invite external user": + return False + + user_who_sent_invite = event.deep_get( + "properties", "initiatedBy", "user", "userPrincipalName", default="" + ) + user_who_received_invite = deep_walk( + event, "properties", "additionalDetails", "value", return_val="last", default="" + ) + domain = user_who_sent_invite.split("@")[-1] + + different_domain = not user_who_received_invite.endswith(domain) + + return different_domain + + +def title(event): + user_who_sent_invite = event.deep_get( + "properties", "initiatedBy", "user", "userPrincipalName", default="" + ) + user_who_received_invite = deep_walk( + event, "properties", "additionalDetails", "value", return_val="last", default="" + ) + + return ( + f"{user_who_sent_invite} invited {user_who_received_invite} to join as an EntraID member." + ) + + +def alert_context(event): + return azure_rule_context(event) diff --git a/rules/azure_entraid_rules/azure_invite_external_users.yml b/rules/azure_entraid_rules/azure_invite_external_users.yml new file mode 100644 index 000000000..36a8a8380 --- /dev/null +++ b/rules/azure_entraid_rules/azure_invite_external_users.yml @@ -0,0 +1,158 @@ +AnalysisType: rule +Filename: azure_invite_external_users.py +RuleID: "Azure.Audit.InviteExternalUsers" +DisplayName: "Azure Invite External Users" +Enabled: true +LogTypes: + - Azure.Audit +Severity: Low +Description: > + This detection looks for a Azure users inviting external users +Reports: + MITRE ATT&CK: + - TA0001:T1078 +Runbook: > + Verify the user permissions and investigate the external user details. If unauthorized, revoke access and block further invites. Update security policies. + +Reference: https://learn.microsoft.com/en-us/entra/identity/authentication/overview-authentication +SummaryAttributes: + - properties:ServicePrincipalName + - properties:UserPrincipalName + - properties:ipAddress +Tests: + - Name: Successful Invite external user + ExpectedResult: true + Log: + { + "callerIpAddress": "1.1.1.1", + "category": "AuditLogs", + "correlationId": "123456789", + "durationMs": 0, + "Level": 4, + "operationName": "Invite external user", + "operationVersion": "1.0", + "properties": { + "activityDateTime": "2024-09-23 14:33:09.049661100", + "activityDisplayName": "Invite external user", + "additionalDetails": [ + { + "key": "oid", + "value": "123456789" + }, + { + "key": "tid", + "value": "0123456789" + }, + { + "key": "ipaddr", + "value": "1.2.3.4" + }, + { + "key": "wids", + "value": "123456789" + }, + { + "key": "InvitationId", + "value": "123456789" + }, + { + "key": "invitedUserEmailAddress", + "value": "Kratos@climbingolympusrn.com" + } + ], + "category": "UserManagement", + "correlationId": "123456789", + "id": "Invited Users_123456789", + "initiatedBy": { + "user": { + "id": "123456789", + "ipAddress": "1.2.3.4.5", + "roles": [], + "userPrincipalName": "Zeus@mtolympus.com" + } + }, + "loggedByService": "Invited Users", + "operationType": "Add", + "result": "success", + "targetResources": [ + { + "administrativeUnits": [], + "displayName": "Zeus.Theboss", + "id": "123456789", + "type": "User" + } + ] + }, + "resourceId": "/tenants/123456789/providers/Microsoft.aadiam", + "resultSignature": "None", + "tenantId": "123456789", + "time": "2024-12-10 14:33:09.049661100" + } + - Name: Same org successful invite + ExpectedResult: false + Log: + { + "callerIpAddress": "1.1.1.1", + "category": "AuditLogs", + "correlationId": "123456789", + "durationMs": 0, + "Level": 4, + "operationName": "Invite external user", + "operationVersion": "1.0", + "properties": { + "activityDateTime": "2024-09-23 14:33:09.049661100", + "activityDisplayName": "Invite external user", + "additionalDetails": [ + { + "key": "oid", + "value": "123456789" + }, + { + "key": "tid", + "value": "0123456789" + }, + { + "key": "ipaddr", + "value": "1.2.3.4" + }, + { + "key": "wids", + "value": "123456789" + }, + { + "key": "InvitationId", + "value": "123456789" + }, + { + "key": "invitedUserEmailAddress", + "value": "Kratos@mtolympus.com" + } + ], + "category": "UserManagement", + "correlationId": "123456789", + "id": "Invited Users_123456789", + "initiatedBy": { + "user": { + "id": "123456789", + "ipAddress": "1.2.3.4.5", + "roles": [], + "userPrincipalName": "Zeus@mtolympus.com" + } + }, + "loggedByService": "Invited Users", + "operationType": "Add", + "result": "success", + "targetResources": [ + { + "administrativeUnits": [], + "displayName": "Zeus.Theboss", + "id": "123456789", + "type": "User" + } + ] + }, + "resourceId": "/tenants/123456789/providers/Microsoft.aadiam", + "resultSignature": "None", + "tenantId": "123456789", + "time": "2024-12-10 14:33:09.049661100" + } \ No newline at end of file diff --git a/rules/azure_entraid_rules/azure_mfa_disabled.py b/rules/azure_entraid_rules/azure_mfa_disabled.py new file mode 100644 index 000000000..3658d0bb3 --- /dev/null +++ b/rules/azure_entraid_rules/azure_mfa_disabled.py @@ -0,0 +1,54 @@ +import json + +from panther_azure_helpers import azure_rule_context +from panther_base_helpers import deep_walk + + +def get_mfa(policy): + parse_one = json.loads(policy) + + mfa_get = deep_walk(parse_one, "grantControls", "builtInControls", default=[]) + mfa_standardized = [n.lower() for n in mfa_get] + return mfa_standardized + + +def rule(event): + if event.get("operationName", default="") != "Update conditional access policy": + return False + + old_value = deep_walk( + event, + "properties", + "targetResources", + "modifiedProperties", + "oldValue", + return_val="first", + default="", + ) + new_value = deep_walk( + event, + "properties", + "targetResources", + "modifiedProperties", + "newValue", + return_val="first", + default="", + ) + + old_value_parsed = get_mfa(old_value) + new_value_parsed = get_mfa(new_value) + + return "mfa" in old_value_parsed and "mfa" not in new_value_parsed + + +def title(event): + actor_name = event.deep_get( + "properties", "initiatedBy", "user", "userPrincipalName", default="" + ) + policy = deep_walk(event, "properties", "targetResources", "displayName", default="") + + return f"mfa disabled by {actor_name} on the policy {policy}" + + +def alert_context(event): + return azure_rule_context(event) diff --git a/rules/azure_entraid_rules/azure_mfa_disabled.yml b/rules/azure_entraid_rules/azure_mfa_disabled.yml new file mode 100644 index 000000000..4c40d0f4f --- /dev/null +++ b/rules/azure_entraid_rules/azure_mfa_disabled.yml @@ -0,0 +1,220 @@ +AnalysisType: rule +Filename: azure_mfa_disabled.py +RuleID: "Azure.Audit.MFADisabled" +DisplayName: "Azure MFA Disabled" +Enabled: true +LogTypes: + - Azure.Audit +Severity: High +Description: > + This detection looks for MFA being disabled in conditional access policy +Reports: + MITRE ATT&CK: + - TA0005:T1556 + - TA0001:T1078 +Runbook: > + Verify if the change was authorized and investigate the user activity. If unauthorized, re-enable MFA, revoke access. + +Reference: https://learn.microsoft.com/en-us/entra/identity/authentication/overview-authentication +SummaryAttributes: + - properties:ServicePrincipalName + - properties:UserPrincipalName + - properties:ipAddress +Tests: + - Name: MFA Disabled Successful + ExpectedResult: true + Log: + { + "time": "2024-11-27T03:31:26.7088498Z", + "resourceId": "/tenants/123456789/providers/Microsoft.aadiam", + "operationName": "Update conditional access policy", + "operationVersion": "1.0", + "category": "AuditLogs", + "tenantId": "123456789", + "resultSignature": "None", + "durationMs": 0, + "callerIpAddress": "1.2.3.4.5", + "correlationId": "123456789", + "Level": 4, + "properties": { + "tenantId": "123456789", + "resultType": "", + "resultDescription": "", + "operationName": "Update conditional access policy", + "identity": "", + "tenantGeo": "NA", + "id": "IPCGraph_123456789", + "category": "Policy", + "correlationId": "123456789", + "result": "success", + "resultReason": null, + "activityDisplayName": "Update conditional access policy", + "activityDateTime": "2024-11-27T03:31:26.7088498+00:00", + "loggedByService": "Conditional Access", + "operationType": "Update", + "userAgent": null, + "initiatedBy": { + "user": { + "id": "123456789b", + "displayName": null, + "userPrincipalName": "Kratos@onmicrosoft.com", + "ipAddress": "1.2.3.4.5", + "roles": [] + } + }, + "targetResources": [ + { + "id": "123456789", + "displayName": "MFA", + "type": "Policy", + "modifiedProperties": [ + { + "displayName": "ConditionalAccessPolicy", + "oldValue": "{\"id\":\"123456789\",\"displayName\":\"MFA\",\"createdDateTime\":\"2024-11-21T16:48:48.1196443+00:00\",\"modifiedDateTime\":\"2024-11-21T16:56:13.9120766+00:00\",\"state\":\"enabled\",\"conditions\":{\"applications\":{\"includeApplications\":[\"None\"],\"excludeApplications\":[],\"includeUserActions\":[],\"includeAuthenticationContextClassReferences\":[],\"applicationFilter\":null},\"users\":{\"includeUsers\":[\"All\"],\"excludeUsers\":[],\"includeGroups\":[],\"excludeGroups\":[],\"includeRoles\":[],\"excludeRoles\":[]},\"userRiskLevels\":[],\"signInRiskLevels\":[],\"clientAppTypes\":[\"all\"],\"servicePrincipalRiskLevels\":[]},\"grantControls\":{\"operator\":\"OR\",\"builtInControls\":[\"MFA\"],\"customAuthenticationFactors\":[],\"termsOfUse\":[]},\"sessionControls\":{\"signInFrequency\":{\"value\":90,\"type\":\"days\",\"authenticationType\":\"primaryAndSecondaryAuthentication\",\"frequencyInterval\":\"timeBased\",\"isEnabled\":true}}}", + "newValue": "{\"id\":\"123456789\",\"displayName\":\"MFA\",\"createdDateTime\":\"2024-11-21T16:48:48.1196443+00:00\",\"modifiedDateTime\":\"2024-11-27T03:31:25.4989035+00:00\",\"state\":\"enabled\",\"conditions\":{\"applications\":{\"includeApplications\":[\"None\"],\"excludeApplications\":[],\"includeUserActions\":[],\"includeAuthenticationContextClassReferences\":[],\"applicationFilter\":null},\"users\":{\"includeUsers\":[\"All\"],\"excludeUsers\":[],\"includeGroups\":[],\"excludeGroups\":[],\"includeRoles\":[],\"excludeRoles\":[]},\"userRiskLevels\":[],\"signInRiskLevels\":[],\"clientAppTypes\":[\"all\"],\"servicePrincipalRiskLevels\":[]},\"sessionControls\":{\"signInFrequency\":{\"value\":90,\"type\":\"days\",\"authenticationType\":\"primaryAndSecondaryAuthentication\",\"frequencyInterval\":\"timeBased\",\"isEnabled\":true}}}" + } + ], + "administrativeUnits": [] + } + ], + "additionalDetails": [ + { + "key": "Category", + "value": "Conditional Access" + } + ] + } + } + - Name: MFA Enabled + ExpectedResult: false + Log: + { + "time": "2024-11-27T03:31:26.7088498Z", + "resourceId": "/tenants/123456789/providers/Microsoft.aadiam", + "operationName": "Update conditional access policy", + "operationVersion": "1.0", + "category": "AuditLogs", + "tenantId": "123456789", + "resultSignature": "None", + "durationMs": 0, + "callerIpAddress": "1.2.3.4.5", + "correlationId": "123456789", + "Level": 4, + "properties": { + "tenantId": "123456789", + "resultType": "", + "resultDescription": "", + "operationName": "Update conditional access policy", + "identity": "", + "tenantGeo": "NA", + "id": "IPCGraph_123456789", + "category": "Policy", + "correlationId": "123456789", + "result": "success", + "resultReason": null, + "activityDisplayName": "Update conditional access policy", + "activityDateTime": "2024-11-27T03:31:26.7088498+00:00", + "loggedByService": "Conditional Access", + "operationType": "Update", + "userAgent": null, + "initiatedBy": { + "user": { + "id": "123456789b", + "displayName": null, + "userPrincipalName": "Kratos@onmicrosoft.com", + "ipAddress": "1.2.3.4.5", + "roles": [] + } + }, + "targetResources": [ + { + "id": "123456789", + "displayName": "MFA", + "type": "Policy", + "modifiedProperties": [ + { + "displayName": "ConditionalAccessPolicy", + "oldValue": "{\"id\":\"123456789\",\"displayName\":\"MFA\",\"createdDateTime\":\"2024-11-21T16:48:48.1196443+00:00\",\"modifiedDateTime\":\"2024-11-27T03:31:25.4989035+00:00\",\"state\":\"enabled\",\"conditions\":{\"applications\":{\"includeApplications\":[\"None\"],\"excludeApplications\":[],\"includeUserActions\":[],\"includeAuthenticationContextClassReferences\":[],\"applicationFilter\":null},\"users\":{\"includeUsers\":[\"All\"],\"excludeUsers\":[],\"includeGroups\":[],\"excludeGroups\":[],\"includeRoles\":[],\"excludeRoles\":[]},\"userRiskLevels\":[],\"signInRiskLevels\":[],\"clientAppTypes\":[\"all\"],\"servicePrincipalRiskLevels\":[]},\"sessionControls\":{\"signInFrequency\":{\"value\":90,\"type\":\"days\",\"authenticationType\":\"primaryAndSecondaryAuthentication\",\"frequencyInterval\":\"timeBased\",\"isEnabled\":true}}}", + "newValue": "{\"id\":\"123456789\",\"displayName\":\"MFA\",\"createdDateTime\":\"2024-11-21T16:48:48.1196443+00:00\",\"modifiedDateTime\":\"2024-11-21T16:56:13.9120766+00:00\",\"state\":\"enabled\",\"conditions\":{\"applications\":{\"includeApplications\":[\"None\"],\"excludeApplications\":[],\"includeUserActions\":[],\"includeAuthenticationContextClassReferences\":[],\"applicationFilter\":null},\"users\":{\"includeUsers\":[\"All\"],\"excludeUsers\":[],\"includeGroups\":[],\"excludeGroups\":[],\"includeRoles\":[],\"excludeRoles\":[]},\"userRiskLevels\":[],\"signInRiskLevels\":[],\"clientAppTypes\":[\"all\"],\"servicePrincipalRiskLevels\":[]},\"grantControls\":{\"operator\":\"OR\",\"builtInControls\":[\"MFA\"],\"customAuthenticationFactors\":[],\"termsOfUse\":[]},\"sessionControls\":{\"signInFrequency\":{\"value\":90,\"type\":\"days\",\"authenticationType\":\"primaryAndSecondaryAuthentication\",\"frequencyInterval\":\"timeBased\",\"isEnabled\":true}}}", + } + ], + "administrativeUnits": [] + } + ], + "additionalDetails": [ + { + "key": "Category", + "value": "Conditional Access" + } + ] + } + } + - Name: MFA Disabled from another log + ExpectedResult: false + Log: + { + "time": "2024-11-27T03:31:26.2934305Z", + "resourceId": "/tenants/123456/providers/Microsoft.aadiam", + "operationName": "Update policy", + "operationVersion": "1.0", + "category": "AuditLogs", + "tenantId": "123456", + "resultSignature": "None", + "durationMs": 0, + "callerIpAddress": "1.2.3.4.5", + "correlationId": "123456", + "Level": 4, + "properties": { + "tenantId": "123456", + "resultType": "", + "resultDescription": "", + "operationName": "Update policy", + "identity": "", + "tenantGeo": "NA", + "id": "Directory_123145", + "category": "Policy", + "correlationId": "1235134516", + "result": "success", + "resultReason": "", + "activityDisplayName": "Update policy", + "activityDateTime": "2024-11-27T03:31:26.2934305+00:00", + "loggedByService": "Core Directory", + "operationType": "Update", + "userAgent": null, + "initiatedBy": { + "user": { + "id": "1324512355", + "displayName": null, + "userPrincipalName": "Kratos@onmicrosoft.com", + "ipAddress": "1.2.3.4.5", + "roles": [] + } + }, + "targetResources": [ + { + "id": "12351254", + "displayName": "MFA", + "type": "Policy", + "modifiedProperties": [ + { + "displayName": "PolicyDetail", + "oldValue": "[\"{\\\"Version\\\":1,\\\"CreatedDateTime\\\":\\\"2024-11-21T16:48:48.1196443Z\\\",\\\"ModifiedDateTime\\\":\\\"2024-11-21T16:56:13.9120766Z\\\",\\\"State\\\":\\\"Enabled\\\",\\\"Conditions\\\":{\\\"Applications\\\":{\\\"Include\\\":[{\\\"Applications\\\":[\\\"None\\\"]}]},\\\"Users\\\":{\\\"Include\\\":[{\\\"Users\\\":[\\\"All\\\"]}]}},\\\"Controls\\\":[{\\\"Control\\\":[\\\"Mfa\\\"]}],\\\"SessionControls\\\":[\\\"SignInFrequency\\\"],\\\"SignInFrequencyTimeSpan\\\":\\\"90.00:00:00\\\",\\\"SignInFrequencyType\\\":10,\\\"EnforceAllPoliciesForEas\\\":true,\\\"IncludeOtherLegacyClientTypeForEvaluation\\\":true}\"]", + "newValue": "[\"{\\\"Version\\\":1,\\\"CreatedDateTime\\\":\\\"2024-11-21T16:48:48.1196443Z\\\",\\\"ModifiedDateTime\\\":\\\"2024-11-27T03:31:25.4989035Z\\\",\\\"State\\\":\\\"Enabled\\\",\\\"Conditions\\\":{\\\"Applications\\\":{\\\"Include\\\":[{\\\"Applications\\\":[\\\"None\\\"]}]},\\\"Users\\\":{\\\"Include\\\":[{\\\"Users\\\":[\\\"All\\\"]}]}},\\\"SessionControls\\\":[\\\"SignInFrequency\\\"],\\\"SignInFrequencyTimeSpan\\\":\\\"90.00:00:00\\\",\\\"SignInFrequencyType\\\":10,\\\"EnforceAllPoliciesForEas\\\":true,\\\"IncludeOtherLegacyClientTypeForEvaluation\\\":true}\"]" + }, + { + "displayName": "Included Updated Properties", + "oldValue": null, + "newValue": "\"PolicyDetail\"" + } + ], + "administrativeUnits": [] + } + ], + "additionalDetails": [ + { + "key": "User-Agent", + "value": "Microsoft Azure Graph Client Library 1.0" + } + ] + } + } \ No newline at end of file diff --git a/rules/azure_entraid_rules/azure_policy_changed.py b/rules/azure_entraid_rules/azure_policy_changed.py new file mode 100644 index 000000000..fd7ff92f4 --- /dev/null +++ b/rules/azure_entraid_rules/azure_policy_changed.py @@ -0,0 +1,36 @@ +from panther_azure_helpers import azure_rule_context, azure_success +from panther_base_helpers import deep_walk + +POLICY_OPERATION = "policy" + +IGNORE_ACTIONS = ["Add", "Added"] + + +def rule(event): + operation = event.get("operationName", default="") + if not azure_success or not operation.endswith(POLICY_OPERATION): + return False + # Ignore added policies + if any( + ( + event.deep_get("properties", "operationName", default="").startswith(ignore) + for ignore in IGNORE_ACTIONS + ) + ): + return False + + return True + + +def title(event): + operation_name = event.get("operationName", default="") + actor_name = event.deep_get( + "properties", "initiatedBy", "user", "userPrincipalName", default="" + ) + policy = deep_walk(event, "properties", "targetResources", "displayName", default="") + + return f"{operation_name} by {actor_name} on the policy {policy}" + + +def alert_context(event): + return azure_rule_context(event) diff --git a/rules/azure_entraid_rules/azure_policy_changed.yml b/rules/azure_entraid_rules/azure_policy_changed.yml new file mode 100644 index 000000000..446046490 --- /dev/null +++ b/rules/azure_entraid_rules/azure_policy_changed.yml @@ -0,0 +1,203 @@ +AnalysisType: rule +Filename: azure_policy_changed.py +RuleID: "Azure.Audit.PolicyChanged" +DisplayName: "Azure Policy Changed" +Enabled: true +LogTypes: + - Azure.Audit +Severity: Low +DedupPeriodMinutes: 10 +Description: > + This detection looks for policy changes in AuditLogs +Reports: + MITRE ATT&CK: + - TA0005:T1526 +Runbook: > + Verify if the change was authorized and review the modifications. If unauthorized, revert the policy, notify relevant teams, and investigate the user actions. + +Reference: https://learn.microsoft.com/en-us/entra/identity/authentication/overview-authentication +SummaryAttributes: + - properties:ServicePrincipalName + - properties:UserPrincipalName + - properties:ipAddress +Tests: + - Name: Policy Changed + ExpectedResult: true + Log: + { + "time": "2024-12-10T02:22:58.7270280Z", + "resourceId": "/tenants/123145/providers/Microsoft.aadiam", + "operationName": "Delete conditional access policy", + "operationVersion": "1.0", + "category": "AuditLogs", + "tenantId": "12341234", + "resultSignature": "None", + "durationMs": 0, + "callerIpAddress": "1.2.3.4.5", + "correlationId": "1324515", + "Level": 4, + "properties": { + "tenantId": "132455112", + "resultType": "", + "resultDescription": "", + "operationName": "Delete conditional access policy", + "identity": "", + "tenantGeo": "NA", + "id": "IPCGraph_af23466234", + "category": "Policy", + "correlationId": "23456234", + "result": "success", + "resultReason": null, + "activityDisplayName": "Delete conditional access policy", + "activityDateTime": "2024-11-27T02:22:58.727028+00:00", + "loggedByService": "Conditional Access", + "operationType": "Delete", + "userAgent": null, + "initiatedBy": { + "user": { + "id": "234526234", + "displayName": null, + "userPrincipalName": "Kratos@mtolympus.com", + "ipAddress": "1.2.3.4.5", + "roles": [] + } + }, + "targetResources": [ + { + "id": "5e3cb481-2814-4295-b4cc-2440a6d66c86", + "displayName": "Outside MFA", + "type": "Policy", + "modifiedProperties": [ + { + "displayName": "ConditionalAccessPolicy", + "oldValue": "{\"id\":\"5e3cb481-2814-4295-b4cc-2440a6d66c86\",\"displayName\":\"Outside MFA\",\"createdDateTime\":\"2024-11-27T02:22:19.8926587+00:00\",\"state\":\"enabled\",\"conditions\":{\"applications\":{\"includeApplications\":[\"None\"],\"excludeApplications\":[],\"includeUserActions\":[],\"includeAuthenticationContextClassReferences\":[],\"applicationFilter\":null},\"users\":{\"includeUsers\":[],\"excludeUsers\":[],\"includeGroups\":[],\"excludeGroups\":[],\"includeRoles\":[],\"excludeRoles\":[],\"includeGuestsOrExternalUsers\":{\"guestOrExternalUserTypes\":63,\"externalTenants\":{}}},\"userRiskLevels\":[],\"signInRiskLevels\":[],\"clientAppTypes\":[\"all\"],\"servicePrincipalRiskLevels\":[]},\"grantControls\":{\"operator\":\"OR\",\"builtInControls\":[\"mfa\"],\"customAuthenticationFactors\":[],\"termsOfUse\":[]}}", + "newValue": null + } + ], + "administrativeUnits": [] + } + ], + "additionalDetails": [ + { + "key": "Category", + "value": "Conditional Access" + } + ] + } + } + - Name: Policy Updated + ExpectedResult: true + Log: + { + "time": "2024-11-21T16:47:21.6424070Z", + "resourceId": "/tenants/1234155/providers/Microsoft.aadiam", + "operationName": "Update policy", + "operationVersion": "1.0", + "category": "AuditLogs", + "tenantId": "12341235", + "resultSignature": "None", + "durationMs": 0, + "callerIpAddress": "1.2.3.4.5", + "correlationId": "`1244`12123", + "Level": 4, + "properties": { + "tenantId": "123515-5-1235", + "resultType": "", + "resultDescription": "", + "operationName": "Update policy", + "identity": "", + "tenantGeo": "NA", + "id": "Directory_12351123", + "category": "Policy", + "correlationId": "12341513", + "result": "success", + "resultReason": "", + "activityDisplayName": "Delete policy", + "activityDateTime": "2024-11-21T16:47:21.642407+00:00", + "loggedByService": "Core Directory", + "operationType": "Delete", + "userAgent": null, + "initiatedBy": { + "user": { + "id": "12345123", + "displayName": null, + "userPrincipalName": "Kratos@onmicrosoft.com", + "ipAddress": "1.2.3.4.5", + "roles": [] + } + }, + "targetResources": [ + { + "id": "123451516", + "displayName": "Require multifactor authentication for all users", + "type": "Policy", + "modifiedProperties": [], + "administrativeUnits": [] + } + ], + "additionalDetails": [ + { + "key": "User-Agent", + "value": "Microsoft Azure Graph Client Library 1.0" + } + ] + } + } + - Name: Policy Added + ExpectedResult: false + Log: + { + "time": "2024-11-21T16:47:21.6424070Z", + "resourceId": "/tenants/1234155/providers/Microsoft.aadiam", + "operationName": "Added policy", + "operationVersion": "1.0", + "category": "AuditLogs", + "tenantId": "12341235", + "resultSignature": "None", + "durationMs": 0, + "callerIpAddress": "1.2.3.4.5", + "correlationId": "`1244`12123", + "Level": 4, + "properties": { + "tenantId": "123515-5-1235", + "resultType": "", + "resultDescription": "", + "operationName": "Added policy", + "identity": "", + "tenantGeo": "NA", + "id": "Directory_12351123", + "category": "Policy", + "correlationId": "12341513", + "result": "success", + "resultReason": "", + "activityDisplayName": "Added policy", + "activityDateTime": "2024-11-21T16:47:21.642407+00:00", + "loggedByService": "Core Directory", + "operationType": "Delete", + "userAgent": null, + "initiatedBy": { + "user": { + "id": "12345123", + "displayName": null, + "userPrincipalName": "Kratos@onmicrosoft.com", + "ipAddress": "1.2.3.4.5", + "roles": [] + } + }, + "targetResources": [ + { + "id": "123451516", + "displayName": "Require multifactor authentication for all users", + "type": "Policy", + "modifiedProperties": [], + "administrativeUnits": [] + } + ], + "additionalDetails": [ + { + "key": "User-Agent", + "value": "Microsoft Azure Graph Client Library 1.0" + } + ] + } + } diff --git a/rules/azure_entraid_rules/azure_role_changed_pim.py b/rules/azure_entraid_rules/azure_role_changed_pim.py new file mode 100644 index 000000000..2f928676a --- /dev/null +++ b/rules/azure_entraid_rules/azure_role_changed_pim.py @@ -0,0 +1,26 @@ +from panther_azure_helpers import azure_rule_context, azure_success, get_target_name +from panther_base_helpers import deep_walk + + +def rule(event): + operation = event.get("operationName", default="") + if azure_success and "Add member to role in PIM completed" in operation: + return True + + return False + + +def title(event): + operation_name = event.get("operationName", default="") + actor_name = event.deep_get( + "properties", "initiatedBy", "user", "userPrincipalName", default="" + ) + target_name = get_target_name(event) + role = deep_walk( + event, "properties", "targetResources", "displayName", return_val="first", default="" + ) + return f"{actor_name} added {target_name} as {role} successfully with {operation_name}" + + +def alert_context(event): + return azure_rule_context(event) diff --git a/rules/azure_entraid_rules/azure_role_changed_pim.yml b/rules/azure_entraid_rules/azure_role_changed_pim.yml new file mode 100644 index 000000000..7cf816af2 --- /dev/null +++ b/rules/azure_entraid_rules/azure_role_changed_pim.yml @@ -0,0 +1,395 @@ +AnalysisType: rule +Filename: azure_role_changed_PIM.py +RuleID: "Azure.Audit.RoleChangedPIM" +DisplayName: "Azure Role Changed PIM" +Enabled: true +LogTypes: + - Azure.Audit +Severity: Medium +DedupPeriodMinutes: 0 +Description: > + This detection looks for a change in member's PIM roles in EntraID +Reports: + MITRE ATT&CK: + - TA0042:T1586 +Runbook: > + Verify if the role change was authorized and review the affected user. If unauthorized, revert the role change, notify relevant teams, + +Reference: https://learn.microsoft.com/en-us/entra/identity/authentication/overview-authentication +SummaryAttributes: + - properties:ServicePrincipalName + - properties:UserPrincipalName + - properties:ipAddress +Tests: + - Name: Successfully added PIM role + ExpectedResult: true + Log: + { + "category": "AuditLogs", + "correlationId": "1234155", + "durationMs": 0, + "identity": "Ju Cho", + "Level": 4, + "operationName": "Add member to role in PIM completed (permanent)", + "operationVersion": "1.0", + "properties": { + "activityDateTime": "2024-12-16 16:32:16.087554000", + "activityDisplayName": "Add member to role in PIM completed (permanent)", + "additionalDetails": [ + { + "key": "RoleDefinitionOriginId", + "value": "123451235" + }, + { + "key": "RoleDefinitionOriginType", + "value": "BuiltInRole" + }, + { + "key": "TemplateId", + "value": "123412351" + }, + { + "key": "StartTime", + "value": "2024-12-16T16:32:15.8441686Z" + }, + { + "key": "Justification", + "value": "test assign" + }, + { + "key": "oid", + "value": "12351534" + }, + { + "key": "tid", + "value": "345667733" + }, + { + "key": "wids", + "value": "234523454" + }, + { + "key": "ipaddr", + "value": "1.2.3.4.5" + }, + { + "key": "RequestId", + "value": "651346123452" + } + ], + "category": "RoleManagement", + "correlationId": "12345", + "id": "PIM_123415", + "initiatedBy": { + "user": { + "displayName": "Ju Cho", + "id": "12345", + "roles": [], + "userPrincipalName": "Radahn@Starscourge.onmicrosoft.com" + } + }, + "loggedByService": "PIM", + "operationType": "Update", + "result": "success", + "resultReason": "test assign", + "targetResources": [ + { + "administrativeUnits": [], + "displayName": "Application Administrator", + "id": "12345", + "modifiedProperties": [ + { + "displayName": "RoleDefinitionOriginId", + "newValue": "\"12345\"", + "oldValue": "\"\"" + }, + { + "displayName": "RoleDefinitionOriginType", + "newValue": "\"BuiltInRole\"", + "oldValue": "\"\"" + }, + { + "displayName": "TemplateId", + "newValue": "\"12345\"", + "oldValue": "\"\"" + } + ], + "type": "Role" + }, + { + "administrativeUnits": [], + "id": "12345", + "type": "Request" + }, + { + "administrativeUnits": [], + "displayName": "Malenia", + "id": "12345", + "type": "User" + }, + { + "administrativeUnits": [], + "displayName": "Panther", + "id": "12345", + "type": "Directory" + }, + { + "administrativeUnits": [], + "id": "12345", + "type": "Other" + } + ] + }, + "resourceId": "/tenants/12345/providers/Microsoft.aadiam", + "resultSignature": "None", + "tenantId": "12345", + "time": "2024-12-16 16:32:16.087554000" + } + - Name: requested adding PIM role + ExpectedResult: false + Log: + { + "category": "AuditLogs", + "correlationId": "1234155", + "durationMs": 0, + "identity": "Ju Cho", + "Level": 4, + "operationName": "Add member to role in PIM requested (permanent)", + "operationVersion": "1.0", + "properties": { + "activityDateTime": "2024-12-16 16:32:16.087554000", + "activityDisplayName": "Add member to role in PIM requested (permanent)", + "additionalDetails": [ + { + "key": "RoleDefinitionOriginId", + "value": "123451235" + }, + { + "key": "RoleDefinitionOriginType", + "value": "BuiltInRole" + }, + { + "key": "TemplateId", + "value": "123412351" + }, + { + "key": "StartTime", + "value": "2024-12-16T16:32:15.8441686Z" + }, + { + "key": "Justification", + "value": "test assign" + }, + { + "key": "oid", + "value": "12351534" + }, + { + "key": "tid", + "value": "345667733" + }, + { + "key": "wids", + "value": "234523454" + }, + { + "key": "ipaddr", + "value": "1.2.3.4.5" + }, + { + "key": "RequestId", + "value": "651346123452" + } + ], + "category": "RoleManagement", + "correlationId": "12345", + "id": "PIM_123415", + "initiatedBy": { + "user": { + "displayName": "Ju Cho", + "id": "12345", + "roles": [], + "userPrincipalName": "Radahn@Starscourge.onmicrosoft.com" + } + }, + "loggedByService": "PIM", + "operationType": "Update", + "result": "success", + "resultReason": "test assign", + "targetResources": [ + { + "administrativeUnits": [], + "displayName": "Application Administrator", + "id": "12345", + "modifiedProperties": [ + { + "displayName": "RoleDefinitionOriginId", + "newValue": "\"12345\"", + "oldValue": "\"\"" + }, + { + "displayName": "RoleDefinitionOriginType", + "newValue": "\"BuiltInRole\"", + "oldValue": "\"\"" + }, + { + "displayName": "TemplateId", + "newValue": "\"12345\"", + "oldValue": "\"\"" + } + ], + "type": "Role" + }, + { + "administrativeUnits": [], + "id": "12345", + "type": "Request" + }, + { + "administrativeUnits": [], + "displayName": "Malenia", + "id": "12345", + "type": "User" + }, + { + "administrativeUnits": [], + "displayName": "Panther", + "id": "12345", + "type": "Directory" + }, + { + "administrativeUnits": [], + "id": "12345", + "type": "Other" + } + ] + }, + "resourceId": "/tenants/12345/providers/Microsoft.aadiam", + "resultSignature": "None", + "tenantId": "12345", + "time": "2024-12-16 16:32:16.087554000" + } + - Name: Add member to role (Non PIM) + ExpectedResult: false + Log: + { + "category": "AuditLogs", + "correlationId": "1234155", + "durationMs": 0, + "identity": "Ju Cho", + "Level": 4, + "operationName": "Add member to role", + "operationVersion": "1.0", + "properties": { + "activityDateTime": "2024-12-16 16:32:16.087554000", + "activityDisplayName": "Add member to role", + "additionalDetails": [ + { + "key": "RoleDefinitionOriginId", + "value": "123451235" + }, + { + "key": "RoleDefinitionOriginType", + "value": "BuiltInRole" + }, + { + "key": "TemplateId", + "value": "123412351" + }, + { + "key": "StartTime", + "value": "2024-12-16T16:32:15.8441686Z" + }, + { + "key": "Justification", + "value": "test assign" + }, + { + "key": "oid", + "value": "12351534" + }, + { + "key": "tid", + "value": "345667733" + }, + { + "key": "wids", + "value": "234523454" + }, + { + "key": "ipaddr", + "value": "1.2.3.4.5" + }, + { + "key": "RequestId", + "value": "651346123452" + } + ], + "category": "RoleManagement", + "correlationId": "12345", + "id": "PIM_123415", + "initiatedBy": { + "user": { + "displayName": "Ju Cho", + "id": "12345", + "roles": [], + "userPrincipalName": "Radahn@Starscourge.onmicrosoft.com" + } + }, + "loggedByService": "PIM", + "operationType": "Update", + "result": "success", + "resultReason": "test assign", + "targetResources": [ + { + "administrativeUnits": [], + "displayName": "Application Administrator", + "id": "12345", + "modifiedProperties": [ + { + "displayName": "RoleDefinitionOriginId", + "newValue": "\"12345\"", + "oldValue": "\"\"" + }, + { + "displayName": "RoleDefinitionOriginType", + "newValue": "\"BuiltInRole\"", + "oldValue": "\"\"" + }, + { + "displayName": "TemplateId", + "newValue": "\"123415\"", + "oldValue": "\"\"" + } + ], + "type": "Role" + }, + { + "administrativeUnits": [], + "id": "12345", + "type": "Request" + }, + { + "administrativeUnits": [], + "displayName": "Malenia", + "id": "12345", + "type": "User" + }, + { + "administrativeUnits": [], + "displayName": "Panther", + "id": "12345", + "type": "Directory" + }, + { + "administrativeUnits": [], + "id": "12345", + "type": "Other" + } + ] + }, + "resourceId": "/tenants/12345/providers/Microsoft.aadiam", + "resultSignature": "None", + "tenantId": "12345", + "time": "2024-12-16 16:32:16.087554000" + } diff --git a/rules/my_local_rules/example_rule.py b/rules/my_local_rules/example_rule.py deleted file mode 100644 index efc28cfcd..000000000 --- a/rules/my_local_rules/example_rule.py +++ /dev/null @@ -1,70 +0,0 @@ -from panther_base_helpers import pattern_match - - -## Required -# -# The logic to determine if an alert should send. -# return True = Alert, False = Do not Alert -def rule(event): - return event.get("field") == "value" and event.deep_get("field", "nestedValue") - - -## Optional Functions -# -# Set custom alert titles, must return a string. -# If not defined, defaults to the rule display name or rule ID. -def title(event): - if pattern_match(event.get("field"), "string*"): - return f"This is my alert title {event.get('field')}" - return f"This is my fallback title {event.get('field')}" - - -# Set custom deduplication strings, must return a string. -# If not defined, defaults to the alert title. -def dedup(event): - return event.get("identity") - - -# Additional information append to an alert, must return a dictionary -def alert_context(event): - return { - "someField": event.get("someField"), - "someRandomValue": 4, # chosen by a dice roll, guaranteed to be random - } - - -## Override Functions -# -# Override the severity of an alert based on the contents of the events, -# must return one of the following strings "INFO", "LOW", "MEDIUM', "HIGH", "CRITICAL" -def severity(event): - if event.get("field") == "value": - return "INFO" - return "HIGH" - - -# Override the description of the alert, must return a string -def description(event): - return f"Some Alert Description {event.get('Something')}" - - -# Override the reference in the alert, must return a string -def reference(event): - return f"https://some.com/reference/{event.get('Something')}" - - -# Override the runbook in the alert, must return a string -def runbook(event): - return f"If this happens, do {event.get('Something')}" - - -# Override the destination(s) the alert is sent to, must return a list of strings corresponding to -# panther destinations -BAD_THINGS = [] - - -def destinations(event): - if event.get("Something") in BAD_THINGS: - return ["01234567-1edf-4edb-8f5b-0123456789a"] - # Suppress the alert - return [] diff --git a/rules/my_local_rules/example_rule.yml b/rules/my_local_rules/example_rule.yml deleted file mode 100644 index 3b611c7cb..000000000 --- a/rules/my_local_rules/example_rule.yml +++ /dev/null @@ -1,34 +0,0 @@ -AnalysisType: rule -Filename: example_rule.py -RuleID: "LogFamily.LogType.DetectionName" -DisplayName: "Human Readable Detection Name" -Enabled: true -LogTypes: - - AWS.CloudTrail # https://docs.panther.com/data-onboarding/supported-logs -Tags: # (Optional) - - Tag -Reports: # (Optional) - MITRE ATT&CK: - - "TA0027:T1475" # Tactic ID:Technique ID (https://attack.mitre.org/tactics/enterprise/) - PCI: - - "1.2.3" # Check ID -Severity: Medium # "Info", "Low", "Medium", "High", "Critical" -Description: > # (Optional) - A description of the detection's impact and why it exists -Runbook: > # (Optional) - How to handle this particular alert -Reference: A URL to an explanation of this type of detection or attack # (Optional) -DedupPeriodMinutes: 15 # The amount of time in minutes for grouping alerts (Optional, defaults to 60) -Threshold: 1 # The minimum number of event matches prior to an alert sending (Optional, defaults to 1) -SummaryAttributes: # A list of fields in the event to create top 5 summaries for (Optional) - - fieldName - - FieldName:nestedFieldName -InlineFilters: # https://docs.panther.com/detections/rules/inline-filters#yaml-inlinefilter-syntax - - KeyPath: environment - Condition: StartsWith - Value: "Sandbox" -Tests: # Unit tests for the Detection. Best practice is to include a positive and negative case (Optional) - - Name: Name # The title of the test - ExpectedResult: false # If the sample event should generate an alert or not - Log: # object - { "JSON": "string" } From 4d4826b70b9aa77f9ae1d392768359167031b142 Mon Sep 17 00:00:00 2001 From: joongi92 <144375655+joongi92@users.noreply.github.com> Date: Wed, 22 Jan 2025 11:17:09 -0500 Subject: [PATCH 03/23] Update upload.yml --- .github/workflows/upload.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/upload.yml b/.github/workflows/upload.yml index 5ccabafb3..ca73f0449 100644 --- a/.github/workflows/upload.yml +++ b/.github/workflows/upload.yml @@ -40,4 +40,4 @@ jobs: - name: upload run: | - pipenv run panther_analysis_tool upload --api-host ${{ env.API_HOST }} --api-token ${{ env.API_TOKEN }} + pipenv run panther_analysis_tool upload --api-host https://zclj1gp1w8.execute-api.us-west-2.amazonaws.com/v1/public/graphql --api-token z0ddJzwbym7WtgGVtelpp8wIgMmNSSvq4ABlIb6a From 89cb8d04a2231002e16c6aa086ed6864e3d520a6 Mon Sep 17 00:00:00 2001 From: joongi92 Date: Thu, 23 Jan 2025 17:28:21 -0500 Subject: [PATCH 04/23] removed API keys --- .github/workflows/upload.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/upload.yml b/.github/workflows/upload.yml index ca73f0449..b9131d718 100644 --- a/.github/workflows/upload.yml +++ b/.github/workflows/upload.yml @@ -14,10 +14,6 @@ jobs: API_HOST: ${{ secrets.API_HOST }} API_TOKEN: ${{ secrets.API_TOKEN }} steps: - - uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 - with: - egress-policy: audit - - name: Validate Secrets if: ${{ env.API_HOST == '' || env.API_TOKEN == '' }} run: | @@ -40,4 +36,4 @@ jobs: - name: upload run: | - pipenv run panther_analysis_tool upload --api-host https://zclj1gp1w8.execute-api.us-west-2.amazonaws.com/v1/public/graphql --api-token z0ddJzwbym7WtgGVtelpp8wIgMmNSSvq4ABlIb6a + pipenv run panther_analysis_tool upload --api-host ${{ env.API_HOST }} --api-token ${{ env.API_TOKEN }} \ No newline at end of file From 550a261ce5b56d660c3e19519f3ef275229cfb6c Mon Sep 17 00:00:00 2001 From: joongi92 Date: Mon, 27 Jan 2025 16:09:58 -0500 Subject: [PATCH 05/23] Updated rules, removed panther_azure_helpers and added to panther_msft_helpers --- global_helpers/panther_azure_helpers.py | 39 ----- global_helpers/panther_azure_helpers.yml | 5 - global_helpers/panther_msft_helpers.py | 39 +++++ .../azure_invite_external_users.py | 17 +-- .../azure_invite_external_users.yml | 136 ++++++++++++++++++ .../azure_entraid_rules/azure_mfa_disabled.py | 10 +- .../azure_policy_changed.py | 14 +- .../azure_role_changed_pim.py | 9 +- 8 files changed, 193 insertions(+), 76 deletions(-) delete mode 100644 global_helpers/panther_azure_helpers.py delete mode 100644 global_helpers/panther_azure_helpers.yml diff --git a/global_helpers/panther_azure_helpers.py b/global_helpers/panther_azure_helpers.py deleted file mode 100644 index 0884c6554..000000000 --- a/global_helpers/panther_azure_helpers.py +++ /dev/null @@ -1,39 +0,0 @@ -from panther_base_helpers import deep_get, deep_walk - - -def azure_rule_context(event: dict): - return { - "operationName": event.get("operationName", default=""), - "category": deep_get(event, "properties", "category", default=""), - "actor_id": deep_get( - event, "properties", "initiatedBy", "user", "id", default=" - Global helpers for Azure detections \ No newline at end of file diff --git a/global_helpers/panther_msft_helpers.py b/global_helpers/panther_msft_helpers.py index 620f453c4..77e7b43e4 100644 --- a/global_helpers/panther_msft_helpers.py +++ b/global_helpers/panther_msft_helpers.py @@ -1,3 +1,5 @@ +from panther_base_helpers import deep_get, deep_walk + def msft_graph_alert_context(event): return { "category": event.get("category", ""), @@ -18,3 +20,40 @@ def m365_alert_context(event): "application": event.get("Application", ""), "actor": event.get("Actor", []), } + +def azure_rule_context(event: dict): + return { + "operationName": event.get("operationName", default=""), + "category": deep_get(event, "properties", "category", default=""), + "actor_id": deep_get( + event, "properties", "initiatedBy", "user", "id", default=" Date: Fri, 31 Jan 2025 09:02:52 -0700 Subject: [PATCH 06/23] Update global_helpers/panther_msft_helpers.py Co-authored-by: ben-githubs <38414634+ben-githubs@users.noreply.github.com> --- global_helpers/panther_msft_helpers.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/global_helpers/panther_msft_helpers.py b/global_helpers/panther_msft_helpers.py index 77e7b43e4..263f2762d 100644 --- a/global_helpers/panther_msft_helpers.py +++ b/global_helpers/panther_msft_helpers.py @@ -54,6 +54,4 @@ def get_target_name(event, target_type="User"): def azure_success(event): result = event.deep_get("properties", "result", default="") - if result != "success": - return False - return True + return result == "success" From f7e7cb0b12e43aab23191f1f8d6e728f8ef34db5 Mon Sep 17 00:00:00 2001 From: Ariel Ropek <79653153+arielkr256@users.noreply.github.com> Date: Fri, 31 Jan 2025 09:02:58 -0700 Subject: [PATCH 07/23] Update global_helpers/panther_msft_helpers.py Co-authored-by: ben-githubs <38414634+ben-githubs@users.noreply.github.com> --- global_helpers/panther_msft_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global_helpers/panther_msft_helpers.py b/global_helpers/panther_msft_helpers.py index 263f2762d..19ea82758 100644 --- a/global_helpers/panther_msft_helpers.py +++ b/global_helpers/panther_msft_helpers.py @@ -47,7 +47,7 @@ def get_target_name(event, target_type="User"): target_resources = deep_walk(event, "properties", "targetResources", default="") for resource in target_resources: if resource.get("type") == target_type: - return resource.get("displayName", "No displayName found") + return resource.get("displayName", "NO DISPLAY NAME FOUND") return "NO DISPLAY NAME FOUND" From a520480093851aaf8217b869aa83f03fd7aa7430 Mon Sep 17 00:00:00 2001 From: Ariel Ropek <79653153+arielkr256@users.noreply.github.com> Date: Fri, 31 Jan 2025 09:03:21 -0700 Subject: [PATCH 08/23] Update rules/azure_entraid_rules/azure_mfa_disabled.yml Co-authored-by: ben-githubs <38414634+ben-githubs@users.noreply.github.com> --- rules/azure_entraid_rules/azure_mfa_disabled.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/azure_entraid_rules/azure_mfa_disabled.yml b/rules/azure_entraid_rules/azure_mfa_disabled.yml index 4c40d0f4f..e608175b1 100644 --- a/rules/azure_entraid_rules/azure_mfa_disabled.yml +++ b/rules/azure_entraid_rules/azure_mfa_disabled.yml @@ -186,7 +186,7 @@ Tests: "id": "1324512355", "displayName": null, "userPrincipalName": "Kratos@onmicrosoft.com", - "ipAddress": "1.2.3.4.5", + "ipAddress": "1.2.3.4", "roles": [] } }, From e9483279c53ad7b07b5dbcea75de7c69e97329f7 Mon Sep 17 00:00:00 2001 From: Ariel Ropek <79653153+arielkr256@users.noreply.github.com> Date: Fri, 31 Jan 2025 09:03:32 -0700 Subject: [PATCH 09/23] Update rules/azure_entraid_rules/azure_mfa_disabled.py Co-authored-by: ben-githubs <38414634+ben-githubs@users.noreply.github.com> --- rules/azure_entraid_rules/azure_mfa_disabled.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/azure_entraid_rules/azure_mfa_disabled.py b/rules/azure_entraid_rules/azure_mfa_disabled.py index 9082e027e..6d06e47a3 100644 --- a/rules/azure_entraid_rules/azure_mfa_disabled.py +++ b/rules/azure_entraid_rules/azure_mfa_disabled.py @@ -45,7 +45,7 @@ def title(event): ) policy = event.deep_walk("properties", "targetResources", "displayName", default="") - return f"mfa disabled by {actor_name} on the policy {policy}" + return f"MFA disabled by {actor_name} on the policy {policy}" def alert_context(event): From 191c58c0bcb8b2fe88fc67e6e84ca33ffd9d6d55 Mon Sep 17 00:00:00 2001 From: Ariel Ropek <79653153+arielkr256@users.noreply.github.com> Date: Fri, 31 Jan 2025 09:03:41 -0700 Subject: [PATCH 10/23] Update rules/azure_entraid_rules/azure_mfa_disabled.py Co-authored-by: ben-githubs <38414634+ben-githubs@users.noreply.github.com> --- rules/azure_entraid_rules/azure_mfa_disabled.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/azure_entraid_rules/azure_mfa_disabled.py b/rules/azure_entraid_rules/azure_mfa_disabled.py index 6d06e47a3..40597e93b 100644 --- a/rules/azure_entraid_rules/azure_mfa_disabled.py +++ b/rules/azure_entraid_rules/azure_mfa_disabled.py @@ -41,7 +41,7 @@ def rule(event): def title(event): actor_name = event.deep_get( - "properties", "initiatedBy", "user", "userPrincipalName", default="" + "properties", "initiatedBy", "user", "userPrincipalName", default="" ) policy = event.deep_walk("properties", "targetResources", "displayName", default="") From ac2c56fa608e992e861d16c96520777fd37d7d6c Mon Sep 17 00:00:00 2001 From: Ariel Ropek <79653153+arielkr256@users.noreply.github.com> Date: Fri, 31 Jan 2025 09:04:04 -0700 Subject: [PATCH 11/23] Update rules/azure_entraid_rules/azure_mfa_disabled.yml Co-authored-by: ben-githubs <38414634+ben-githubs@users.noreply.github.com> --- rules/azure_entraid_rules/azure_mfa_disabled.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/azure_entraid_rules/azure_mfa_disabled.yml b/rules/azure_entraid_rules/azure_mfa_disabled.yml index e608175b1..7bfe9f865 100644 --- a/rules/azure_entraid_rules/azure_mfa_disabled.yml +++ b/rules/azure_entraid_rules/azure_mfa_disabled.yml @@ -33,7 +33,7 @@ Tests: "tenantId": "123456789", "resultSignature": "None", "durationMs": 0, - "callerIpAddress": "1.2.3.4.5", + "callerIpAddress": "1.2.3.4", "correlationId": "123456789", "Level": 4, "properties": { From a18944358240c3e5e9b2eddcd824b258541abb84 Mon Sep 17 00:00:00 2001 From: Ariel Ropek <79653153+arielkr256@users.noreply.github.com> Date: Fri, 31 Jan 2025 09:04:22 -0700 Subject: [PATCH 12/23] Update rules/azure_entraid_rules/azure_mfa_disabled.yml Co-authored-by: ben-githubs <38414634+ben-githubs@users.noreply.github.com> --- rules/azure_entraid_rules/azure_mfa_disabled.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/azure_entraid_rules/azure_mfa_disabled.yml b/rules/azure_entraid_rules/azure_mfa_disabled.yml index 7bfe9f865..28c99ff37 100644 --- a/rules/azure_entraid_rules/azure_mfa_disabled.yml +++ b/rules/azure_entraid_rules/azure_mfa_disabled.yml @@ -97,7 +97,7 @@ Tests: "tenantId": "123456789", "resultSignature": "None", "durationMs": 0, - "callerIpAddress": "1.2.3.4.5", + "callerIpAddress": "1.2.3.4", "correlationId": "123456789", "Level": 4, "properties": { From 5379376174e643a66246c1d5cdadef98f7553019 Mon Sep 17 00:00:00 2001 From: Ariel Ropek <79653153+arielkr256@users.noreply.github.com> Date: Fri, 31 Jan 2025 09:04:48 -0700 Subject: [PATCH 13/23] Update rules/azure_entraid_rules/azure_mfa_disabled.yml Co-authored-by: ben-githubs <38414634+ben-githubs@users.noreply.github.com> --- rules/azure_entraid_rules/azure_mfa_disabled.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/azure_entraid_rules/azure_mfa_disabled.yml b/rules/azure_entraid_rules/azure_mfa_disabled.yml index 28c99ff37..95d40c9a2 100644 --- a/rules/azure_entraid_rules/azure_mfa_disabled.yml +++ b/rules/azure_entraid_rules/azure_mfa_disabled.yml @@ -161,7 +161,7 @@ Tests: "tenantId": "123456", "resultSignature": "None", "durationMs": 0, - "callerIpAddress": "1.2.3.4.5", + "callerIpAddress": "1.2.3.4", "correlationId": "123456", "Level": 4, "properties": { From 2da2ce6a1104f25b1752b7ac294fb2c70ab9f1c6 Mon Sep 17 00:00:00 2001 From: Ariel Ropek <79653153+arielkr256@users.noreply.github.com> Date: Fri, 31 Jan 2025 09:05:10 -0700 Subject: [PATCH 14/23] Update rules/azure_entraid_rules/azure_policy_changed.yml Co-authored-by: ben-githubs <38414634+ben-githubs@users.noreply.github.com> --- rules/azure_entraid_rules/azure_policy_changed.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/azure_entraid_rules/azure_policy_changed.yml b/rules/azure_entraid_rules/azure_policy_changed.yml index 446046490..d2fd722d3 100644 --- a/rules/azure_entraid_rules/azure_policy_changed.yml +++ b/rules/azure_entraid_rules/azure_policy_changed.yml @@ -33,7 +33,7 @@ Tests: "tenantId": "12341234", "resultSignature": "None", "durationMs": 0, - "callerIpAddress": "1.2.3.4.5", + "callerIpAddress": "1.2.3.4", "correlationId": "1324515", "Level": 4, "properties": { From cac72b477a86e59b63c8407253d2fd42274451a5 Mon Sep 17 00:00:00 2001 From: Ariel Ropek <79653153+arielkr256@users.noreply.github.com> Date: Fri, 31 Jan 2025 09:05:41 -0700 Subject: [PATCH 15/23] Update rules/azure_entraid_rules/azure_policy_changed.py Co-authored-by: ben-githubs <38414634+ben-githubs@users.noreply.github.com> --- rules/azure_entraid_rules/azure_policy_changed.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/azure_entraid_rules/azure_policy_changed.py b/rules/azure_entraid_rules/azure_policy_changed.py index 5199cedd8..c2626f8c5 100644 --- a/rules/azure_entraid_rules/azure_policy_changed.py +++ b/rules/azure_entraid_rules/azure_policy_changed.py @@ -19,7 +19,7 @@ def rule(event): def title(event): operation_name = event.get("operationName", default="") actor_name = event.deep_get( - "properties", "initiatedBy", "user", "userPrincipalName", default="" + "properties", "initiatedBy", "user", "userPrincipalName", default="" ) policy = event.deep_walk("properties", "targetResources", "displayName", default="") From b34e2df49a5d4593054e82d5386848936c68643a Mon Sep 17 00:00:00 2001 From: Ariel Ropek <79653153+arielkr256@users.noreply.github.com> Date: Fri, 31 Jan 2025 09:06:21 -0700 Subject: [PATCH 16/23] Update rules/azure_entraid_rules/azure_policy_changed.py Co-authored-by: ben-githubs <38414634+ben-githubs@users.noreply.github.com> --- rules/azure_entraid_rules/azure_policy_changed.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/azure_entraid_rules/azure_policy_changed.py b/rules/azure_entraid_rules/azure_policy_changed.py index c2626f8c5..24bc75030 100644 --- a/rules/azure_entraid_rules/azure_policy_changed.py +++ b/rules/azure_entraid_rules/azure_policy_changed.py @@ -21,7 +21,7 @@ def title(event): actor_name = event.deep_get( "properties", "initiatedBy", "user", "userPrincipalName", default="" ) - policy = event.deep_walk("properties", "targetResources", "displayName", default="") + policy = event.deep_walk("properties", "targetResources", "displayName", default="") return f"{operation_name} by {actor_name} on the policy {policy}" From 41f2eeaa1dc040be0325f267aad78bb34cdeb2bf Mon Sep 17 00:00:00 2001 From: Ariel Ropek <79653153+arielkr256@users.noreply.github.com> Date: Fri, 31 Jan 2025 09:06:37 -0700 Subject: [PATCH 17/23] Update rules/azure_entraid_rules/azure_policy_changed.py Co-authored-by: ben-githubs <38414634+ben-githubs@users.noreply.github.com> --- rules/azure_entraid_rules/azure_policy_changed.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/azure_entraid_rules/azure_policy_changed.py b/rules/azure_entraid_rules/azure_policy_changed.py index 24bc75030..f8a69cf4d 100644 --- a/rules/azure_entraid_rules/azure_policy_changed.py +++ b/rules/azure_entraid_rules/azure_policy_changed.py @@ -17,7 +17,7 @@ def rule(event): def title(event): - operation_name = event.get("operationName", default="") + operation_name = event.get("operationName", default="") actor_name = event.deep_get( "properties", "initiatedBy", "user", "userPrincipalName", default="" ) From 09ca4810937e93c2295def1500affa5a1acc2ebe Mon Sep 17 00:00:00 2001 From: Ariel Ropek <79653153+arielkr256@users.noreply.github.com> Date: Fri, 31 Jan 2025 09:07:07 -0700 Subject: [PATCH 18/23] Update rules/azure_entraid_rules/azure_policy_changed.yml Co-authored-by: ben-githubs <38414634+ben-githubs@users.noreply.github.com> --- rules/azure_entraid_rules/azure_policy_changed.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/azure_entraid_rules/azure_policy_changed.yml b/rules/azure_entraid_rules/azure_policy_changed.yml index d2fd722d3..fe27c5b3f 100644 --- a/rules/azure_entraid_rules/azure_policy_changed.yml +++ b/rules/azure_entraid_rules/azure_policy_changed.yml @@ -97,7 +97,7 @@ Tests: "tenantId": "12341235", "resultSignature": "None", "durationMs": 0, - "callerIpAddress": "1.2.3.4.5", + "callerIpAddress": "1.2.3.4", "correlationId": "`1244`12123", "Level": 4, "properties": { From d7066b6f82f661842d46e22db1b2eca5eac6b1a4 Mon Sep 17 00:00:00 2001 From: Ariel Ropek <79653153+arielkr256@users.noreply.github.com> Date: Fri, 31 Jan 2025 09:07:36 -0700 Subject: [PATCH 19/23] Update rules/azure_entraid_rules/azure_role_changed_pim.py Co-authored-by: ben-githubs <38414634+ben-githubs@users.noreply.github.com> --- rules/azure_entraid_rules/azure_role_changed_pim.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/azure_entraid_rules/azure_role_changed_pim.py b/rules/azure_entraid_rules/azure_role_changed_pim.py index 05bb6f938..890b7d992 100644 --- a/rules/azure_entraid_rules/azure_role_changed_pim.py +++ b/rules/azure_entraid_rules/azure_role_changed_pim.py @@ -12,7 +12,7 @@ def rule(event): def title(event): operation_name = event.get("operationName", default="") actor_name = event.deep_get( - "properties", "initiatedBy", "user", "userPrincipalName", default="" + "properties", "initiatedBy", "user", "userPrincipalName", default="" ) target_name = get_target_name(event) role = event.deep_walk( From 08e1839c054b37ba5fa6c4694d580980883f954f Mon Sep 17 00:00:00 2001 From: Ariel Ropek <79653153+arielkr256@users.noreply.github.com> Date: Fri, 31 Jan 2025 09:08:59 -0700 Subject: [PATCH 20/23] Update rules/azure_entraid_rules/azure_policy_changed.yml Co-authored-by: ben-githubs <38414634+ben-githubs@users.noreply.github.com> --- rules/azure_entraid_rules/azure_policy_changed.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/azure_entraid_rules/azure_policy_changed.yml b/rules/azure_entraid_rules/azure_policy_changed.yml index fe27c5b3f..00f52112a 100644 --- a/rules/azure_entraid_rules/azure_policy_changed.yml +++ b/rules/azure_entraid_rules/azure_policy_changed.yml @@ -98,7 +98,7 @@ Tests: "resultSignature": "None", "durationMs": 0, "callerIpAddress": "1.2.3.4", - "correlationId": "`1244`12123", + "correlationId": "124412123", "Level": 4, "properties": { "tenantId": "123515-5-1235", From 3fc314f468eade552a3d3f155c6638e7933c0a33 Mon Sep 17 00:00:00 2001 From: Ariel Ropek <79653153+arielkr256@users.noreply.github.com> Date: Fri, 31 Jan 2025 09:09:27 -0700 Subject: [PATCH 21/23] Update rules/azure_entraid_rules/azure_policy_changed.yml Co-authored-by: ben-githubs <38414634+ben-githubs@users.noreply.github.com> --- rules/azure_entraid_rules/azure_policy_changed.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/azure_entraid_rules/azure_policy_changed.yml b/rules/azure_entraid_rules/azure_policy_changed.yml index 00f52112a..dceef17b7 100644 --- a/rules/azure_entraid_rules/azure_policy_changed.yml +++ b/rules/azure_entraid_rules/azure_policy_changed.yml @@ -122,7 +122,7 @@ Tests: "id": "12345123", "displayName": null, "userPrincipalName": "Kratos@onmicrosoft.com", - "ipAddress": "1.2.3.4.5", + "ipAddress": "1.2.3.4", "roles": [] } }, From 2c47bf1e6ba298a459a37dcb32b0d088642eae65 Mon Sep 17 00:00:00 2001 From: Ariel Ropek <79653153+arielkr256@users.noreply.github.com> Date: Fri, 31 Jan 2025 09:10:04 -0700 Subject: [PATCH 22/23] Update rules/azure_entraid_rules/azure_policy_changed.yml Co-authored-by: ben-githubs <38414634+ben-githubs@users.noreply.github.com> --- rules/azure_entraid_rules/azure_policy_changed.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/azure_entraid_rules/azure_policy_changed.yml b/rules/azure_entraid_rules/azure_policy_changed.yml index dceef17b7..4cf9fcbf8 100644 --- a/rules/azure_entraid_rules/azure_policy_changed.yml +++ b/rules/azure_entraid_rules/azure_policy_changed.yml @@ -58,7 +58,7 @@ Tests: "id": "234526234", "displayName": null, "userPrincipalName": "Kratos@mtolympus.com", - "ipAddress": "1.2.3.4.5", + "ipAddress": "1.2.3.4", "roles": [] } }, From 24db394ef70ec640295026094ad424e4754eec54 Mon Sep 17 00:00:00 2001 From: Ariel Ropek <79653153+arielkr256@users.noreply.github.com> Date: Fri, 31 Jan 2025 09:10:56 -0700 Subject: [PATCH 23/23] Update rules/azure_entraid_rules/azure_policy_changed.yml Co-authored-by: ben-githubs <38414634+ben-githubs@users.noreply.github.com> --- rules/azure_entraid_rules/azure_policy_changed.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/azure_entraid_rules/azure_policy_changed.yml b/rules/azure_entraid_rules/azure_policy_changed.yml index 4cf9fcbf8..b5a5729c6 100644 --- a/rules/azure_entraid_rules/azure_policy_changed.yml +++ b/rules/azure_entraid_rules/azure_policy_changed.yml @@ -180,7 +180,7 @@ Tests: "id": "12345123", "displayName": null, "userPrincipalName": "Kratos@onmicrosoft.com", - "ipAddress": "1.2.3.4.5", + "ipAddress": "1.2.3.4", "roles": [] } },