Skip to content

Commit

Permalink
Additional gsuite rules (#78)
Browse files Browse the repository at this point in the history
* additional gsuite rules

* fixed formatting

* more rules

* final rules for now

* fixed  formatting

* removed invalid  characters

* addressed pr comments

* re-formatted example
  • Loading branch information
nhakmiller authored Aug 3, 2020
1 parent 4368d92 commit 074e23b
Show file tree
Hide file tree
Showing 31 changed files with 1,156 additions and 0 deletions.
46 changes: 46 additions & 0 deletions global_helpers/panther_base_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,49 @@ def is_dmz_tags(resource):
# function being used, etc.
IN_PCI_SCOPE = in_pci_scope_tags
IS_DMZ = is_dmz_tags

GSUITE_PARAMETER_VALUES = [
'value',
'intValue',
'boolValue',
'multiValue',
'multiIntValue',
'messageValue',
'multiMessageValue',
]


# GSuite parameters are formatted as a list of dictionaries, where each dictionary has a 'name' key
# that maps to the name of the parameter, and one key from GSUITE_PARAMETER_VALUES that maps to the
# value of the parameter. This means to lookup the value of a particular parameter, you must
# traverse the entire list of parameters to find it and then know (or guess) what type of value it
# contains. This helper function handles that for us.
#
# Example parameters list:
# parameters = [
# {
# "name": "event_id",
# "value": "abc123"
# },
# {
# "name": "start_time",
# "intValue": 63731901000
# },
# {
# "name": "end_time",
# "intValue": 63731903000
# },
# {
# "name": "things",
# "multiValue": [ "DRIVE" , "MEME"]
# }
# ]
def gsuite_parameter_lookup(parameters, key):
for param in parameters:
if param['name'] != key:
continue
for value in GSUITE_PARAMETER_VALUES:
if value in param:
return param[value]
return None
return None
19 changes: 19 additions & 0 deletions gsuite_reports_rules/gsuite_advanced_protection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
def rule(event):
if event['id'].get('applicationName') != 'user_accounts':
return False

for details in event.get('events', [{}]):
if (details.get('type') == 'titanium_change' and
details.get('name') == 'titanium_unenroll'):
return True

return False


def dedup(event):
return event.get('actor', {}).get('email')


def title(event):
return 'Advanced protection was disabled for user [{}]'.format(
event.get('actor', {}).get('email'))
46 changes: 46 additions & 0 deletions gsuite_reports_rules/gsuite_advanced_protection.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
AnalysisType: rule
Filename: gsuite_advanced_protection.py
RuleID: GSuite.AdvancedProtection
DisplayName: GSuite User Advanced Protection Change
Enabled: true
LogTypes:
- GSuite.Reports
Tags:
- GSuite
Severity: Low
Description: >
A user disabled advanced protection for themselves.
Reference: https://developers.google.com/admin-sdk/reports/v1/appendix/activity/user-accounts#titanium_change
Runbook: >
Have the user re-enable Google Advanced Protection
Tests:
-
Name: Advanced Protection Enabled
LogType: GSuites.Reports
ExpectedResult: false
Log:
{
'actor': {'email': '[email protected]'},
'id': {'applicationName': 'user_accounts'},
'events': [
{
'type': 'titanium_change',
'name': 'titanium_enroll'
}
]
}
-
Name: Advanced Protection Disabled
LogType: GSuites.Reports
ExpectedResult: true
Log:
{
'actor': {'email': '[email protected]'},
'id': {'applicationName': 'user_accounts'},
'events': [
{
'type': 'titanium_change',
'name': 'titanium_unenroll'
}
]
}
19 changes: 19 additions & 0 deletions gsuite_reports_rules/gsuite_doc_ownership_transfer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from panther_base_helpers import gsuite_parameter_lookup as param_lookup

ORG_DOMAINS = {
'@example.com',
}


def rule(event):
if event['id'].get('applicationName') != 'admin':
return False

for details in event.get('events', [{}]):
if (details.get('type') == 'DOCS_SETTINGS' and
details.get('name') == 'TRANSFER_DOCUMENT_OWNERSHIP'):
new_owner = param_lookup(details.get('parameters', {}), 'NEW_VALUE')
return bool(new_owner) and not any(
new_owner.endswith(x) for x in ORG_DOMAINS)

return False
49 changes: 49 additions & 0 deletions gsuite_reports_rules/gsuite_doc_ownership_transfer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
AnalysisType: rule
Filename: gsuite_doc_ownership_transfer.py
RuleID: GSuite.DocOwnershipTransfer
DisplayName: GSuite Document External Ownership Transfer
Enabled: false
LogTypes:
- GSuite.Reports
Tags:
- GSuite
- Configuration Required
Severity: Low
Description: >
A GSuite document's ownership was transferred to an external party.
Reference: https://developers.google.com/admin-sdk/reports/v1/appendix/activity/admin-docs-settings#TRANSFER_DOCUMENT_OWNERSHIP
Runbook: >
Verify that this document did not contain sensitive or private company information.
Tests:
-
Name: Ownership Transferred Within Organization
LogType: GSuites.Reports
ExpectedResult: false
Log:
{
'actor': {'email': '[email protected]'},
'id': {'applicationName': 'admin'},
'events': [
{
'type': 'DOCS_SETTINGS',
'name': 'TRANSFER_DOCUMENT_OWNERSHIP',
'parameters': [{'name': 'NEW_VALUE', 'value': '[email protected]'}]
}
]
}
-
Name: Document Transferred to External User
LogType: GSuites.Reports
ExpectedResult: true
Log:
{
'actor': {'email': '[email protected]'},
'id': {'applicationName': 'admin'},
'events': [
{
'type': 'DOCS_SETTINGS',
'name': 'TRANSFER_DOCUMENT_OWNERSHIP',
'parameters': [{'name': 'NEW_VALUE', 'value': '[email protected]'}]
}
]
}
36 changes: 36 additions & 0 deletions gsuite_reports_rules/gsuite_drive_overly_visible.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from panther_base_helpers import gsuite_parameter_lookup as param_lookup

RESOURCE_CHANGE_EVENTS = {
'create',
'move',
'upload',
'edit',
}

PERMISSIVE_VISIBILITY = {
'people_with_link',
'public_on_the_web',
}


def rule(event):
if event['id'].get('applicationName') != 'drive':
return False

for details in event.get('events', [{}]):
if (details.get('type') == 'access' and
details.get('name') in RESOURCE_CHANGE_EVENTS and
param_lookup(details.get('parameters', {}),
'visibility') in PERMISSIVE_VISIBILITY):
return True

return False


def dedup(event):
return event['p_row_id']


def title(event):
return 'User [{}] modified a document that has overly permissive share settings'.format(
event.get('actor', {}).get('email'))
63 changes: 63 additions & 0 deletions gsuite_reports_rules/gsuite_drive_overly_visible.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
AnalysisType: rule
Filename: gsuite_drive_overly_visible.py
RuleID: GSuite.DriveOverlyVisible
DisplayName: GSuite Overly Visible Drive Document
Enabled: true
LogTypes:
- GSuite.Reports
Tags:
- GSuite
Severity: Medium
Description: >
A Google drive resource that is overly visible has been modified.
Reference: https://developers.google.com/admin-sdk/reports/v1/appendix/activity/drive#access
Runbook: >
Investigate whether the drive document is appropriate to be this visible.
Tests:
-
Name: Access Event
LogType: GSuites.Reports
ExpectedResult: false
Log:
{
'actor': {'email': '[email protected]'},
'id': {'applicationName': 'drive'},
'events': [
{
'type': 'access',
'name': 'download'
}
]
}
-
Name: Modify Event Without Over Visibility
LogType: GSuites.Reports
ExpectedResult: false
Log:
{
'actor': {'email': '[email protected]'},
'id': {'applicationName': 'drive'},
'events': [
{
'type': 'access',
'name': 'edit',
'parameters': [{'name': 'visibility', 'value': 'private'}]
}
]
}
-
Name: Overly Visible Doc Modified
LogType: GSuites.Reports
ExpectedResult: true
Log:
{
'actor': {'email': '[email protected]'},
'id': {'applicationName': 'drive'},
'events': [
{
'type': 'access',
'name': 'edit',
'parameters': [{'name': 'visibility', 'value': 'people_with_link'}]
}
]
}
23 changes: 23 additions & 0 deletions gsuite_reports_rules/gsuite_drive_visibility_change.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from panther_base_helpers import gsuite_parameter_lookup as param_lookup


def rule(event):
if event['id'].get('applicationName') != 'drive':
return False

for details in event.get('events', [{}]):
if (details.get('type') == 'acl_change' and
param_lookup(details.get('parameters', {}),
'visibility_change') == 'external'):
return True

return False


def dedup(event):
return event['p_row_id']


def title(event):
return 'User [{}] made a document externally visible for the first time'.format(
event.get('actor', {}).get('email'))
76 changes: 76 additions & 0 deletions gsuite_reports_rules/gsuite_drive_visibility_change.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
AnalysisType: rule
Filename: gsuite_drive_visibility_change.py
RuleID: GSuite.DriveVisiblityChanged
DisplayName: GSuite External Drive Document
Enabled: true
LogTypes:
- GSuite.Reports
Tags:
- GSuite
Severity: Medium
Description: >
A Google drive resource became externally accessible.
Reference: https://developers.google.com/admin-sdk/reports/v1/appendix/activity/drive#acl_change
Runbook: >
Investigate whether the drive document is appropriate to be publicly accessible.
Tests:
-
Name: Access Event
LogType: GSuites.Reports
ExpectedResult: false
Log:
{
'actor': {'email': '[email protected]'},
'id': {'applicationName': 'drive'},
'events': [
{
'type': 'access',
'name': 'upload'
}
]
}
-
Name: ACL Change without Visiblity Change
LogType: GSuites.Reports
ExpectedResult: false
Log:
{
'actor': {'email': '[email protected]'},
'id': {'applicationName': 'drive'},
'events': [
{
'type': 'acl_change',
'name': 'shared_drive_settings_change'
}
]
}
-
Name: Doc Became Public
LogType: GSuites.Reports
ExpectedResult: true
Log:
{
'actor': {'email': '[email protected]'},
'id': {'applicationName': 'drive'},
'events': [
{
'type': 'acl_change',
'parameters': [{'name': 'visibility_change', 'value': 'external'}]
}
]
}
-
Name: Doc Became Private
LogType: GSuites.Reports
ExpectedResult: false
Log:
{
'actor': {'email': '[email protected]'},
'id': {'applicationName': 'drive'},
'events': [
{
'type': 'acl_change',
'parameters': [{'name': 'visibility_change', 'value': 'internal'}]
}
]
}
10 changes: 10 additions & 0 deletions gsuite_reports_rules/gsuite_google_access.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
def rule(event):
if event['id'].get('applicationName') != 'access_transparency':
return False

for details in event.get('events', [{}]):
if (details.get('type') == 'GSUITE_RESOURCE' and
details.get('name') == 'ACCESS'):
return True

return False
Loading

0 comments on commit 074e23b

Please sign in to comment.