Skip to content

Commit

Permalink
Added attribute status_reasons and address review comments
Browse files Browse the repository at this point in the history
  • Loading branch information
roshanmaskey committed Jan 3, 2025
1 parent 72dad4b commit a333753
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 5 deletions.
25 changes: 22 additions & 3 deletions plaso/parsers/jsonl_plugins/gcp_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class GCPLogEventData(events.EventData):
recorded_time (dfdatetime.DateTimeValues): date and time the log entry
was recorded.
request_account_identifier (str): GCP account identifier of the request.
request_address (str): IP address assigned to a Google Cloud Engine (GCE)
instance.
request_description (str): description of the request.
request_direction (str): direction of the request.
request_email (str): email address of the request.
Expand All @@ -58,6 +60,7 @@ class GCPLogEventData(events.EventData):
engine instance.
status_code (str): operation success or failure code.
status_message (str); operation success or failure message.
status_reasons (list[str]): reasons for operation failure.
text_payload (str): text payload for logs not using a JSON or proto payload.
user_agent (str): user agent used in the request.
"""
Expand Down Expand Up @@ -88,6 +91,7 @@ def __init__(self):
self.principal_subject = None
self.recorded_time = None
self.request_account_identifier = None
self.request_address = None
self.request_description = None
self.request_direction = None
self.request_email = None
Expand Down Expand Up @@ -249,16 +253,30 @@ def _ParseProtoPayloadStatus(self, proto_payload, event_data):
#
# Empty `code` and `message` fields indicate the operation was successful.

event_data.status_code = self._GetJSONValue(status, 'code')
event_data.status_code = str(self._GetJSONValue(status, 'code', ''))
event_data.status_message = self._GetJSONValue(status, 'message')

def _ParseComputeInstancesInsert(self, request, event_data):
# `protoPayload.status.details[].reason` contains reason for an operation
# failure.
status_reasons = []

for status_detail in self._GetJSONValue(status, 'details', []):
status_reason = self._GetJSONValue(status_detail, 'reason')
if status_reason:
status_reasons.append(status_reason)

if status_reasons:
event_data.status_reasons = status_reasons

def _ParseComputeInsertRequest(self, request, event_data):
"""Extracts compute.instances.insert information.
Args:
request (dict): JSON dictionary of the `protoPayload.request` field.
event_data (GCPLogEventData): event data.
"""
# source_images hold Google Cloud source disk path used in creating a GCE
# instance.
source_images = []

for disk in self._GetJSONValue(request, 'disks', []):
Expand Down Expand Up @@ -307,7 +325,7 @@ def _ParseComputeProtoPayload(self, proto_payload, event_data):
return

if request_type == 'type.googleapis.com/compute.instances.insert':
self._ParseComputeInstancesInsert(request, event_data)
self._ParseComputeInsertRequest(request, event_data)

def _ParseProtoPayload(self, json_dict, event_data):
"""Extracts information from a protoPayload value.
Expand Down Expand Up @@ -351,6 +369,7 @@ def _ParseProtoPayloadRequest(self, proto_payload, event_data):

event_data.request_account_identifier = self._GetJSONValue(
request, 'account_id')
event_data.request_address = self._GetJSONValue(request, 'address')
event_data.request_description = self._GetJSONValue(request, 'description')
event_data.request_direction = self._GetJSONValue(request, 'direction')
event_data.request_email = self._GetJSONValue(request, 'email')
Expand Down
1 change: 1 addition & 0 deletions test_data/gcp_logging.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
{"insertId": "1k28f3cfv7aknt", "jsonPayload": {"content": "This is a json payload", "event_subtype": "test_subtype", "actor": {"user": "[email protected]"}}, "logName": "projects/fake-project/logs/testlog", "receiveTimestamp": "2021-10-19T02:05:41.496590981Z", "resource": {"labels": {"location": "", "namespace": "", "node_id": "", "project_id": "fake-project", "instance_id": "866011396029255273"}, "type": "generic_node"}, "timestamp": "2021-10-19T02:05:41.496590981Z"}
{"insertId": "1io3yo2fursxdi", "logName": "projects/fake-project/logs/testlog", "receiveTimestamp": "2021-10-19T02:04:00.272384509Z", "resource": {"labels": {"location": "", "namespace": "", "node_id": "", "project_id": "fake-project"}, "type": "generic_node"}, "textPayload": "This is a text payload", "timestamp": "2021-10-19T02:04:00.272384509Z"}
{"insertId":"-duywnve29mpi","labels":{"compute.googleapis.com/root_trigger_id":"b0966c41-45b9-4484-b3b9-b436bfdde977"},"logName":"projects/fake-project/logs/cloudaudit.googleapis.com%2Factivity","operation":{"first":true,"id":"operation-1714162209773-617057d99d773-818c5509-b54204dc","producer":"compute.googleapis.com"},"protoPayload":{"@type":"type.googleapis.com/google.cloud.audit.AuditLog","authenticationInfo":{"principalEmail":"[email protected]", "principalSubject":"user:[email protected]","serviceAccountDelegationInfo":[{"firstPartyPrincipal":{"principalEmail":"[email protected]"}},{"firstPartyPrincipal":{"principalEmail":"[email protected]"}}]},"authorizationInfo":[{"granted":true,"permission":"compute.instances.create","permissionType":"ADMIN_WRITE","resource":"projects/fake-project/zones/us-central1-b/instances/fake-compute-instance","resourceAttributes":{"name":"projects/fake-project/zones/us-central1-b/instances/fake-compute-instance","service":"compute","type":"compute.instances"}},{"granted":true,"permission":"compute.disks.create","permissionType":"ADMIN_WRITE","resource":"projects/fake-project/zones/us-central1-b/disks/fake-compute-instance","resourceAttributes":{"name":"projects/fake-project/zones/us-central1-b/disks/fake-compute-instance","service":"compute","type":"compute.disks"}},{"granted":true,"permission":"compute.subnetworks.use","permissionType":"ADMIN_WRITE","resource":"projects/fake-project/regions/us-central1/subnetworks/default","resourceAttributes":{"name":"projects/fake-project/regions/us-central1/subnetworks/default","service":"compute","type":"compute.subnetworks"}},{"granted":true,"permission":"compute.subnetworks.useExternalIp","permissionType":"ADMIN_WRITE","resource":"projects/fake-project/regions/us-central1/subnetworks/default","resourceAttributes":{"name":"projects/fake-project/regions/us-central1/subnetworks/default","service":"compute","type":"compute.subnetworks"}},{"granted":true,"permission":"compute.instances.setMetadata","permissionType":"ADMIN_WRITE","resource":"projects/fake-project/zones/us-central1-b/instances/fake-compute-instance","resourceAttributes":{"name":"projects/fake-project/zones/us-central1-b/instances/fake-compute-instance","service":"compute","type":"compute.instances"}},{"granted":true,"permission":"compute.instances.setLabels","permissionType":"ADMIN_WRITE","resource":"projects/fake-project/zones/us-central1-b/instances/fake-compute-instance","resourceAttributes":{"name":"projects/fake-project/zones/us-central1-b/instances/fake-compute-instance","service":"compute","type":"compute.instances"}},{"granted":true,"permission":"compute.instances.setServiceAccount","permissionType":"ADMIN_WRITE","resource":"projects/fake-project/zones/us-central1-b/instances/fake-compute-instance","resourceAttributes":{"name":"projects/fake-project/zones/us-central1-b/instances/fake-compute-instance","service":"compute","type":"compute.instances"}}],"methodName":"beta.compute.instances.insert","request":{"@type":"type.googleapis.com/compute.instances.insert","description":"Fake GCE instance","disks":[{"autoDelete":true,"boot":true,"initializeParams":{"diskSizeGb":"100","diskType":"zones/us-central1-b/diskTypes/pd-ssd","sourceImage":"projects/fake-project/global/images/fake-source-image"}}],"labels":[],"machineType":"zones/us-central1-b/machineTypes/n1-highmem-16","name":"fake-compute-instance","networkInterfaces":[{"accessConfigs":[{"name":"External NAT","type":"ONE_TO_ONE_NAT"}],"network":"global/networks/default"}],"scheduling":{"automaticRestart":true},"serviceAccounts":[{"email":"[email protected]","scopes":["https://www.googleapis.com/auth/cloud-platform"]}]},"requestMetadata":{"callerIp":"1.1.1.1","callerSuppliedUserAgent":"fake-user-agent-string command/gcloud.compute.instances.insert invocation-id/a1b2c3d4e5f6 environment/GCE","destinationAttributes":{},"requestAttributes":{"auth":{},"time":"2024-04-26T20:10:11.171739Z"}},"resourceLocation":{"currentLocations":["us-central1-b"]},"resourceName":"projects/1234567890/zones/us-central1-b/instances/fake-compute-instance","response":{"@type":"type.googleapis.com/operation","id":"30220247688656077","insertTime":"2024-04-26T13:10:11.055-07:00","name":"operation-1714162209773-617057d99d773-818c5509-b54204dc","operationType":"insert","progress":"0","selfLink":"https://www.googleapis.com/compute/beta/projects/fake-project/zones/us-central1-b/operations/operation-1714162209773-617057d99d773-818c5509-b54204dc","selfLinkWithId":"https://www.googleapis.com/compute/beta/projects/fake-project/zones/us-central1-b/operations/30220247688656077","startTime":"2024-04-26T13:10:11.056-07:00","status":"RUNNING","targetId":"9876543210","targetLink":"https://www.googleapis.com/compute/beta/projects/fake-project/zones/us-central1-b/instances/fake-compute-instance","user":"[email protected]","zone":"https://www.googleapis.com/compute/beta/projects/fake-project/zones/us-central1-b"},"serviceName":"compute.googleapis.com"},"receiveTimestamp":"2024-04-26T20:10:11.491740716Z","resource":{"labels":{"instance_id":"9876543210","project_id":"fake-project","zone":"us-central1-b"},"type":"gce_instance"},"severity":"NOTICE","timestamp":"2024-04-26T20:10:10.024055Z"}
{"insertId":"1awjxggeaxqgz","logName":"projects/ketchup/logs/cloudaudit.googleapis.com%2Factivity","protoPayload":{"@type":"type.googleapis.com/google.cloud.audit.AuditLog","status":{"code":7,"message":"Permission \"iam.serviceAccounts.create\" denied on resource (or it may not exist).","details":[{"@type":"type.googleapis.com/google.rpc.ErrorInfo","reason":"IAM_PERMISSION_DENIED","domain":"iam.googleapis.com","metadata":{"permission":"iam.serviceAccounts.create"}}]},"authenticationInfo":{"principalEmail":"[email protected]","serviceAccountDelegationInfo":[{"firstPartyPrincipal":{"principalEmail":"[email protected]"}}],"principalSubject":"serviceAccount:[email protected]"},"requestMetadata":{"callerIp":"34.72.217.225","callerSuppliedUserAgent":"(gzip),gzip(gfe)","requestAttributes":{"time":"2024-12-03T17:58:45.019694350Z","auth":{}},"destinationAttributes":{}},"serviceName":"iam.googleapis.com","methodName":"google.iam.admin.v1.CreateServiceAccount","authorizationInfo":[{"resource":"projects/ketchup","permission":"iam.serviceAccounts.create","resourceAttributes":{"type":"iam.googleapis.com/ServiceAccount"},"permissionType":"ADMIN_WRITE"}],"resourceName":"projects/ketchup","request":{"service_account":{"display_name":"This is the attacker account"},"account_id":"theattacker","name":"projects/ketchup","@type":"type.googleapis.com/google.iam.admin.v1.CreateServiceAccountRequest"},"response":{"@type":"type.googleapis.com/google.iam.admin.v1.ServiceAccount"}},"receiveTimestamp":"2024-12-03T17:58:45.716564605Z","resource":{"type":"service_account","labels":{"unique_id":"","project_id":"ketchup","email_id":""}},"timestamp":"2024-12-03T17:58:44.882119699Z","severity":"ERROR"}
2 changes: 1 addition & 1 deletion tests/parsers/jsonl_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def testParse(self):

number_of_event_data = storage_writer.GetNumberOfAttributeContainers(
'event_data')
self.assertEqual(number_of_event_data, 10)
self.assertEqual(number_of_event_data, 11)

number_of_warnings = storage_writer.GetNumberOfAttributeContainers(
'extraction_warning')
Expand Down
61 changes: 60 additions & 1 deletion tests/parsers/jsonl_plugins/gcp_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def testProcess(self):

number_of_event_data = storage_writer.GetNumberOfAttributeContainers(
'event_data')
self.assertEqual(number_of_event_data, 10)
self.assertEqual(number_of_event_data, 11)

number_of_warnings = storage_writer.GetNumberOfAttributeContainers(
'extraction_warning')
Expand Down Expand Up @@ -133,6 +133,65 @@ def testComputeInstancesInsert(self):
event_data = storage_writer.GetAttributeContainerByIndex('event_data', 9)
self.CheckEventData(event_data, expected_event_values)

def testServiceAccountCreateFailure(self):
"""Tests service account creation failure log."""
plugin = gcp_log.GCPLogJSONLPlugin()
storage_writer = self._ParseJSONLFileWithPlugin(
['gcp_logging.jsonl'], plugin)

expected_event_values = {
'caller_ip': '34.72.217.225',
'container': None,
'dcsa_emails': None,
'dcsa_scopes': None,
'delegation_chain': ('[email protected].'
'gserviceaccount.com'),
'event_subtype': 'google.iam.admin.v1.CreateServiceAccount',
'event_type': None,
'filename': None,
'firewall_rules': None,
'firewall_source_ranges': None,
'gcloud_command_identifier': None,
'gcloud_command_pattern': None,
'log_name': ('projects/ketchup/logs/cloudaudit.googleapis.com%2F'
'activity'),
'message': None,
'method_name': 'google.iam.admin.v1.CreateServiceAccount',
'permissions': ['iam.serviceAccounts.create'],
'policy_deltas': None,
'principal_email': ('[email protected].'
'gserviceaccount.com'),
'principal_subject': ('serviceAccount:dvwa-service-account@ketchup.'
'iam.gserviceaccount.com'),
'recorded_time': '2024-12-03T17:58:44.882119+00:00',
'request_account_identifier': 'theattacker',
'request_address': None,
'request_description': None,
'request_direction': None,
'request_email': None,
'request_member': None,
'request_metadata': None,
'request_name': 'projects/ketchup',
'request_target': None,
'request_labels': None,
'resource_name': 'projects/ketchup',
'service_account_delegation': [
'[email protected]'],
'service_account_display_name': 'This is the attacker account',
'service_account_key_name': None,
'service_name': 'iam.googleapis.com',
'severity': 'ERROR',
'source_images': None,
'status_code': '7',
'status_message': ('Permission "iam.serviceAccounts.create" denied on'
' resource (or it may not exist).'),
'status_reasons': ['IAM_PERMISSION_DENIED'],
'text_payload': None,
'user_agent': '(gzip),gzip(gfe)',
}

event_data = storage_writer.GetAttributeContainerByIndex('event_data', 10)
self.CheckEventData(event_data, expected_event_values)

if __name__ == '__main__':
unittest.main()

0 comments on commit a333753

Please sign in to comment.