Cloudflare Pages, part 1: The fellowship of the secret
Five vulnerabilities in Cloudflare Pages across 3 blog posts. Three vulns are command injection, one is a container escape, and one is a lack of access control.
**Part 1 - Two command injections in CLONE_REPO
and PUBLISH_ASSETS
build steps
They focused their early reserach on azure pipelines, and the build_tool
python script that would get ran by the workflow. The CLONE_REPO
and PUBLISH_ASSETS
steps were interesting in particular as they were passed the GITHUB_PROD_PRIVATE_KEY
and CF_PROD_API_TOKEN
through the environment. The CLONE_REPO
step would use a user-controllable root_dir
parameter to build up a path that gets passed into a mv
command. This is done purely with string concatenation and is vulnerable to command injection. This same issue existed in the PUBLISH_ASSETS
step with the asset path.
Exploiting these issues allowed them to escalate privileges from the unprivileged buildbot
user to AzDevops
, which was effectively root (inside the container), as AzDevops
had passwordless sudo access. Furthermore, with the ability to dump the environment, they got the github and cloudflare API private keys. It turns out the cloudflare API token was not scoped to just that project, but had global access. This allowed them to get access to the repositories of all 18k users of Cloudflare pages!
Part 2 - Container escape/jailbreak
In part 2, they found another command injection directly in the workflow through the account_env
variable. They try to pass this variable to the build script with no special care against command injection, again giving them root access inside the container. They wanted to take it further to escape the container, and discovered docker was being used. They also found that the docker socket was accessible from inside the container via /var/run/docker.socket
.
From there, they were able to use docker to create a privileged container that used the host namespace and mounted the host filesystem inside the container, which they could use to list all cloudflare users and access build history for all users.
Part 3 - Revisit after infrastructure overhaul After many months, assetnote received a notice from cloudflare that their new pages environment could be opt’d into for testing. The environment was very different, not even using Azure pipelines anymore and moving to GKE / Kubernetes. Furthermore, the build scripts are no longer readable, and a lot of previous privesc vectors were killed by GKE and gVisor’s stronger sandboxing.
They discovered an internal kubelet API endpoint running on :10255 after some port scanning and API enumeration. This endpoint was accessible to unprivileged, and contained the git access token. Unlike before though, this token only works for that organization’s repositories, so impact of this issue is much lower.