Coordinated Disclosure Timeline

Summary

Apache Superset is vulnerable to a Poisoned Pipeline Execution (PPE) attack which may lead to a full compromise of the apache/superset repository.

Project

Apache Superset

Tested Version

Latest commit at the time of reporting

Details

Poisoned Pipeline Execution (PPE) in ephemeral-env.yml (GHSL-2024-208)

The ephemeral-env.yml workflow is triggered when a Pull Request gets commented.

This workflow first checks if certain secrets are available to the runner:

  config:
    runs-on: "ubuntu-22.04"
    if: github.event.issue.pull_request
    outputs:
      has-secrets: ${{ steps.check.outputs.has-secrets }}
    steps:
      - name: "Check for secrets"
        id: check
        shell: bash
        run: |
          if [ -n "${{ (secrets.AWS_ACCESS_KEY_ID != '' && secrets.AWS_SECRET_ACCESS_KEY != '') || '' }}" ]; then
            echo "has-secrets=1" >> "$GITHUB_OUTPUT"
          fi

According to past executions, these secrets are available.

It then process the body of the comment. If the comment does not match the /^\/testenv (up|down)/ regular expression, it will return noop. Because of this, the “Limit to committers” step does not get executed and therefore the job wont fail, enabling the execution of the following job: “ephemeral-docker-build”.

    - name: Limit to committers
      if: >
        steps.eval-body.outputs.result != 'noop' &&
        github.event.comment.author_association != 'MEMBER' &&
        github.event.comment.author_association != 'OWNER'
      uses: actions/github-script@v7
      with:
        github-token: ${{github.token}}
        script: |
          const errMsg = '@${{ github.event.comment.user.login }} Ephemeral environment creation is currently limited to committers.'
          github.rest.issues.createComment({
            issue_number: ${{ github.event.issue.number }},
            owner: context.repo.owner,
            repo: context.repo.repo,
            body: errMsg
          })
          core.setFailed(errMsg)

The ephemeral-docker-build job will collect some data from the triggering Pull Request:

      - name: Get Info from comment
        uses: actions/github-script@v7
        id: get-pr-info
        with:
          script: |
            const request = {
                owner: context.repo.owner,
                repo: context.repo.repo,
                pull_number: ${{ github.event.issue.number }},
            }
            core.info(`Getting PR #${request.pull_number} from ${request.owner}/${request.repo}`)
            const pr = await github.rest.pulls.get(request);
            return pr.data;

And set the Pull Request HEAD SHA to a step output variable:

      - name: Debug
        id: get-sha
        run: |
          echo "sha=${{ fromJSON(steps.get-pr-info.outputs.result).head.sha }}" >> $GITHUB_OUTPUT

The HEAD of the Pull Request gets checked-out:

      - name: "Checkout ${{ github.ref }} ( ${{ github.sha }} : ${{steps.get-sha.outputs.sha}} )"
        uses: actions/checkout@v4
        with:
          ref: ${{ steps.get-sha.outputs.sha }}
          persist-credentials: false

Finally, the ./scripts/build_docker.py checked-out script and, therefore, attacker controlled gets executed:

      - name: Build ephemeral env image
        run: |
          ./scripts/build_docker.py \
            "ci" \
            "pull_request" \
            --build_context_ref ${{ github.event.issue.number }}

An attacker would be able to open a Pull Request with a malicious ./scripts/build_docker.py and add a comment to the Pull Request to trigger the workflow and gain code execution.

Impact

This issue may lead to secret leak (AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY) and full write access to the repository.

Credit

This issue was 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-208 in any communication regarding this issue.