Skip to content

fix(go): stop eval'ing GIT_CONFIG_VAL as shell#1098

Open
evilgensec wants to merge 1 commit into
GoogleCloudPlatform:masterfrom
evilgensec:fix/go-prepare-workspace-eval-injection
Open

fix(go): stop eval'ing GIT_CONFIG_VAL as shell#1098
evilgensec wants to merge 1 commit into
GoogleCloudPlatform:masterfrom
evilgensec:fix/go-prepare-workspace-eval-injection

Conversation

@evilgensec
Copy link
Copy Markdown

Summary

go/prepare_workspace.inc:18 runs:

eval "git config --global $GIT_CONFIG_VAL"

Any \$(...), backtick, ;, &&, or || embedded in GIT_CONFIG_VAL is interpreted by the shell when this .inc is sourced by the gcr.io/cloud-builders/go step. Because gcr.io/cloud-builders/go is the canonical Go builder for Cloud Build, any pipeline that lets GIT_CONFIG_VAL be templated from an input the attacker can influence (PR-trigger substitutions, webhook payloads, branch/tag names, derived env) ends up running attacker shell as root inside the build with the Cloud Build service-account token reachable from the metadata server.

The eval was never necessary: git config --global <key> <value> accepts positional args. PR #500 (Jun 2019) introduced this knob and used eval to handle shell-quoted values such as url."git@github.com:".insteadOf "https://github.com/". We can keep that behavior — including quote handling — by piping through xargs, which tokenizes the value into argv positions without re-evaluating shell metacharacters.

Reproduction (before this PR)

Verified against the live gcr.io/cloud-builders/go:latest image (digest sha256:e8c442f3eaa523a6a190bec463b29e82193a1ca4e36b9de69301fb4d2e1cee0e):

$ mkdir -p /tmp/poc && cd /tmp/poc && printf 'package main\nfunc main(){}\n' > main.go

$ docker run --rm \
    --env 'GIT_CONFIG_VAL=user.email "x@y" && touch /workspace/RCE && id > /workspace/RCE_ID' \
    -v "$PWD:/workspace" -w /workspace \
    gcr.io/cloud-builders/go version

$ cat /tmp/poc/RCE_ID
uid=0(root) gid=0(root) groups=0(root),...

In Cloud Build the equivalent payload exfiltrates http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token to an attacker-controlled URL.

Reproduction (after this PR)

Re-running the same docker run against the patched image:

$ ls /tmp/poc/RCE /tmp/poc/RCE_ID
ls: cannot access '/tmp/poc/RCE': No such file or directory
ls: cannot access '/tmp/poc/RCE_ID': No such file or directory

git config rejects the literal && token (usage: git config [<options>]) and the attacker's touch / id never run.

Legitimate usage with quoted values is preserved:

$ GIT_CONFIG_VAL='url."git@github.com:".insteadOf "https://github.com/"'
$ printf '%s' "$GIT_CONFIG_VAL" | xargs git config --global
$ cat /root/.gitconfig
[url "git@github.com:"]
        insteadOf = https://github.com/

Test plan

  • Verified RCE against unpatched gcr.io/cloud-builders/go:latest.
  • Built a patched image (FROM gcr.io/cloud-builders/go:latest + COPY of the fixed .inc) and re-ran the PoC: injection is now blocked.
  • Verified the url."git@github.com:".insteadOf "https://github.com/" legitimate-usage shape continues to set the expected ~/.gitconfig entry.
  • Works in both the bash (go.bash) and ash (go.ash) entrypoint variants, since printf+xargs are POSIX.

go/prepare_workspace.inc passed `$GIT_CONFIG_VAL` through `eval`, so any
$(...), backtick, ;, &&, || embedded in the value executed as shell
when the gcr.io/cloud-builders/go step ran. That image is the canonical
Go builder for Cloud Build; once a pipeline lets GIT_CONFIG_VAL be
templated from any input the attacker can influence (PR-trigger
substitutions, webhook payloads, branch/tag names, derived env), the
embedded command runs as root inside the build step with the Cloud
Build service-account token available from the metadata server.

Verified against the live gcr.io/cloud-builders/go:latest image
(digest sha256:e8c442f3eaa523a6a190bec463b29e82193a1ca4e36b9de69301fb4d2e1cee0e):
setting GIT_CONFIG_VAL='user.email "x@y" && touch /workspace/RCE'
created /workspace/RCE in the host bind-mount. The fix swaps the eval
for `printf '%s' "$GIT_CONFIG_VAL" | xargs git config --global`, which
tokenizes the value into argv positions while preserving legitimate
quoted forms (verified that
GIT_CONFIG_VAL='url."git@github.com:".insteadOf "https://github.com/"'
still produces the expected gitconfig entry after the fix).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant