综述:多个 Eclipse 存储库容易受到通过代码注入的中毒管道执行 (PPE) 的攻击,从而允许恶意行为者以组织写入权限泄露 Eclipse 的个人访问令牌。
问题 1:通过 PR 分支名称注入代码publishVersionCheckResults.yml
(GHSL-2024-320
)
工作流完成时,将执行 、 和 存储库中存在的 version-increments.yml
工作流:eclipse-jdt/eclipse.jdt.core
eclipse-jdt/eclipse.jdt.ui
eclipse-jdt/eclipse.jdt.debug
Pull-Request Checks
name: Publish Version Check Results
on:
workflow_run:
workflows: ["Pull-Request Checks"]
types: [completed]
然后,它从存储库中调用可重用工作流,并将密钥传递给它:publishVersionCheckResults.yml
eclipse-platform/eclipse.platform.releng.aggregator
JDT_BOT_PAT
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 }}
publishVersionCheckResults.yml
工作流将检查触发工作流中是否提供了调用的工件:versions-git-patch
- 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
如果存在,它将签出触发 Pull Request 的代码,下载构件并创建补丁以应用于 Pull Request 源存储库:
- 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 }}'
在最后一行中,攻击者控制的 Pull Request 分支名称在 bash 代码中插入。由于分支名称可以包含反引号和美元符号,因此攻击者可以从调用该命令的分支创建拉取请求,从而允许攻击者在 GitHub 运行程序上运行任意命令,并通过转储运行程序的内存来泄露。foo`CMD$IFSARG1$IFSARG2`bar
CMD ARG1 ARG2
JDT_BOT_PAT
冲击
此问题可能会导致 Personal Access Token 外泄。由于此 PAT 用于三个不同的存储库,因此它似乎是在组织级别定义的令牌,并且可能在组织级别具有写入访问权限。JDT_BOT_PAT
问题 2:通过 PR 进行代码注入更改了 中的文件名publishVersionCheckResults.yml
(GHSL-2024-321
)
publishVersionCheckResults.yml
工作流程从触发的 Pull Request 中签出不受信任的代码:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
if: steps.search-patch.outputs.result
with:
ref: "${{ github.event.workflow_run.head_sha }}"
persist-credentials: false #Opt out from persisting the default Github-token authentication in order to enable use of the bot's PAT when pushing below
由于攻击者可以上传文件,因此他们将能够控制命令返回的文件列表:version_increments.patch
git diff-tree
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
此攻击者控制的名称列表被分配给输出变量,然后插值到 JS 脚本中,从而允许攻击者执行任意代码:steps.git-commit.outputs.file-list
- 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