Multiple repositories are vulnerable to Poisoned Pipeline Execution (PPE)
Basic information
Project name: eclipse-jdt/eclipse.jdt.core, eclipse-jdt/eclipse.jdt.ui, and eclipse-jdt/eclipse.jdt.debug GitHub repositories
Project id:
What are the affected versions?
Latest commit at the time of reporting.
Summary
eclipse-jdt/eclipse.jdt.core, eclipse-jdt/eclipse.jdt.ui, and eclipse-jdt/eclipse.jdt.debug repositories are vulnerable to Poisoned Pipeline Execution (PPE) via Code Injection allowing a malicious actor to exfiltrate the JDT_BOT_PAT Personal Access Token with organization write permission.
Details
Issue 1: Code Injection via PR branch name in publishVersionCheckResults.yml (GHSL-2024-320)
The version-increments.yml workflow present in the eclipse-jdt/eclipse.jdt.core, eclipse-jdt/eclipse.jdt.ui, and eclipse-jdt/eclipse.jdt.debug repositories get executed when the Pull-Request Checks workflow completes:
name: Publish Version Check Results
on:
workflow_run:
workflows: [ 'Pull-Request Checks' ]
types: [ completed ]
It then calls the publishVersionCheckResults.yml reusable workflow from the eclipse-platform/eclipse.platform.releng.aggregator repository and passes the JDT_BOT_PAT secret to it:
jobs:
publish-version-check-results:
uses: eclipse-platform/eclipse.platform.releng.aggregator/.github/workflows/publishVersionCheckResults.yml@master
with:
botGithubId: eclipse-jdt-bot
secrets:
githubBotPAT: ${{ secrets.JDT_BOT_PAT }}
The publishVersionCheckResults.yml workflow will check if an artifact called versions-git-patch is available from the triggering workflow:
- name: Search version increment git patch
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
id: search-patch
with:
script: |
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
run_id: context.payload.workflow_run.id,
...context.repo
})
let artifact = allArtifacts.data.artifacts.find(artifact => artifact.name == 'versions-git-patch')
return artifact?.id
If it exists, it will checkout the code of the triggering Pull request, download the artifact and create a patch to apply to the Pull Request origin repository:
- name: Apply and push version increment
id: git-commit
if: steps.search-patch.outputs.result
run: |
set -x
# Set initial placeholder name/mail and read it from the patch later
git config --global user.email 'foo@bar'
git config --global user.name 'Foo Bar'
git am version_increments.patch
# Read the author's name+mail from the just applied patch and recommit it with both set as committer
botMail=$(git log -1 --pretty=format:'%ae')
botName=$(git log -1 --pretty=format:'%an')
git config --global user.email "${botMail}"
git config --global user.name "${botName}"
git commit --amend --no-edit
fileList=$(git diff-tree --no-commit-id --name-only HEAD -r)
echo "file-list<<EOF" >> $GITHUB_OUTPUT
echo "$fileList" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
git push \
"https://oauth2:${BOT_PA_TOKEN}@github.com/${{ github.event.workflow_run.head_repository.full_name }}.git" \
'HEAD:refs/heads/${{ github.event.workflow_run.head_branch }}'
In the last line, the attacker-controlled Pull Request branch name gets interpolated in the bash code. Since branch names can contain backticks and dollar symbols, it is possible for an attacker to create a Pull Request from a branch called foo`CMD$IFSARG1$IFSARG2`bar which will run the CMD ARG1 ARG2 command, allowing the attacker to run arbitrary commands on the GitHub runner and exfiltrate the JDT_BOT_PAT by dumping the runner's memory.
Impact
This issue may lead to the exfiltration of the JDT_BOT_PAT Personal Access Token. Since this PAT is used in three different repositories, it seems like its a token defined at the organization level and that could have write access at the org level.
Remediation
Pass the branch name as an environment variable:
- name: Apply and push version increment
id: git-commit
if: steps.search-patch.outputs.result
env:
BRANCH_NAME: ${{ github.event.workflow_run.head_branch }}
REPO_NAME: ${{ github.event.workflow_run.head_repository.full_name }}
run: |
...
git push \
"https://oauth2:${BOT_PA_TOKEN}@github.com/${REPO_NAME}.git" \
'HEAD:refs/heads/${BRANCH_NAME}}'
Issue 2: Code Injection via PR changed file names in publishVersionCheckResults.yml (GHSL-2024-321)
Similarly, the publishVersionCheckResults.yml workflow extracts a list of files changed by the Pull Request:
fileList=$(git diff-tree --no-commit-id --name-only HEAD -r)
echo "file-list<<EOF" >> $GITHUB_OUTPUT
echo "$fileList" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
This list of attacker-controlled names is assigned to the steps.git-commit.outputs.file-list output variable and later interpolated into a JS script allowing an attacker to gain arbitrary code execution:
- name: Add or update information comment
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
if: always()
with:
github-token: ${{ secrets.githubBotPAT }}
script: |
const fs = require('fs')
const fileList = `${{ steps.git-commit.outputs.file-list }}`
if (fileList) { // if list is empty, no versions were changed
...
Impact
This issue may lead to the exfiltration of the JDT_BOT_PAT Personal Access Token. Since this PAT is used in three different repositories, it seems like its a token defined at the organization level and that could have write access at the org level.
Remediation
Pass the steps.git-commit.outputs.file-list variable as an environment variable to the JS script to prevent code injection:
- name: Add or update information comment
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
if: always()
env:
FILELIST: ${{ steps.git-commit.outputs.file-list }}
with:
github-token: ${{ secrets.githubBotPAT }}
script: |
const fs = require('fs')
const fileList = process.env.FILELIST
if (fileList) { // if list is empty, no versions were changed
...
GitHub Security Advisories
We recommend you create a private GitHub Security Advisory for these findings. This also allows you to invite the GHSL team to collaborate and further discuss these findings in private before they are published.
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-320 or GHSL-2024-321 in any communication regarding these issues.
Disclosure Policy
This report is subject to a 90-day disclosure deadline, as described in more detail in our coordinated disclosure policy.