From d3f75adf710980dfbf360a224a43f771aadba6f9 Mon Sep 17 00:00:00 2001 From: akozlovets098 Date: Tue, 10 Sep 2024 13:00:15 +0300 Subject: [PATCH 1/3] THREAT-354 Converting caching rules to correlation --- .../notion_account_changed_after_login.yml | 54 +++++++++++++ ...ful_login_after_high_risk_failed_login.yml | 67 ++++++++++++++++ packs/notion.yml | 6 +- packs/onelogin.yml | 5 +- rules/notion_rules/notion_account_changed.py | 35 +++++++++ rules/notion_rules/notion_account_changed.yml | 76 +++++++++++++++++++ .../notion_account_changed_after_login.yml | 3 +- rules/notion_rules/notion_login.py | 23 ++++++ rules/notion_rules/notion_login.yml | 75 ++++++++++++++++++ .../onelogin_high_risk_login.yml | 3 +- rules/onelogin_rules/onelogin_login.py | 8 ++ rules/onelogin_rules/onelogin_login.yml | 34 +++++++++ 12 files changed, 385 insertions(+), 4 deletions(-) create mode 100644 correlation_rules/notion_account_changed_after_login.yml create mode 100644 correlation_rules/onelogin_successful_login_after_high_risk_failed_login.yml create mode 100644 rules/notion_rules/notion_account_changed.py create mode 100644 rules/notion_rules/notion_account_changed.yml create mode 100644 rules/notion_rules/notion_login.py create mode 100644 rules/notion_rules/notion_login.yml create mode 100644 rules/onelogin_rules/onelogin_login.py create mode 100644 rules/onelogin_rules/onelogin_login.yml 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..0df56f294 --- /dev/null +++ b/correlation_rules/notion_account_changed_after_login.yml @@ -0,0 +1,54 @@ +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 + Match: + - On: p_alert_context.actor_id + LookbackWindowMinutes: 15 + Schedule: + RateMinutes: 60 + 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..3212b0800 --- /dev/null +++ b/correlation_rules/onelogin_successful_login_after_high_risk_failed_login.yml @@ -0,0 +1,67 @@ +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 + Match: + - On: user_name + LookbackWindowMinutes: 15 + Schedule: + RateMinutes: 60 + 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", + } From 657bf46da2f5d6046d12c079955a99af8cfd99e8 Mon Sep 17 00:00:00 2001 From: akozlovets098 Date: Tue, 10 Sep 2024 13:33:59 +0300 Subject: [PATCH 2/3] THREAT-354 Converting caching rules to correlation - fixed timeframes --- correlation_rules/notion_account_changed_after_login.yml | 3 ++- .../onelogin_successful_login_after_high_risk_failed_login.yml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/correlation_rules/notion_account_changed_after_login.yml b/correlation_rules/notion_account_changed_after_login.yml index 0df56f294..3c89e12ee 100644 --- a/correlation_rules/notion_account_changed_after_login.yml +++ b/correlation_rules/notion_account_changed_after_login.yml @@ -19,9 +19,10 @@ Detection: - ID: Login FOLLOWED BY AccountChange From: Login To: AccountChange + WithinTimeFrameMinutes: 15 Match: - On: p_alert_context.actor_id - LookbackWindowMinutes: 15 + LookbackWindowMinutes: 1440 Schedule: RateMinutes: 60 TimeoutMinutes: 5 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 index 3212b0800..8fd24202d 100644 --- a/correlation_rules/onelogin_successful_login_after_high_risk_failed_login.yml +++ b/correlation_rules/onelogin_successful_login_after_high_risk_failed_login.yml @@ -19,9 +19,10 @@ Detection: - ID: HighRiskFailedLogin FOLLOWED BY SuccessfulLogin From: HighRiskFailedLogin To: SuccessfulLogin + WithinTimeFrameMinutes: 15 Match: - On: user_name - LookbackWindowMinutes: 15 + LookbackWindowMinutes: 1440 Schedule: RateMinutes: 60 TimeoutMinutes: 5 From 00eab4a583178f01857c0a56f37dbb8b99de1b47 Mon Sep 17 00:00:00 2001 From: akozlovets098 Date: Thu, 12 Sep 2024 15:40:26 +0300 Subject: [PATCH 3/3] THREAT-354 Converting caching rules to correlation - fixed timeframes --- correlation_rules/notion_account_changed_after_login.yml | 2 +- .../onelogin_successful_login_after_high_risk_failed_login.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/correlation_rules/notion_account_changed_after_login.yml b/correlation_rules/notion_account_changed_after_login.yml index 3c89e12ee..3149b8d40 100644 --- a/correlation_rules/notion_account_changed_after_login.yml +++ b/correlation_rules/notion_account_changed_after_login.yml @@ -24,7 +24,7 @@ Detection: - On: p_alert_context.actor_id LookbackWindowMinutes: 1440 Schedule: - RateMinutes: 60 + RateMinutes: 1440 TimeoutMinutes: 5 Tests: - Name: Login, Followed By AccountChange within short time 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 index 8fd24202d..34aa2edac 100644 --- a/correlation_rules/onelogin_successful_login_after_high_risk_failed_login.yml +++ b/correlation_rules/onelogin_successful_login_after_high_risk_failed_login.yml @@ -24,7 +24,7 @@ Detection: - On: user_name LookbackWindowMinutes: 1440 Schedule: - RateMinutes: 60 + RateMinutes: 1440 TimeoutMinutes: 5 Tests: - Name: High Risk Failed Login FOLLOWED BY Successful Login within short time