Coordinated Disclosure Timeline
- 2024-09-30: Reported through GitHub’s Private Vulnerability Reporting (PVR).
- 2024-11-11: Fix commit.
- 2024-11-20: Proposed security in-depth fix.
Summary
Cilium push-chart-ci.yaml
workflow is vulnerable to a Poisoned Pipeline Execution (PPE) attack which may lead to the exfiltration of the QUAY_CHARTS_DEV_PASSWORD
and QUAY_CHARTS_DEV_USERNAME
secrets. Additionally, it is also vulnerable to Cache Poisoning attack which may allow an attacker to gain elevated privileges in a different workflow.
Project
Cilium
Tested Version
Latest commit at the time of reporting.
Details
Issue 1: Secret leak (GHSL-2024-226
)
The push-chart-ci.yaml
workflow gets triggered when the Image CI Build
or Hot Fix Image Release Build
finish:
on:
# run after the image build completes
workflow_run:
workflows:
- Image CI Build
- Hot Fix Image Release Build
types:
- completed
When that happens successfully, the push-charts
gets executed:
jobs:
push-charts:
name: Push Charts
runs-on: ubuntu-22.04
# we also check for push events in case someone is testing the workflow by uncommenting the push trigger above.
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' || github.event_name == 'push' }}
In this scenario, the get-ref
step will load the triggering Pull Request HEAD SHA into the steps.get-ref.outputs.ref
variable ([1]):
- name: Get triggering event ref
id: get-ref
run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo ref="${{ inputs.checkout_ref }}" >> $GITHUB_OUTPUT
echo sha="${{ inputs.checkout_ref }}" >> $GITHUB_OUTPUT
elif [[ "${{ github.event_name }}" == "workflow_run" ]]; then
if [[ "${{ github.event.workflow_run.head_repository.fork }}" == "true" ]]; then
# use the SHA on forks since the head_branch won't exist in the upstream repository
echo ref="${{ github.event.workflow_run.head_sha }}" >> $GITHUB_OUTPUT # <--------- [1]
else
echo ref="${{ github.event.workflow_run.head_branch }}" >> $GITHUB_OUTPUT
fi
echo sha="${{ github.event.workflow_run.head_sha }}" >> $GITHUB_OUTPUT
elif [[ "${{ github.event_name }}" == "push" ]]; then
echo ref="${{ github.ref }}" >> $GITHUB_OUTPUT
echo sha="${{ github.sha }}" >> $GITHUB_OUTPUT
else
echo "Invalid event type"
exit 1
fi
After that, the workflow will checkout the Pull Request HEAD branch containing untrusted code since an attacker will be able to control its contents:
- name: Checkout Source Code
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
persist-credentials: false
# checkout ref not SHA so we can get useful branch names (see previous comments)
ref: ${{ steps.get-ref.outputs.ref }}
# required for git describe
fetch-depth: 0
Right after checking out the untrusted code into the workflow’s workspace, it will execute the ./contrib/scripts/print-chart-version.sh
script which may contain attacker controlled code.
- name: Get version
id: get-version
run: |
set -o pipefail
set -e
if [[ -f ./contrib/scripts/print-chart-version.sh ]]; then
echo "chart_version=$(./contrib/scripts/print-chart-version.sh)" | tee -a $GITHUB_OUTPUT
else
echo "./contrib/scripts/print-chart-version.sh missing. Perhaps it needs to be backported to your target branch?"
exit 1
fi
Steps to Reproduce
- Fork the project and create a Pull Request which modifies two files:
./contrib/scripts/print-chart-version.sh
should contain the code to be executed (eg:echo $(whoami)
)..github/workflows/build-images-ci.yaml
should be modified to run on a pull request and finish successfully:
name: Image CI Build
on:
pull_request:
jobs:
ok:
runs-on: ubuntu-latest
steps:
- run: echo "Done"
- Submit the PR which should trigger the
Image CI Build
workflow first and upon finalization, it should trigger the vulnerableChart CI Push
workflow.
Impact
The workflow runs with read only permissions, however, it reads the QUAY_CHARTS_DEV_PASSWORD
and QUAY_CHARTS_DEV_USERNAME
secrets and therefore the attacker-controlled code will be able to dump the runner’s memory and exfiltrate these secrets.
Resources
Issue 2: Cache Poisoning (GHSL-2024-227
)
The same code execution described above will allow an attacker to write to the cache in the context of the default branch (since the job is triggered by a workflow_run
event). The cache of the default branch is queried by any other branch and therefore, being able to write to this cache will allow an attacker to control the files restored by other workflows.
Impact
This issue may lead to privilege escalation allowing an attacker to move laterally to other more privileged 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-226
or GHSL-2024-227
in any communication regarding these issues.