Detection rules › Panther
Panther rules: mongodb
MongoDB 2FA Disabled
#2FA was disabled.
Detection logic
from panther_mongodb_helpers import mongodb_alert_context
def rule(event):
return event.get("eventTypeName", "") == "ORG_TWO_FACTOR_AUTH_OPTIONAL"
def title(event):
user = event.get("username", "<USER_NOT_FOUND>")
return f"MongoDB Atlas: [{user}] has disabled 2FA"
def alert_context(event):
return mongodb_alert_context(event)
Rule specification
AnalysisType: rule
Description: "2FA was disabled."
DisplayName: "MongoDB 2FA Disabled"
Enabled: true
Filename: mongodb_2fa_disabled.py
Severity: Medium
Reference: https://www.mongodb.com/docs/atlas/security-multi-factor-authentication/
DedupPeriodMinutes: 60
LogTypes:
- MongoDB.OrganizationEvent
RuleID: "MongoDB.2FA.Disabled"
Threshold: 1
Stages and Predicates
Fires on MongoDB.OrganizationEvent events when the condition below holds.
Condition
eventTypeNameisORG_TWO_FACTOR_AUTH_OPTIONAL
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
eventTypeName | eq |
| field:"eventTypeName" kind:eq value:"ORG_TWO_FACTOR_AUTH_OPTIONAL" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
username | |
target_username | targetUsername |
org_id | orgId |
remote_address | remoteAddress |
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"created": "2023-06-07 16:57:55",
"currentValue": {},
"eventTypeName": "ORG_TWO_FACTOR_AUTH_OPTIONAL",
"id": "6480b7139bd8a012345ABCDE",
"isGlobalAdmin": false,
"links": [
{
"href": "https://cloud.mongodb.com/api/atlas/v1.0/orgs/12345xyzlmnce4f17d6e8e130/events/6480b7139bd8a012345ABCDE",
"rel": "self"
}
],
"orgId": "12345xyzlmnce4f17d6e8e130",
"p_event_time": "2023-06-07 16:57:55",
"p_log_type": "MongoDB.OrganizationEvent",
"p_parse_time": "2023-06-07 17:04:42.59",
"p_row_id": "ea276b16216684d9e198c0d0188a3d",
"p_schema_version": 0,
"p_source_id": "7c3cb124-9c30-492c-99e6-46518c232d73",
"p_source_label": "MongoDB",
"remoteAddress": "1.2.3.4",
"targetUsername": "outsider@other.com",
"userId": "647f654f93bebc69123abc1",
"username": "user@company.com"
}
MongoDB access allowed from anywhere
#Atlas only allows client connections to the database deployment from entries in the project's IP access list. This rule detects when 0.0.0.0/0 is added to that list, which allows access from anywhere.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Lateral Movement |
Detection logic
from panther_mongodb_helpers import mongodb_alert_context
def rule(event):
if (
event.get("eventTypeName", "") == "NETWORK_PERMISSION_ENTRY_ADDED"
and event.get("whitelistEntry", "") == "0.0.0.0/0"
):
return True
return False
def title(event):
user = event.get("username", "<USER_NOT_FOUND>")
group_id = event.get("groupId", "<GROUP_NOT_FOUND>")
return f"MongoDB: [{user}] has allowed access to group [{group_id}] from anywhere"
def alert_context(event):
context = mongodb_alert_context(event)
context["groupId"] = event.get("groupId", "<GROUP_NOT_FOUND>")
return context
Rule specification
AnalysisType: rule
Description: Atlas only allows client connections to the database deployment from entries in the project's IP access list. This rule detects when 0.0.0.0/0 is added to that list, which allows access from anywhere.
DisplayName: "MongoDB access allowed from anywhere"
Enabled: true
LogTypes:
- MongoDB.ProjectEvent
RuleID: "MongoDB.Access.Allowed.From.Anywhere"
Filename: mongodb_access_allowed_from_anywhere.py
Severity: High
Tags:
- MongoDB
- Persistence
- Remote Services
- Modify Authentication Process - Conditional Access Policies
Reports:
MITRE ATT&CK:
- TA0003:T1556.009 # Modify Authentication Process - Conditional Access Policies
- TA0008:T1021.007 # Remote Services
Reference: https://www.mongodb.com/docs/atlas/security/ip-access-list/
Runbook: Check if this activity was legitimate. If not, delete 0.0.0.0/0 from the list of allowed ips.
DedupPeriodMinutes: 60
Threshold: 1
Stages and Predicates
Fires on MongoDB.ProjectEvent events when all of the conditions below hold.
Condition
eventTypeNameisNETWORK_PERMISSION_ENTRY_ADDEDwhitelistEntryis0.0.0.0/0
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
eventTypeName | eq |
| field:"eventTypeName" kind:eq value:"NETWORK_PERMISSION_ENTRY_ADDED" |
whitelistEntry | eq |
| field:"whitelistEntry" kind:eq value:"0.0.0.0/0" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
username | |
target_username | targetUsername |
org_id | orgId |
remote_address | remoteAddress |
groupId |
Response runbook
Check if this activity was legitimate. If not, delete 0.0.0.0/0 from the list of allowed ips.
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"created": "2024-04-03 11:13:04.000000000",
"currentValue": {},
"eventTypeName": "NETWORK_PERMISSION_ENTRY_ADDED",
"groupId": "some_group_id",
"id": "123abc",
"isGlobalAdmin": false,
"remoteAddress": "1.2.3.4",
"userId": "123abc",
"username": "some_user@company.com",
"whitelistEntry": "0.0.0.0/0"
}
MongoDB Atlas API Key Created
#A MongoDB Atlas api key's access list was updated
Detection logic
from panther_mongodb_helpers import mongodb_alert_context
def rule(event):
return event.get("eventTypeName", "") == "API_KEY_ACCESS_LIST_ENTRY_ADDED"
def title(event):
user = event.get("username", "<USER_NOT_FOUND>")
public_key = event.get("targetPublicKey", "<PUBLIC_KEY_NOT_FOUND>")
return f"MongoDB Atlas: [{user}] updated the allowed access list for API Key [{public_key}]"
def alert_context(event):
context = mongodb_alert_context(event)
links = event.deep_walk("links", "href", return_val="first", default="<LINKS_NOT_FOUND>")
extra_context = {
"links": links,
"event_type_name": event.get("eventTypeName", "<EVENT_TYPE_NOT_FOUND>"),
"target_public_key": event.get("targetPublicKey", "<PUBLIC_KEY_NOT_FOUND>"),
}
context.update(extra_context)
return context
Rule specification
AnalysisType: rule
Description: A MongoDB Atlas api key's access list was updated
DisplayName: "MongoDB Atlas API Key Created"
Enabled: true
Filename: mongodb_atlas_api_key_created.py
Severity: Medium
Reference: https://www.mongodb.com/docs/atlas/configure-api-access/#std-label-about-org-api-keys
DedupPeriodMinutes: 60
LogTypes:
- MongoDB.OrganizationEvent
RuleID: "MongoDB.Atlas.ApiKeyCreated"
Threshold: 1
Stages and Predicates
Fires on MongoDB.OrganizationEvent events when the condition below holds.
Condition
eventTypeNameisAPI_KEY_ACCESS_LIST_ENTRY_ADDED
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
eventTypeName | eq |
| field:"eventTypeName" kind:eq value:"API_KEY_ACCESS_LIST_ENTRY_ADDED" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
username | |
target_username | targetUsername |
org_id | orgId |
remote_address | remoteAddress |
targetPublicKey |
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"created": "2023-06-14 15:47:15",
"currentvalue": {},
"eventtypename": "API_KEY_ACCESS_LIST_ENTRY_ADDED",
"id": "1234abcd13f2804962409423",
"isglobaladmin": false,
"links": [
{
"href": "https://cloud.mongodb.com/api/atlas/v1.0/orgs/9876xyz123lmnop0/events/1234abcd13f2804962409423",
"rel": "self"
}
],
"orgid": "9876xyz123lmnop0",
"p_event_time": "2023-06-14 15:47:15",
"p_log_type": "MongoDB.OrganizationEvent",
"p_parse_time": "2023-06-14 15:53:42.415",
"p_row_id": "5ad9c2df49e19aac98def9e118e236",
"p_schema_version": 0,
"p_source_id": "a2e8f928-c6e5-4110-b6f9-1b741176041d",
"p_source_label": "mongo-test-2",
"remoteaddress": "1.2.3.4",
"targetpublickey": "xfvcfwtt",
"userid": "abcd1234userid988",
"username": "user@company.com",
"whitelistentry": "1.2.3.4"
}
MongoDB External User Invited
#An external user has been invited to a MongoDB org.
Detection logic
import json
from unittest.mock import MagicMock
from panther_mongodb_helpers import mongodb_alert_context
# Set domains allowed to join the organization ie. company.com
ALLOWED_DOMAINS = []
def rule(event):
global ALLOWED_DOMAINS # pylint: disable=global-statement
if isinstance(ALLOWED_DOMAINS, MagicMock):
ALLOWED_DOMAINS = json.loads(ALLOWED_DOMAINS()) # pylint: disable=not-callable
if event.get("eventTypeName", "") == "INVITED_TO_ORG":
target_user = event.get("targetUsername", "")
target_domain = target_user.split("@")[-1]
return target_domain not in ALLOWED_DOMAINS
return False
def title(event):
actor = event.get("username", "<USER_NOT_FOUND>")
target = event.get("targetUsername", "<USER_NOT_FOUND>")
org_id = event.get("orgId", "<ORG_NOT_FOUND>")
return f"MongoDB Atlas: [{actor}] invited external user [{target}] to the org [{org_id}]"
def alert_context(event):
return mongodb_alert_context(event)
Rule specification
AnalysisType: rule
Description: "An external user has been invited to a MongoDB org. "
DisplayName: "MongoDB External User Invited"
Enabled: true
Filename: mongodb_external_user_invited.py
Severity: Medium
Reference: https://www.mongodb.com/docs/v4.2/tutorial/create-users/
Tags:
- Configuration Required
DedupPeriodMinutes: 60
LogTypes:
- MongoDB.OrganizationEvent
RuleID: "MongoDB.External.UserInvited"
Threshold: 1
Stages and Predicates
Fires on MongoDB.OrganizationEvent events when the condition below holds.
Condition
eventTypeNameisINVITED_TO_ORG
This rule also runs imperative logic the parser cannot express as a filter. The conditions above are the structured part it could extract.
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
eventTypeName | eq |
| field:"eventTypeName" kind:eq value:"INVITED_TO_ORG" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
username | |
target_username | targetUsername |
org_id | orgId |
remote_address | remoteAddress |
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"created": "2023-06-07 16:57:55",
"currentValue": {},
"eventTypeName": "INVITED_TO_ORG",
"id": "6480b7139bd8a012345ABCDE",
"isGlobalAdmin": false,
"links": [
{
"href": "https://cloud.mongodb.com/api/atlas/v1.0/orgs/12345xyzlmnce4f17d6e8e130/events/6480b7139bd8a012345ABCDE",
"rel": "self"
}
],
"orgId": "12345xyzlmnce4f17d6e8e130",
"p_event_time": "2023-06-07 16:57:55",
"p_log_type": "MongoDB.OrganizationEvent",
"p_parse_time": "2023-06-07 17:04:42.59",
"p_row_id": "ea276b16216684d9e198c0d0188a3d",
"p_schema_version": 0,
"p_source_id": "7c3cb124-9c30-492c-99e6-46518c232d73",
"p_source_label": "MongoDB",
"remoteAddress": "1.2.3.4",
"targetUsername": "outsider@other.com",
"userId": "647f654f93bebc69123abc1",
"username": "user@company.com"
}
MongoDB External User Invited (no config)
#An external user has been invited to a MongoDB org (no config).
Detection logic
from panther_mongodb_helpers import mongodb_alert_context
def rule(event):
if event.get("eventTypeName", "") != "INVITED_TO_ORG":
return False
user_who_sent_an_invitation = event.get("username", "")
user_who_was_invited = event.get("targetUsername", "")
domain = user_who_sent_an_invitation.split("@")[-1]
email_domains_are_different = not user_who_was_invited.endswith(domain)
return email_domains_are_different
def title(event):
actor = event.get("username", "<USER_NOT_FOUND>")
target = event.get("targetUsername", "<USER_NOT_FOUND>")
org_id = event.get("orgId", "<ORG_NOT_FOUND>")
return f"MongoDB Atlas: [{actor}] invited external user [{target}] to the org [{org_id}]"
def alert_context(event):
return mongodb_alert_context(event)
Rule specification
AnalysisType: rule
Description: "An external user has been invited to a MongoDB org (no config)."
DisplayName: "MongoDB External User Invited (no config)"
Enabled: true
Filename: mongodb_external_user_invited_no_config.py
Severity: High
Reference: https://www.mongodb.com/docs/v4.2/tutorial/create-users/
DedupPeriodMinutes: 60
LogTypes:
- MongoDB.OrganizationEvent
RuleID: "MongoDB.External.UserInvited.NoConfig"
Threshold: 1
Stages and Predicates
Fires on MongoDB.OrganizationEvent events when the condition below holds.
Condition
eventTypeNameisINVITED_TO_ORG
This rule also runs imperative logic the parser cannot express as a filter. The conditions above are the structured part it could extract.
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
eventTypeName | eq |
| field:"eventTypeName" kind:eq value:"INVITED_TO_ORG" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
username | |
target_username | targetUsername |
org_id | orgId |
remote_address | remoteAddress |
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"created": "2023-06-07 16:57:55",
"currentValue": {},
"eventTypeName": "INVITED_TO_ORG",
"id": "6480b7139bd8a012345ABCDE",
"isGlobalAdmin": false,
"links": [
{
"href": "https://cloud.mongodb.com/api/atlas/v1.0/orgs/12345xyzlmnce4f17d6e8e130/events/6480b7139bd8a012345ABCDE",
"rel": "self"
}
],
"orgId": "12345xyzlmnce4f17d6e8e130",
"p_event_time": "2023-06-07 16:57:55",
"p_log_type": "MongoDB.OrganizationEvent",
"p_parse_time": "2023-06-07 17:04:42.59",
"p_row_id": "ea276b16216684d9e198c0d0188a3d",
"p_schema_version": 0,
"p_source_id": "7c3cb124-9c30-492c-99e6-46518c232d73",
"p_source_label": "MongoDB",
"remoteAddress": "1.2.3.4",
"targetUsername": "outsider@other.com",
"userId": "647f654f93bebc69123abc1",
"username": "user@company.com"
}
MongoDB Identity Provider Activity
#Changes to identity provider settings are privileged activities that should be carefully audited. Attackers may add or change IDP integrations to gain persistence to environments
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Defense Impairment | |
| Credential Access |
Detection logic
from panther_mongodb_helpers import mongodb_alert_context
def rule(event):
important_event_types = {
"FEDERATION_SETTINGS_CREATED",
"FEDERATION_SETTINGS_DELETED",
"FEDERATION_SETTINGS_UPDATED",
"IDENTITY_PROVIDER_CREATED",
"IDENTITY_PROVIDER_UPDATED",
"IDENTITY_PROVIDER_DELETED",
"IDENTITY_PROVIDER_ACTIVATED",
"IDENTITY_PROVIDER_DEACTIVATED",
"IDENTITY_PROVIDER_JWKS_REVOKED",
"OIDC_IDENTITY_PROVIDER_UPDATED",
"OIDC_IDENTITY_PROVIDER_ENABLED",
"OIDC_IDENTITY_PROVIDER_DISABLED",
}
return event.get("eventTypeName") in important_event_types
def title(event):
target_username = event.get("targetUsername", "<USER_NOT_FOUND>")
org_id = event.get("orgId", "<ORG_NOT_FOUND>")
return f"MongoDB Atlas: User [{target_username}] roles changed in org [{org_id}]"
def alert_context(event):
return mongodb_alert_context(event)
Rule specification
AnalysisType: rule
Description: "Changes to identity provider settings are privileged activities that should be carefully audited. Attackers may add or change IDP integrations to gain persistence to environments"
DisplayName: "MongoDB Identity Provider Activity"
Enabled: true
Filename: mongodb_identity_provider_activity.py
Severity: Medium
Reference: https://attack.mitre.org/techniques/T1556/007/
DedupPeriodMinutes: 60
LogTypes:
- MongoDB.OrganizationEvent
RuleID: "MongoDB.Identity.Provider.Activity"
Threshold: 1
Stages and Predicates
Fires on MongoDB.OrganizationEvent events when the condition below holds.
Condition
eventTypeNameis one ofFEDERATION_SETTINGS_CREATED,FEDERATION_SETTINGS_DELETED,FEDERATION_SETTINGS_UPDATED,IDENTITY_PROVIDER_CREATED,IDENTITY_PROVIDER_UPDATED
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
eventTypeName | in |
| field:"eventTypeName" kind:in |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
username | |
target_username | targetUsername |
org_id | orgId |
remote_address | remoteAddress |
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"eventTypeName": "FEDERATION_SETTINGS_CREATED"
}
MongoDB logging toggled
#MongoDB logging toggled
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Defense Impairment |
Detection logic
from panther_mongodb_helpers import mongodb_alert_context
def rule(event):
return event.get("eventTypeName", "") == "AUDIT_LOG_CONFIGURATION_UPDATED"
def title(event):
user = event.get("username", "<USER_NOT_FOUND>")
return f"MongoDB: [{user}] has changed logging configuration."
def alert_context(event):
return mongodb_alert_context(event)
Rule specification
AnalysisType: rule
Description: "MongoDB logging toggled"
DisplayName: "MongoDB logging toggled"
Enabled: true
Filename: mongodb_logging_toggled.py
Severity: Low
Reference: https://attack.mitre.org/techniques/T1562/008/
DedupPeriodMinutes: 60
LogTypes:
- MongoDB.ProjectEvent
RuleID: "MongoDB.Logging.Toggled"
Threshold: 1
Stages and Predicates
Fires on MongoDB.ProjectEvent events when the condition below holds.
Condition
eventTypeNameisAUDIT_LOG_CONFIGURATION_UPDATED
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
eventTypeName | eq |
| field:"eventTypeName" kind:eq value:"AUDIT_LOG_CONFIGURATION_UPDATED" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
username | |
target_username | targetUsername |
org_id | orgId |
remote_address | remoteAddress |
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"created": "2023-06-07 16:57:55",
"currentValue": {},
"eventTypeName": "AUDIT_LOG_CONFIGURATION_UPDATED",
"id": "6480b7139bd8a012345ABCDE",
"isGlobalAdmin": false,
"links": [
{
"href": "https://cloud.mongodb.com/api/atlas/v1.0/orgs/12345xyzlmnce4f17d6e8e130/events/6480b7139bd8a012345ABCDE",
"rel": "self"
}
],
"orgId": "12345xyzlmnce4f17d6e8e130",
"p_event_time": "2023-06-07 16:57:55",
"p_log_type": "MongoDB.OrganizationEvent",
"p_parse_time": "2023-06-07 17:04:42.59",
"p_row_id": "ea276b16216684d9e198c0d0188a3d",
"p_schema_version": 0,
"p_source_id": "7c3cb124-9c30-492c-99e6-46518c232d73",
"p_source_label": "MongoDB",
"remoteAddress": "1.2.3.4",
"targetUsername": "insider@company.com",
"userId": "647f654f93bebc69123abc1",
"username": "user@company.com"
}
MongoDB org membership restriction disabled
#You can configure Atlas to require API access lists at the organization level. When you enable IP access list for the Atlas Administration API, all API calls in that organization must originate from a valid entry in the associated Atlas Administration API key access list. This rule detects when IP access list is disabled
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Detection logic
from panther_mongodb_helpers import mongodb_alert_context
def rule(event):
return event.get("eventTypeName", "") == "ORG_PUBLIC_API_ACCESS_LIST_NOT_REQUIRED"
def title(event):
user = event.get("username", "<USER_NOT_FOUND>")
return f"MongoDB: [{user}] has disabled IP access list for the Atlas Administration API"
def alert_context(event):
return mongodb_alert_context(event)
Rule specification
AnalysisType: rule
Description:
You can configure Atlas to require API access lists at the organization level.
When you enable IP access list for the Atlas Administration API, all API calls in that organization must originate
from a valid entry in the associated Atlas Administration API key access list.
This rule detects when IP access list is disabled
DisplayName: "MongoDB org membership restriction disabled"
Enabled: true
LogTypes:
- MongoDB.OrganizationEvent
RuleID: "MongoDB.org.Membership.Restriction.Disabled"
Filename: mongodb_org_membership_restriction_disabled.py
Severity: High
Tags:
- MongoDB
- Persistence
- Modify Authentication Process
- Conditional Access Policies
Reports:
MITRE ATT&CK:
- TA0003:T1556.009 # Modify Authentication Process
Reference: https://www.mongodb.com/docs/atlas/tutorial/manage-organizations/
Runbook: Check if this activity is legitimate. If not, re-enable IP access list for the Atlas Administration API
DedupPeriodMinutes: 60
Threshold: 1
Stages and Predicates
Fires on MongoDB.OrganizationEvent events when the condition below holds.
Condition
eventTypeNameisORG_PUBLIC_API_ACCESS_LIST_NOT_REQUIRED
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
eventTypeName | eq |
| field:"eventTypeName" kind:eq value:"ORG_PUBLIC_API_ACCESS_LIST_NOT_REQUIRED" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
username | |
target_username | targetUsername |
org_id | orgId |
remote_address | remoteAddress |
Response runbook
Check if this activity is legitimate. If not, re-enable IP access list for the Atlas Administration API
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"created": "2024-04-03 15:03:51.000000000",
"currentValue": {},
"eventTypeName": "ORG_PUBLIC_API_ACCESS_LIST_NOT_REQUIRED",
"id": "alert_id",
"isGlobalAdmin": false,
"orgId": "some_org_id",
"remoteAddress": "1.2.3.4",
"userId": "user_id",
"username": "some_user@company.com"
}
MongoDB security alerts disabled or deleted
#MongoDB provides security alerting policies for notifying admins when certain conditions are met. This rule detects when these policies are disabled or deleted.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Defense Impairment |
Detection logic
from panther_mongodb_helpers import mongodb_alert_context
def rule(event):
return event.get("eventTypeName", "") in [
"ALERT_CONFIG_DISABLED_AUDIT",
"ALERT_CONFIG_DELETED_AUDIT",
]
def title(event):
user = event.get("username", "<USER_NOT_FOUND>")
alert_id = event.get("alertConfigId", "<ALERT_NOT_FOUND>")
return f"MongoDB: [{user}] has disabled or deleted security alert [{alert_id}]"
def alert_context(event):
context = mongodb_alert_context(event)
context["alertConfigId"] = event.get("alertConfigId", "<ALERT_NOT_FOUND>")
return context
Rule specification
AnalysisType: rule
Description:
MongoDB provides security alerting policies for notifying admins when certain conditions are met.
This rule detects when these policies are disabled or deleted.
DisplayName: "MongoDB security alerts disabled or deleted"
Enabled: true
LogTypes:
- MongoDB.OrganizationEvent
RuleID: "MongoDB.Alerting.Disabled.Or.Deleted"
Filename: mongodb_alerting_disabled.py
Severity: High
Reports:
MITRE ATT&CK:
- TA0005:T1562.001 # Impair Defenses: Disable or Modify Tools
Reference: https://www.mongodb.com/docs/atlas/configure-alerts/
Runbook: Re-enable security alerts
DedupPeriodMinutes: 60
Threshold: 1
Stages and Predicates
Fires on MongoDB.OrganizationEvent events when the condition below holds.
Condition
eventTypeNameis one ofALERT_CONFIG_DISABLED_AUDIT,ALERT_CONFIG_DELETED_AUDIT
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
eventTypeName | in |
| field:"eventTypeName" kind:in |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
username | |
target_username | targetUsername |
org_id | orgId |
remote_address | remoteAddress |
alertConfigId |
Response runbook
Re-enable security alerts
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"alertConfigId": "alert_id",
"created": "2024-04-01 11:58:52.000000000",
"currentValue": {},
"eventTypeName": "ALERT_CONFIG_DELETED_AUDIT",
"id": "alert_id",
"isGlobalAdmin": false,
"links": [],
"orgId": "some_org_id",
"remoteAddress": "1.2.3.4",
"userId": "user_id",
"username": "some_user@company.com"
}
MongoDB user roles changed
#User roles changed.
Detection logic
from panther_mongodb_helpers import mongodb_alert_context
def rule(event):
return event.get("eventTypeName") == "USER_ROLES_CHANGED_AUDIT"
def title(event):
target_username = event.get("targetUsername", "<USER_NOT_FOUND>")
org_id = event.get("orgId", "<ORG_NOT_FOUND>")
return f"MongoDB Atlas: User [{target_username}] roles changed in org [{org_id}]"
def alert_context(event):
return mongodb_alert_context(event)
Rule specification
AnalysisType: rule
Description: "User roles changed."
DisplayName: "MongoDB user roles changed"
Enabled: true
Filename: mongodb_user_roles_changed.py
Severity: Low
Reference: https://www.mongodb.com/docs/v4.2/tutorial/create-users/
DedupPeriodMinutes: 60
LogTypes:
- MongoDB.OrganizationEvent
RuleID: "MongoDB.User.Roles.Changed"
Threshold: 1
Stages and Predicates
Fires on MongoDB.OrganizationEvent events when the condition below holds.
Condition
eventTypeNameisUSER_ROLES_CHANGED_AUDIT
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
eventTypeName | eq |
| field:"eventTypeName" kind:eq value:"USER_ROLES_CHANGED_AUDIT" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
username | |
target_username | targetUsername |
org_id | orgId |
remote_address | remoteAddress |
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"created": "2023-06-07 16:57:55",
"currentValue": {},
"eventTypeName": "USER_ROLES_CHANGED_AUDIT",
"id": "6480b7139bd8a012345ABCDE",
"isGlobalAdmin": false,
"links": [
{
"href": "https://cloud.mongodb.com/api/atlas/v1.0/orgs/12345xyzlmnce4f17d6e8e130/events/6480b7139bd8a012345ABCDE",
"rel": "self"
}
],
"orgId": "12345xyzlmnce4f17d6e8e130",
"p_event_time": "2023-06-07 16:57:55",
"p_log_type": "MongoDB.OrganizationEvent",
"p_parse_time": "2023-06-07 17:04:42.59",
"p_row_id": "ea276b16216684d9e198c0d0188a3d",
"p_schema_version": 0,
"p_source_id": "7c3cb124-9c30-492c-99e6-46518c232d73",
"p_source_label": "MongoDB",
"remoteAddress": "1.2.3.4",
"targetUsername": "insider@company.com",
"userId": "647f654f93bebc69123abc1",
"username": "user@company.com"
}
MongoDB user was created or deleted
#User was created or deleted.
Detection logic
from panther_mongodb_helpers import mongodb_alert_context
def rule(event):
return event.get("eventTypeName", "") in ("JOINED_ORG", "REMOVED_FROM_ORG")
def title(event):
event_name = event.get("eventTypeName")
target_username = event.get("targetUsername", "<USER_NOT_FOUND>")
org_id = event.get("orgId", "<ORG_NOT_FOUND>")
action = "has joined org" if event_name == "JOINED_ORG" else "was removed from org"
return f"MongoDB Atlas: [{target_username}] {action} [{org_id}]"
def alert_context(event):
return mongodb_alert_context(event)
Rule specification
AnalysisType: rule
Description: "User was created or deleted."
DisplayName: "MongoDB user was created or deleted"
Enabled: true
Filename: mongodb_user_created_or_deleted.py
Severity: Medium
Reference: https://www.mongodb.com/docs/v4.2/tutorial/create-users/
DedupPeriodMinutes: 60
LogTypes:
- MongoDB.OrganizationEvent
RuleID: "MongoDB.User.Created.Or.Deleted"
Threshold: 1
Stages and Predicates
Fires on MongoDB.OrganizationEvent events when the condition below holds.
Condition
eventTypeNameis one ofJOINED_ORG,REMOVED_FROM_ORG
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
eventTypeName | in |
| field:"eventTypeName" kind:in |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
username | |
target_username | targetUsername |
org_id | orgId |
remote_address | remoteAddress |
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"created": "2023-06-07 16:57:55",
"currentValue": {},
"eventTypeName": "JOINED_ORG",
"id": "6480b7139bd8a012345ABCDE",
"isGlobalAdmin": false,
"links": [
{
"href": "https://cloud.mongodb.com/api/atlas/v1.0/orgs/12345xyzlmnce4f17d6e8e130/events/6480b7139bd8a012345ABCDE",
"rel": "self"
}
],
"orgId": "12345xyzlmnce4f17d6e8e130",
"p_event_time": "2023-06-07 16:57:55",
"p_log_type": "MongoDB.OrganizationEvent",
"p_parse_time": "2023-06-07 17:04:42.59",
"p_row_id": "ea276b16216684d9e198c0d0188a3d",
"p_schema_version": 0,
"p_source_id": "7c3cb124-9c30-492c-99e6-46518c232d73",
"p_source_label": "MongoDB",
"remoteAddress": "1.2.3.4",
"targetUsername": "insider@company.com",
"userId": "647f654f93bebc69123abc1",
"username": "user@company.com"
}