Skip to content

Latest commit

 

History

History
119 lines (68 loc) · 6.13 KB

File metadata and controls

119 lines (68 loc) · 6.13 KB

Path Traversal via Symlink Bypass in Google Cloud Shell cloudshell Utility Allowing Arbitrary File Exposure

Google Cloud Shell includes an inline command-line utility named cloudshell, implemented as a Python script located at /usr/local/bin/cloudshell. The utility provides commands such as open-workspace and edit-files which require that workspaces and file paths remain inside the user's $HOME directory.

1

To enforce this restriction, the application validates that the resolved path does not contain ".." relative traversal.

2

However, the validation only uses os.path.abspath() and os.path.relpath(), and does not validate whether the input path is a symlink pointing outside the user's home directory.

3

Because symlinks are not checked, an attacker can create a symbolic link inside $HOME that points to any directory on the underlying host (e.g., /, /etc, /var). When the symlink is passed as the workspace argument or to cloudshell_open_in_editor, the path validation is bypassed and the Cloud Shell editor opens arbitrary system files.

This vulnerability is locally exploitable, but becomes remotely triggerable via the "Open in Cloud Shell" feature, where a malicious Git repo containing crafted symlinks can be loaded automatically through a single URL. When combined with "Trust repo" the attacker can cause Cloud Shell to open internal system files, including sensitive configuration files and secrets.

Attack Preconditions

  • The victim must open a malicious Open in Cloud Shell link or run a cloudshell open-workspace/cloudshell edit-files command referencing the attacker-controlled symlink.
  • For higher-impact exploitation, the victim must click "Trust repo", which disables ephemeral mode and grants access to user-specific and host-specific files.

Reproduction Steps / POC

== Local PoC (Cloud Shell Terminal) ==

Step 1 - Create a symlink to /etc

ln -s /etc/ local_etc
ls -al local_etc

Step 2 - Trigger the bypass using open-workspace

cloudshell open-workspace /home/$USER/ local_etc/passwd

Cloud Shell editor now opens /etc as the workspace and /etc/passwd as the file tab.

4

== REMOTE EXPLOITATION (Open in Cloud Shell one-click attack) ==

Step 3 - Prepare malicious repository (https://github.com/win3zz/root_symlink)

mkdir myproject
ln -s / myproject/root
cd myproject
git init
git add root
git commit -m "add symlink to /"
git remote add origin https://github.com/win3zz/root_symlink.git
git branch -M main
git push -u origin main

Step 4 - Send "Open in Cloud Shell" Link

Example malicious Link:

Open in Cloud Shell

Opening the link automatically opens all these system files inside the Cloud Shell editor.

5

Step 5 - Higher-impact scenario when user clicks "Trust Repo"

When a user checks "Trust Repo" Ephemeral Mode is disabled and real user filesystem becomes accessible.

Example Link:

Open in Cloud Shell

6

The file /var/config/shared-secret/shared-secret (internal Cloud Shell shared secret) is successfully opened.

7 - REDACT

Who can exploit it?

Any user capable of sending a malicious "Open in Cloud Shell" link or a repository with crafted symlinks. No elevated permissions are required. Exploitation occurs in the context of the victim's Cloud Shell session.

What do they gain?

  • Ability to bypass path restrictions and read any arbitrary file on the Cloud Shell host.
  • Potential leakage of internal system secrets if the victim trusts the repo.
  • The vulnerability could enable more severe exploit chains if combined with other vulnerabilities in the Cloud Shell editor or environment.

In isolation, the impact is limited to unauthorized file reads. However, when chained with other editor or environment-level vulnerabilities, the impact can increase significantly (e.g., privilege escalation, sensitive data disclosure, session compromise).

Mitigation

Before performing path checks, fully resolve symlinks:

ws_real = os.path.realpath(args.workspace)
home_real = os.path.realpath(os.path.expanduser('~'))

if not ws_real.startswith(home_real + os.sep):
    abort("Workspace path must be within the user's home directory")

Unlike abspath(), realpath() normalizes and resolves symlinks. Also, disallow dereferencing symlinks that point outside the cloned repo or $HOME.