diff --git a/correlation_rules/notion_account_changed_after_login.yml b/correlation_rules/notion_account_changed_after_login.yml new file mode 100644 index 000000000..3149b8d40 --- /dev/null +++ b/correlation_rules/notion_account_changed_after_login.yml @@ -0,0 +1,55 @@ +AnalysisType: correlation_rule +RuleID: "Notion.Login.FOLLOWED.BY.AccountChange" +DisplayName: "Notion Login FOLLOWED BY AccountChange" +Enabled: true +Severity: Medium +Description: A Notion User logged in then changed their account details. +Reference: https://www.notion.so/help/account-settings +Runbook: Possible account takeover. Follow up with the Notion User to determine if this email change is genuine. +Reports: + MITRE ATT&CK: + - TA0004:T1098 # Account Manipulation +Detection: + - Sequence: + - ID: Login + RuleID: Notion.Login + - ID: AccountChange + RuleID: Notion.AccountChange + Transitions: + - ID: Login FOLLOWED BY AccountChange + From: Login + To: AccountChange + WithinTimeFrameMinutes: 15 + Match: + - On: p_alert_context.actor_id + LookbackWindowMinutes: 1440 + Schedule: + RateMinutes: 1440 + TimeoutMinutes: 5 +Tests: + - Name: Login, Followed By AccountChange within short time + ExpectedResult: true + RuleOutputs: + - ID: Login + Matches: + p_alert_context.actor_id: + 'i-abcdef0123456789a': + - "2024-06-01T10:00:01Z" + - ID: AccountChange + Matches: + p_alert_context.actor_id: + 'i-abcdef0123456789a': + - "2024-06-01T10:01:01Z" + - Name: Login, Followed By AccountChange not within short time + ExpectedResult: false + RuleOutputs: + - ID: Login + Matches: + p_alert_context.actor_id: + 'i-abcdef0123456789a': + - "2024-06-01T10:00:01Z" + - ID: AccountChange + Matches: + p_alert_context.actor_id: + 'i-abcdef0123456789a': + - "2024-06-01T11:01:01Z" diff --git a/correlation_rules/onelogin_successful_login_after_high_risk_failed_login.yml b/correlation_rules/onelogin_successful_login_after_high_risk_failed_login.yml new file mode 100644 index 000000000..34aa2edac --- /dev/null +++ b/correlation_rules/onelogin_successful_login_after_high_risk_failed_login.yml @@ -0,0 +1,68 @@ +AnalysisType: correlation_rule +RuleID: "OneLogin.HighRiskFailedLogin.FOLLOWED.BY.SuccessfulLogin" +DisplayName: "OneLogin High Risk Failed Login FOLLOWED BY Successful Login" +Enabled: true +Severity: Medium +Description: A OneLogin user successfully logged in after a failed high-risk login attempt. +Reference: https://resources.onelogin.com/OneLogin_RiskBasedAuthentication-WP-v5.pdf +Runbook: Investigate whether this was caused by expected user activity. +Reports: + MITRE ATT&CK: + - TA0001:T1078 # Valid Accounts +Detection: + - Sequence: + - ID: HighRiskFailedLogin + RuleID: OneLogin.HighRiskFailedLogin + - ID: SuccessfulLogin + RuleID: OneLogin.Login + Transitions: + - ID: HighRiskFailedLogin FOLLOWED BY SuccessfulLogin + From: HighRiskFailedLogin + To: SuccessfulLogin + WithinTimeFrameMinutes: 15 + Match: + - On: user_name + LookbackWindowMinutes: 1440 + Schedule: + RateMinutes: 1440 + TimeoutMinutes: 5 +Tests: + - Name: High Risk Failed Login FOLLOWED BY Successful Login within short time + ExpectedResult: true + RuleOutputs: + - ID: HighRiskFailedLogin + Matches: + user_name: + 'Some_user': + - "2024-06-01T10:00:01Z" + - ID: SuccessfulLogin + Matches: + user_name: + 'Some_user': + - "2024-06-01T10:01:01Z" + - Name: High Risk Failed Login FOLLOWED BY Successful Login not within short time + ExpectedResult: false + RuleOutputs: + - ID: HighRiskFailedLogin + Matches: + user_name: + 'Some_user': + - "2024-06-01T10:00:01Z" + - ID: SuccessfulLogin + Matches: + user_name: + 'Some_user': + - "2024-06-01T11:01:01Z" + - Name: High Risk Failed Login FOLLOWED BY Successful Login of other user + ExpectedResult: false + RuleOutputs: + - ID: HighRiskFailedLogin + Matches: + user_name: + 'Some_user': + - "2024-06-01T10:00:01Z" + - ID: SuccessfulLogin + Matches: + user_name: + 'Some_other_user': + - "2024-06-01T10:01:01Z" diff --git a/packs/notion.yml b/packs/notion.yml index 8e26d0ca8..6af944201 100644 --- a/packs/notion.yml +++ b/packs/notion.yml @@ -3,7 +3,6 @@ PackID: PantherManaged.Notion Description: Group of all Notion detections PackDefinition: IDs: - - Notion.AccountChangedAfterLogin - Notion.Audit.Log.Exported - Notion.PagePerms.GuestPermsChanged - Notion.LoginFromNewLocation @@ -17,6 +16,11 @@ PackDefinition: - Notion.Workspace.Public.Page.Added - Notion.SharingSettingsUpdated - Notion.TeamspaceOwnerAdded + # Correlation Rules + - Notion.Login.FOLLOWED.BY.AccountChange + # Signal Rules + - Notion.Login + - Notion.AccountChange # Globals used in these detections - global_filter_notion - panther_base_helpers diff --git a/packs/onelogin.yml b/packs/onelogin.yml index cf985947d..7fc9f0c64 100644 --- a/packs/onelogin.yml +++ b/packs/onelogin.yml @@ -5,7 +5,6 @@ PackDefinition: IDs: - OneLogin.ActiveLoginActivity - OneLogin.HighRiskFailedLogin - - OneLogin.HighRiskLogin - OneLogin.PasswordAccess - OneLogin.PasswordChanged - OneLogin.AuthFactorRemoved @@ -14,6 +13,10 @@ PackDefinition: - OneLogin.UnauthorizedAccess - OneLogin.UserAccountLocked - OneLogin.UserAssumption + # Correlation Rules + - OneLogin.HighRiskFailedLogin.FOLLOWED.BY.SuccessfulLogin + # Signal Rules + - OneLogin.Login # Globals used in these detections - panther_base_helpers - panther_oss_helpers diff --git a/rules/notion_rules/notion_account_changed.py b/rules/notion_rules/notion_account_changed.py new file mode 100644 index 000000000..305fc226d --- /dev/null +++ b/rules/notion_rules/notion_account_changed.py @@ -0,0 +1,35 @@ +from global_filter_notion import filter_include_event +from panther_notion_helpers import notion_alert_context + + +def rule(event): + if not filter_include_event(event): + return False + + allowed_event_types = { + "user.settings.login_method.email_updated", + "user.settings.login_method.password_updated", + "user.settings.login_method.password_added", + "user.settings.login_method.password_removed", + } + if event.deep_walk("event", "type") in allowed_event_types: + return True + return False + + +def title(event): + user_email = event.deep_walk("event", "actor", "person", "email", default="UNKNOWN EMAIL") + action_taken = { + "user.settings.login_method.email_updated": "changed their email", + "user.settings.login_method.password_updated": "changed their password", + "user.settings.login_method.password_added": "added a password to their account", + "user.settings.login_method.password_removed": "removed the password from their account", + }.get(event.deep_get("event", "type"), "altered their account info") + return f"Notion User [{user_email}] {action_taken}." + + +def alert_context(event): + context = notion_alert_context(event) + context["login_timestamp"] = event.get("p_event_time") + context["actor_id"] = event.deep_walk("event", "actor", "id") + return context diff --git a/rules/notion_rules/notion_account_changed.yml b/rules/notion_rules/notion_account_changed.yml new file mode 100644 index 000000000..38bf175d6 --- /dev/null +++ b/rules/notion_rules/notion_account_changed.yml @@ -0,0 +1,76 @@ +AnalysisType: rule +Filename: notion_account_changed.py +RuleID: "Notion.AccountChange" +DisplayName: "Signal - Notion Account Changed" +Enabled: true +CreateAlert: false +LogTypes: + - Notion.AuditLogs +Tags: + - Notion + - Identity & Access Management + - Persistence +Severity: Info +Description: A Notion User changed their account information. +DedupPeriodMinutes: 60 +Threshold: 1 +Reference: https://www.notion.so/help/account-settings +Tests: + - Name: Login event + ExpectedResult: false + Log: + { + "event": + { + "actor": + { + "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "object": "user", + "person": { "email": "aragorn.elessar@lotr.com" }, + "type": "person", + }, + "details": { "authType": "email" }, + "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "ip_address": "192.168.100.100", + "platform": "web", + "timestamp": "2023-06-12 21:40:28.690000000", + "type": "user.login", + "workspace_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + }, + "p_event_time": "2023-06-12 21:40:28.690000000", + "p_log_type": "Notion.AuditLogs", + "p_parse_time": "2023-06-12 22:53:51.602223297", + "p_row_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "p_schema_version": 0, + "p_source_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "p_source_label": "Notion Logs", + } + - Name: Email Changed + ExpectedResult: true + Log: + { + "event": + { + "actor": + { + "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "object": "user", + "person": { "email": "aragorn.elessar@lotr.com" }, + "type": "person", + }, + "details": { "authType": "email" }, + "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "ip_address": "192.168.100.100", + "platform": "web", + "timestamp": "2023-06-12 21:40:28.690000000", + "type": "user.settings.login_method.email_updated", + "workspace_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + }, + "p_event_time": "2023-06-12 21:40:28.690000000", + "p_log_type": "Notion.AuditLogs", + "p_parse_time": "2023-06-12 22:53:51.602223297", + "p_row_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "p_schema_version": 0, + "p_source_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "p_source_label": "Notion Logs", + } diff --git a/rules/notion_rules/notion_account_changed_after_login.yml b/rules/notion_rules/notion_account_changed_after_login.yml index c58f08882..f96a3ae38 100644 --- a/rules/notion_rules/notion_account_changed_after_login.yml +++ b/rules/notion_rules/notion_account_changed_after_login.yml @@ -1,7 +1,7 @@ AnalysisType: rule Filename: notion_account_changed_after_login.py RuleID: "Notion.AccountChangedAfterLogin" -DisplayName: "Notion Account Changed Shortly After Login" +DisplayName: "DEPRECATED - Notion Account Changed Shortly After Login" Enabled: true LogTypes: - Notion.AuditLogs @@ -9,6 +9,7 @@ Tags: - Notion - Identity & Access Management - Persistence + - DEPRECATED Severity: Medium Description: A Notion User logged in then changed their account details. DedupPeriodMinutes: 60 diff --git a/rules/notion_rules/notion_login.py b/rules/notion_rules/notion_login.py new file mode 100644 index 000000000..ca4e4fd65 --- /dev/null +++ b/rules/notion_rules/notion_login.py @@ -0,0 +1,23 @@ +from global_filter_notion import filter_include_event +from panther_notion_helpers import notion_alert_context + + +def rule(event): + if not filter_include_event(event): + return False + + if event.deep_walk("event", "type") == "user.login": + return True + return False + + +def title(event): + user_email = event.deep_walk("event", "actor", "person", "email", default="UNKNOWN EMAIL") + return f"Notion User [{user_email}] logged in." + + +def alert_context(event): + context = notion_alert_context(event) + context["login_timestamp"] = event.get("p_event_time") + context["actor_id"] = event.deep_walk("event", "actor", "id") + return context diff --git a/rules/notion_rules/notion_login.yml b/rules/notion_rules/notion_login.yml new file mode 100644 index 000000000..d34cbc877 --- /dev/null +++ b/rules/notion_rules/notion_login.yml @@ -0,0 +1,75 @@ +AnalysisType: rule +Filename: notion_login.py +RuleID: "Notion.Login" +DisplayName: "Signal - Notion Login" +Enabled: true +CreateAlert: false +LogTypes: + - Notion.AuditLogs +Tags: + - Notion + - Identity & Access Management +Severity: Info +Description: A Notion User logged in. +DedupPeriodMinutes: 60 +Threshold: 1 +Reference: https://www.notion.so/help/account-settings +Tests: + - Name: Login event + ExpectedResult: true + Log: + { + "event": + { + "actor": + { + "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "object": "user", + "person": { "email": "aragorn.elessar@lotr.com" }, + "type": "person", + }, + "details": { "authType": "email" }, + "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "ip_address": "192.168.100.100", + "platform": "web", + "timestamp": "2023-06-12 21:40:28.690000000", + "type": "user.login", + "workspace_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + }, + "p_event_time": "2023-06-12 21:40:28.690000000", + "p_log_type": "Notion.AuditLogs", + "p_parse_time": "2023-06-12 22:53:51.602223297", + "p_row_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "p_schema_version": 0, + "p_source_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "p_source_label": "Notion Logs", + } + - Name: Not login event + ExpectedResult: false + Log: + { + "event": + { + "actor": + { + "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "object": "user", + "person": { "email": "aragorn.elessar@lotr.com" }, + "type": "person", + }, + "details": { "authType": "email" }, + "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "ip_address": "192.168.100.100", + "platform": "web", + "timestamp": "2023-06-12 21:40:28.690000000", + "type": "user.settings.login_method.email_updated", + "workspace_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + }, + "p_event_time": "2023-06-12 21:40:28.690000000", + "p_log_type": "Notion.AuditLogs", + "p_parse_time": "2023-06-12 22:53:51.602223297", + "p_row_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "p_schema_version": 0, + "p_source_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "p_source_label": "Notion Logs", + } diff --git a/rules/onelogin_rules/onelogin_high_risk_login.yml b/rules/onelogin_rules/onelogin_high_risk_login.yml index dcae68313..5abbd0c11 100644 --- a/rules/onelogin_rules/onelogin_high_risk_login.yml +++ b/rules/onelogin_rules/onelogin_high_risk_login.yml @@ -1,12 +1,13 @@ AnalysisType: rule Filename: onelogin_high_risk_login.py RuleID: "OneLogin.HighRiskLogin" -DisplayName: "OneLogin High Risk Login" +DisplayName: "DEPRECATED - OneLogin High Risk Login" Enabled: true LogTypes: - OneLogin.Events Tags: - OneLogin + - DEPRECATED Severity: Medium Description: A OneLogin user successfully logged in after a failed high-risk login attempt. Reference: https://resources.onelogin.com/OneLogin_RiskBasedAuthentication-WP-v5.pdf diff --git a/rules/onelogin_rules/onelogin_login.py b/rules/onelogin_rules/onelogin_login.py new file mode 100644 index 000000000..f8ac12f62 --- /dev/null +++ b/rules/onelogin_rules/onelogin_login.py @@ -0,0 +1,8 @@ +def rule(event): + if str(event.get("event_type_id")) == "5": + return True + return False + + +def title(event): + return f"A user [{event.get('user_name', '')}] successfully logged in" diff --git a/rules/onelogin_rules/onelogin_login.yml b/rules/onelogin_rules/onelogin_login.yml new file mode 100644 index 000000000..31a6f2875 --- /dev/null +++ b/rules/onelogin_rules/onelogin_login.yml @@ -0,0 +1,34 @@ +AnalysisType: rule +Filename: onelogin_login.py +RuleID: "OneLogin.Login" +DisplayName: "Signal - OneLogin Login" +Enabled: true +CreateAlert: false +LogTypes: + - OneLogin.Events +Tags: + - OneLogin +Severity: Info +Description: A OneLogin user successfully logged in. +Reference: https://resources.onelogin.com/OneLogin_RiskBasedAuthentication-WP-v5.pdf +Tests: + - Name: Successful Login Event + ExpectedResult: true + Log: + { + "event_type_id": "5", + "actor_user_id": 123456, + "actor_user_name": "Bob Cat", + "user_id": 123456, + "user_name": "Bob Cat", + } + - Name: Failed Login Event + ExpectedResult: false + Log: + { + "event_type_id": "6", + "actor_user_id": 123456, + "actor_user_name": "Bob Cat", + "user_id": 123456, + "user_name": "Bob Cat", + }