CyberDefenders - GoogleCloudHunt
Learn cloud forensics by analyzing Google Cloud logs with JQ to identify compromised accounts, data exfiltration, and attacker persistence methods in a simulated breach scenario.
Intro Text
A wide credential breach has recently affected multiple organizations across various sectors. This incident has raised concerns about potential unauthorized access to your organization operating within the Google Cloud environment. As a cybersecurity analyst, your task is to conduct a thorough investigation to determine the extent of the compromise.
A link to the lab can be found here: https://cyberdefenders.org/blueteam-ctf-challenges/googlecloudhunt/
I'm not as experienced as I want to be when analyzing cloud logs, so hopefully this lab will be great for me! 😁
Questions
-
We need to determine if a user has fallen victim to the credential breach, which user account was compromised?
I am really not great with
jq, so before jumping into it, I decided to look into the logs just to familiarize myself with the data, and I found that there was an e-mail field inside theautenticationInfoobject. After poking around some jq cheatsheets, I was able to come up with the following command, to extract all the e-mails from the data, and count how many times they appear, as an high occurrence rate could imply that an account was bruteforced.ubuntu@ip-172-31-43-34:~/Desktop/Start here/Artifacts$ jq '.[] | .protoPayload.authenticationInfo.principalEmail' logs.json | sort | uniq -c 13 "cloudops-service@hybrid-elixir-370815.iam.gserviceaccount.com" 1070 "david.smith8391273718@gmail.com" 114 "john.doe4768219831@gmail.com" 99 "laura.brown4234252341@gmail.com" 107 "sarah.johnson62342721@gmail.com" 19 null -
In the attacker's initial exploration of the environment, what is the name of the first Google Cloud Storage bucket they accessed?
To do this, we need to filter the logs for ones that contain our newfound compromised user.
ubuntu@ip-172-31-43-34:~/Desktop/Start here/Artifacts$ jq '.[] | select(.protoPayload.authenticationInfo.principalEmail == "david.smith8391273718@gmail.com")' logs.json | head { "protoPayload": { "@type": "type.googleapis.com/google.cloud.audit.AuditLog", "authenticationInfo": { "principalEmail": "david.smith8391273718@gmail.com", "principalSubject": "user:david.smith8391273718@gmail.com" }, "requestMetadata": { "callerIp": "45.131.195.179", "callerSuppliedUserAgent": "google-cloud-sdk gcloud/455.0.0 command/gcloud.compute.instances.list invocation-id/0ed959263ba942f9a58dad7933a7454f environment/None environment-version/None client-os/LINUX client-os-ver/6.3.0 client-pltf-arch/x86_64 interactive/True from-script/True python/3.11.6 term/xterm-256color (Linux 6.3.0-kali1-amd64),gzip(gfe)",But this isn't enough, we need to find the field that contains the bucket names. Luckily for me, it was easy to find when just reading the logs.
"resource": { "type": "gcs_bucket", "labels": { "location": "global", "project_id": "hybrid-elixir-370815", "bucket_name": "" } },Armed with this, we can make a query that tells us the name of the bucket that was accessed by the attacker.
ubuntu@ip-172-31-43-34:~/Desktop/Start here/Artifacts$ jq '.[] | select(.protoPayload.authenticationInfo.principalEmail == "david.smith8391273718@gmail.com") | select (.resource.labels.bucket_name != null) | select (.resource.labels.bucket_name != "") .resource.labels.bucket_name' logs.json "confidential-documents-482374561" "confidential-documents-482374561"The query became very long due to me repeating the field names, but if it works it works 😅.
-
Considering the attacker's focus on data exfiltration, what's the name of the object they may have exfiltrated within the first accessed bucket?
ubuntu@ip-172-31-43-34:~/Desktop/Start here/Artifacts$ jq '.[] | select(.protoPayload.authenticationInfo.principalEmail == "david.smith8391273718@gmail.com") | select (.resource.labels.bucket_name == "confidential-documents-482374561")| .protoPayload.resourceName' logs.json "projects/_/buckets/confidential-documents-482374561" (...) "projects/_/buckets/confidential-documents-482374561/objects/Financial_Report_2023_Classified.pdf"We can see that the attacker accessed
Financial_Report_2023_Classified.pdffrom that bucket. -
Knowing each step the attacker takes is crucial. What's the name of the Compute Engine instance the attacker accessed?
After bashing my head with some jq queries, I was able to search for the resource name.
ubuntu@ip-172-31-43-34:~/Desktop/Start here/Artifacts$ jq '.[] | select(.protoPayload.authenticationInfo.principalEmail == "david.smith8391273718@gmail.com" and .protoPayload.serviceName == "compute.googleapis.com" and select(.protoPayload.authorizationInfo[].resource | contains("instance"))) | .protoPayload.authorizationInfo[].resource' logs.json "projects/hybrid-elixir-370815/zones/us-central1-a/instances/monitoring-instance" "projects/hybrid-elixir-370815/zones/us-central1-a/instances/monitoring-instance" "projects/hybrid-elixir-370815/zones/us-central1-a/instances/monitoring-instance" "projects/hybrid-elixir-370815/zones/us-central1-a/instances/monitoring-instance" "projects/hybrid-elixir-370815/zones/us-central1-a/disks/monitoring-instance" "projects/hybrid-elixir-370815/zones/us-central1-a/disks/monitoring-instance" "projects/hybrid-elixir-370815/zones/us-central1-a/instances/monitoring-instance" "projects/hybrid-elixir-370815/zones/us-central1-a/instances/monitoring-instance" "projects/hybrid-elixir-370815/zones/us-central1-a/instances/monitoring-instance" "projects/hybrid-elixir-370815/zones/us-central1-a/instances/monitoring-instance" "projects/hybrid-elixir-370815/zones/us-central1-a/instances/monitoring-instance" "projects/hybrid-elixir-370815/zones/us-central1-a/disks/monitoring-instance" "projects/hybrid-elixir-370815/zones/us-central1-a/instances/analytics-instance" "projects/hybrid-elixir-370815/zones/us-central1-a/instances/analytics-instance" -
What service account is used by the Compute instance for calls to Google Cloud APIs?
I filtered the logs to get everything associated with the Compute Engine, and because the question is asking for a different e-mail, i just filtered for e-mails different to the attacker one 😛
ubuntu@ip-172-31-43-34:~/Desktop/Start here/Artifacts$ jq '.[] | select(.protoPayload.authenticationInfo.principalEmail != "david.smith8391273718@gmail.com" and .protoPayload.serviceName == "compute.googleapis.com" and select(.protoPayload.authorizationInfo[].resource == "projects/hybrid-elixir-370815/zones/us-central1-a/instances/monitoring-instance"))' logs.json { "protoPayload": { "@type": "type.googleapis.com/google.cloud.audit.AuditLog", "authenticationInfo": { "principalEmail": "cloudops-service@hybrid-elixir-370815.iam.gserviceaccount.com", "serviceAccountDelegationInfo": [ { "firstPartyPrincipal": { "principalEmail": "service-137088779611@compute-system.iam.gserviceaccount.com" } } ], -
In the process of exfiltrating data, identify the Google Cloud SQL database the attacker attempted to export. What is the database's name?
Grepping the data for sql, the last instance found contains a method called "cloudsql.instances.export", so filtering for that, we can see the databases that were exported.
ubuntu@ip-172-31-43-34:~/Desktop/Start here/Artifacts$ jq '.[] | select(.protoPayload.methodName == "cloudsql.instances.export")' logs.json (...) }, "insertId": "qxdup1e4bto4", "resource": { "type": "cloudsql_database", "labels": { "region": "us-central1", "database_id": "hybrid-elixir-370815:analytics-db", "project_id": "hybrid-elixir-370815" } }, "timestamp": "2023-12-06T16:42:16.945774Z", "severity": "ERROR", "logName": "projects/hybrid-elixir-370815/logs/cloudaudit.googleapis.com%2Fdata_access", "receiveTimestamp": "2023-12-06T16:42:18.045764393Z" } -
Tracking the data movement, which Google Cloud Storage bucket did the attacker attempt to export the database to?
Actually using the same query as before we can see the answer to this question 😭 I got hella lucky, inside the data there was this:
"resourceName": "projects/hybrid-elixir-370815/instances/analytics-db", "request": { "instance": "analytics-db", "@type": "type.googleapis.com/google.cloud.sql.v1beta4.SqlInstancesExportRequest", "project": "hybrid-elixir-370815", "body": { "exportContext": { "kind": "sql#exportContext", "uri": "gs://backup-repository-543268313682/database_backup.gz", "offload": false, "sqlExportOptions": {}, "fileType": "SQL" } } } }, -
In the attacker's effort to maintain access, identify the new service account created by the attacker. What is the account ID of this service account?
I grepped the logs for create and there seems to be a method called
google.iam.admin.v1.CreateServiceAccount. Filtering for that, we can see that the account iscloud-ops-service.ubuntu@ip-172-31-43-34:~/Desktop/Start here/Artifacts$ jq '.[] | select(.protoPayload.methodName == "google.iam.admin.v1.CreateServiceAccount")' logs.json (...) "request": { "account_id": "cloud-ops-service", "service_account": { "display_name": "cloud-ops-service" }, "@type": "type.googleapis.com/google.iam.admin.v1.CreateServiceAccountRequest", "name": "projects/hybrid-elixir-370815" }, (...) -
As part of establishing persistence, what is the ID of the secret key generated for the newly created service account?
If we grep the logs for "key", we can find the
google.iam.admin.v1.CreateServiceAccountKeymethod, so using that as a filter we can find the key!ubuntu@ip-172-31-43-34:~/Desktop/Start here/Artifacts$ jq '.[] | select(.protoPayload.methodName == "google.iam.admin.v1.CreateServiceAccountKey")' logs.json "response": { "key_algorithm": 2, "key_type": 1, "name": "projects/hybrid-elixir-370815/serviceAccounts/cloud-ops-service@hybrid-elixir-370815.iam.gserviceaccount.com/keys/16569af4fb98194fc210770d353a2513bada24b3", "valid_before_time": { "seconds": 253402300799 }, }
Conclusion
This is a pretty good lab to introduce us to some Google Cloud Log Analysis!
Best Regards,
Bernardo