Skip to content

Commit

Permalink
Merge branch 'develop' into THREAT-311/cs_eventstreams_passthrough
Browse files Browse the repository at this point in the history
  • Loading branch information
arielkr256 authored Dec 6, 2024
2 parents 2187efc + 2495608 commit ad607fd
Show file tree
Hide file tree
Showing 22 changed files with 1,385 additions and 9 deletions.
35 changes: 35 additions & 0 deletions data_models/aws_cloudtrail_data_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,38 @@ def load_ip_address(event):
except ipaddress.AddressValueError:
return None
return source_ip


# get actor user from correct field based on identity type
# https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-user-identity.html#cloudtrail-event-reference-user-identity-fields
def get_actor_user(event):
user_type = deep_get(event, "userIdentity", "type")
if event.get("eventType") == "AwsServiceEvent":
actor_user = deep_get(event, "userIdentity", "invokedBy", default="UnknownAwsServiceEvent")
elif user_type == "Root":
actor_user = deep_get(
event,
"userIdentity",
"userName",
default=deep_get(event, "userIdentity", "accountId", default="UnknownRootUser"),
)
elif user_type in ("IAMUser", "Directory", "Unknown", "SAMLUser", "WebIdentityUser"):
actor_user = deep_get(event, "userIdentity", "userName", default=f"Unknown{user_type}")
elif user_type in ("AssumedRole", "Role", "FederatedUser"):
actor_user = deep_get(
event,
"userIdentity",
"sessionContext",
"sessionIssuer",
"userName",
default=f"Unknown{user_type}",
)
elif user_type == "IdentityCenterUser":
actor_user = deep_get(
event, "additionalEventData", "UserName", default=f"Unknown{user_type}"
)
elif user_type in ("AWSService", "AWSAccount"):
actor_user = event.get("sourceIdentity", f"Unknown{user_type}")
else:
actor_user = "UnknownUser"
return actor_user
2 changes: 1 addition & 1 deletion data_models/aws_cloudtrail_data_model.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Filename: aws_cloudtrail_data_model.py
Enabled: true
Mappings:
- Name: actor_user
Path: $.userIdentity..userName
Method: get_actor_user
- Name: event_type
Method: get_event_type
- Name: source_ip
Expand Down
76 changes: 76 additions & 0 deletions global_helpers/global_helpers_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2391,5 +2391,81 @@ def test_change_filed_is_empty_on_update_context(self):
)


class TestPantherFlowInvestigation(unittest.TestCase):
def test_pantherflow_investigation(self):
# pylint: disable=line-too-long
event = {
"p_any_ip_addresses": ["12.34.56.78"],
"p_source_file": {
"aws_s3_bucket": "threat-research-trail-trail-bucket-0ipb5nzxam",
"aws_s3_key": "AWSLogs/123456789123/CloudTrail/us-east-1/2024/11/25/123456789123_CloudTrail_us-east-1_20241125T1505Z_XLixf09QqBSOD7c4.json.gz",
},
"p_any_trace_ids": ["ASIAQWERTYUIOPASDFGH"],
"p_any_actor_ids": ["AROAQWERTYUIOPASDFGH", "AROAQWERTYUIOPASDFGH:bob.ross"],
"p_any_aws_account_ids": ["123456789123"],
"p_any_aws_arns": [
"arn:aws:iam::123456789123:role/aws-reserved/sso.amazonaws.com/AWSReservedSSO_DevAdmin",
"arn:aws:sts::123456789123:assumed-role/AWSReservedSSO_DevAdmin/bob.ross",
"arn:aws:iam::123456789123:role/aws-reserved/sso.amazonaws.com/us-west-2/AWSReservedSSO_DevAdmin",
],
"p_any_usernames": ["AWSReservedSSO_DevAdmin", "bob.ross"],
"p_event_time": "2024-11-25 15:00:21.000000",
"p_log_type": "AWS.CloudTrail",
"p_parse_time": "2024-11-25 15:05:54.123385",
"p_row_id": "d66379c617d1f7b3b2e7ce9623c104",
"p_schema_version": 0,
"p_source_id": "d0a1e235-6548-4e7f-952a-35063b304007",
"p_source_label": "threat-research-trail-us-east-1",
"p_udm": {
"source": {"address": "12.34.56.78", "ip": "12.34.56.78"},
"user": {
"arns": [
"arn:aws:iam::123456789123:role/aws-reserved/sso.amazonaws.com/AWSReservedSSO_DevAdmin",
"arn:aws:sts::123456789123:assumed-role/AWSReservedSSO_DevAdmin/bob.ross",
]
},
},
}
event = ImmutableCaseInsensitiveDict(event)
query = """union panther_signals.public.correlation_signals
, panther_logs.public.aws_cloudtrail
| where p_event_time between datetime('2024-11-25 15:00:21.000000') - time.parse_timespan('30m') .. datetime('2024-11-25 15:00:21.000000') + time.parse_timespan('30m')
| where arrays.overlap(p_any_ip_addresses, ['12.34.56.78'])
or arrays.overlap(p_any_trace_ids, ['ASIAQWERTYUIOPASDFGH'])
or arrays.overlap(p_any_actor_ids, ['AROAQWERTYUIOPASDFGH', 'AROAQWERTYUIOPASDFGH:bob.ross'])
or arrays.overlap(p_any_aws_arns, ['arn:aws:iam::123456789123:role/aws-reserved/sso.amazonaws.com/AWSReservedSSO_DevAdmin', 'arn:aws:sts::123456789123:assumed-role/AWSReservedSSO_DevAdmin/bob.ross', 'arn:aws:iam::123456789123:role/aws-reserved/sso.amazonaws.com/us-west-2/AWSReservedSSO_DevAdmin'])
or arrays.overlap(p_any_usernames, ['AWSReservedSSO_DevAdmin', 'bob.ross'])
| sort p_event_time"""
self.assertEqual(p_b_h.pantherflow_investigation(event), query)


class TestEmailRegex(unittest.TestCase):
def test_email_regex(self):
email_regex = p_b_h.EMAIL_REGEX
valid_emails = [
"asfe@acme.com",
"afef-awef@feaf.efaef.aef-aefc.org",
"ifjlid%fesfdj+123@gmail.com",
"a@b.co",
"alfij.fjii-fjids+123@fsjd-sdf-sjkj.co.co.co.uk",
]
invalid_emails = [
"asfe@acme",
"dff@.com",
"a@b",
"a@b.",
"a@b.c",
"a@b.c.",
"a@b.c.c",
"asdf?2d@gmail.com",
"asdf@",
"a.b@g&g.com",
]
for email in valid_emails:
self.assertTrue(email_regex.match(email))
for email in invalid_emails:
self.assertFalse(email_regex.match(email))


if __name__ == "__main__":
unittest.main()
2 changes: 2 additions & 0 deletions global_helpers/panther_aws_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import Any, Dict, List

import boto3
from panther_base_helpers import pantherflow_investigation
from panther_config import config


Expand Down Expand Up @@ -38,6 +39,7 @@ def aws_rule_context(event):
"sourceIPAddress": event.get("sourceIPAddress", "<MISSING_SOURCE_IP>"),
"userAgent": event.get("userAgent", "<MISSING_USER_AGENT>"),
"userIdentity": event.get("userIdentity", "<MISSING_USER_IDENTITY>"),
"PantherFlow Investigation": pantherflow_investigation(event),
}


Expand Down
25 changes: 25 additions & 0 deletions global_helpers/panther_base_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class PantherUnexpectedAlert(Exception):
# Generic Helpers #
# # # # # # # # # # # # # #

EMAIL_REGEX = re.compile(r"[\w.+%-]+@[\w.-]+\.[a-zA-Z]{2,}")


def deep_get(dictionary: dict, *keys, default=None):
"""Safely return the value of an arbitrarily nested map
Expand Down Expand Up @@ -327,3 +329,26 @@ def add_parse_delay(event, context: dict) -> dict:
parsing_delay = time_delta(event.get("p_event_time"), event.get("p_parse_time"))
context["parseDelay"] = f"{parsing_delay}"
return context


# generate a PantherFlow investigation from an event
def pantherflow_investigation(event, interval="30m"):
logtype = event.get("p_log_type", "").lower().replace(".", "_")
timestamp = event.get("p_event_time", "")

query = f"""union panther_signals.public.correlation_signals
, panther_logs.public.{logtype}
| where p_event_time between datetime('{timestamp}') - time.parse_timespan('{interval}') .. datetime('{timestamp}') + time.parse_timespan('{interval}')
"""

first = True
for key, value in event.items():
if key.startswith("p_any_") and key != "p_any_aws_account_ids":
if first:
query += f"| where arrays.overlap({key}, {value.copy()})\n"
first = False
else:
query += f" or arrays.overlap({key}, {value.copy()})\n"
query += "| sort p_event_time"

return query
4 changes: 4 additions & 0 deletions global_helpers/panther_okta_helpers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from panther_base_helpers import pantherflow_investigation


def okta_alert_context(event):
"""Returns common context for automation of Okta alerts"""
return {
Expand All @@ -12,4 +15,5 @@ def okta_alert_context(event):
"authentication_context": event.get("authenticationcontext", {}),
"security_context": event.get("securitycontext", {}),
"ips": event.get("p_any_ip_addresses", []),
"PantherFlow Investigation": pantherflow_investigation(event),
}
5 changes: 5 additions & 0 deletions packs/aws.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,11 @@ PackDefinition:
- AWS.CloudFormation.Stack.UsesIAMServiceRole
- AWS.CloudTrail.CodebuildProjectMadePublic
- AWS.CloudTrail.Created
- AWS.CloudTrail.DNSLogsDeleted
- AWS.CloudTrail.Enabled
- AWS.CloudTrail.EventSelectorsDisabled
- AWS.CloudTrail.SecurityConfigurationChange
- AWS.CloudTrail.ShortLifecycle
- AWS.CloudTrail.Stopped
- AWS.CloudTrail.UnauthorizedAPICall
- AWS.CloudWatchLogs.DataRetention1Year
Expand Down Expand Up @@ -131,6 +134,7 @@ PackDefinition:
- AWS.S3.ServerAccess.Error
- AWS.SecurityHub.Finding.Evasion
- AWS.VPC.FlowLogs
- AWS.VPCFlow.LogsDeleted
- AWS.WAF.Disassociation
- AWS.WAF.HasXSSPredicate
- AWS.WAF.LoggingConfigured
Expand All @@ -144,6 +148,7 @@ PackDefinition:
- AWS.CMK.KeyRotation
- AWS.DynamoDB.TableTTLEnabled
- AWS.EC2.Vulnerable.XZ.Image.Launched
- Amazon.EKS.AnonymousAPIAccess
- AWS.IAM.Policy.DoesNotGrantAdminAccess
- AWS.IAM.Policy.DoesNotGrantNetworkAdminAccess
- AWS.IAM.Resource.DoesNotHaveInlinePolicy
Expand Down
1 change: 1 addition & 0 deletions packs/msft_graph.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ PackDefinition:
- Microsoft365.MFA.Disabled
- Microsoft365.Exchange.External.Forwarding
# Globals
- panther_base_helpers
- panther_msft_helpers
- panther_config
- panther_config_defaults
Expand Down
3 changes: 1 addition & 2 deletions packs/zoom.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ PackDefinition:
# Data Models used in these detections
- Standard.Zoom.Operation
# Globals used in these detections


- panther_base_helpers
- panther_event_type_helpers
- panther_zoom_helpers
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from panther_aws_helpers import aws_cloudtrail_success, aws_rule_context, lookup_aws_account_name

# API calls that are indicative of CloudTrail changes
CLOUDTRAIL_EDIT_SELECTORS = {"PutEventSelectors"}


def rule(event):
if not (aws_cloudtrail_success(event) and event.get("eventName") in CLOUDTRAIL_EDIT_SELECTORS):
return False

# Check if management events are included for each selector.
# deep_walk only returns a list if there's more than 1 entry in the nested array, so we must
# enforce it to be a list.
includes = event.deep_walk("requestParameters", "eventSelectors", "includeManagementEvents")
if not isinstance(includes, list):
includes = [includes]

# Return False all the management events are included, else return True and raise alert
return not all(includes)


def dedup(event):
# Merge on the CloudTrail ARN
return event.deep_get("requestParameters", "trailName", default="<UNKNOWN_NAME>")


def title(event):
return (
f"Management events have been exluded from CloudTrail [{dedup(event)}] in account "
f"[{lookup_aws_account_name(event.get('recipientAccountId'))}]"
)


def alert_context(event):
return aws_rule_context(event)
Loading

0 comments on commit ad607fd

Please sign in to comment.