Skip to content

Repository takeover via `.github/workflows/pr.yml`

Critical
keithwillcode published GHSA-p3f6-52gv-cj7m Apr 8, 2024

Package

No package listed

Affected versions

main

Patched versions

None

Description

Summary

Workflow runs with full write permissions, check-types.yml unsafely checks out attacker's submitted code, and then runs it.

Details

PoC

  1. Create a private repository containing the contents of cal.com
  2. Change
    runs-on: buildjet-4vcpu-ubuntu-2204
    to ubuntu-latest (or you could wire up a custom runner if you want to give GitHub more money, that's a bad use of money)
  3. Create a branch
  4. Change
    "type-check:ci": "turbo run type-check:ci --log-prefix=none",
    to run something exciting touch a; git add a; git config user.name user; git config user.email hello@example.com; git push origin HEAD:main
  5. Push the branch to your private repository
  6. Create a PR from your private repository to your private repository's main branch

Note that it isn't possible to safely test this in public but there is nothing in the code flow that's blocking a fork from doing this as the pull_request_target has removed the permissions block.

So, what's happening...

pull_request_target:
branches:
- main

Selects the target and default permissions (this repository defaults to write, you could set it to read only, but that wouldn't do what you want).

type-check:
name: Type check
uses: ./.github/workflows/check-types.yml
secrets: inherit
passes permissions down.
- uses: actions/checkout@v3

performs a safe checkout because checkout defaults to being cautious

- uses: ./.github/actions/dangerous-git-checkout
is as it says, dangerous.

- name: Checkout repo
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 2

now you've checked out the attacker's code.

- uses: ./.github/actions/yarn-install

yarn install --inline-builds

This does a yarn install, which isn't terribly exciting.

- run: yarn type-check:ci

now we run the attacker's command.

Impact

The repository's contents are compromised.

The GITHUB_TOKEN can merge PRs, mutate them, add / delete comments, push commits to the repository, delete branches from the repository, force-push over branches in the repository.

Unless you have lots of other machinery guarding against things, which seems unlikely (and the github token can often undo that machinery).


Note that it's likely there are other workflows that can also be attacked.

In general, you're going to want to split your workflows into multiple jobs, some with contents:read (the stuff that runs untrusted content), and others w/ and use an artifact or similar to transfer data between them so that you control what data is processed by the side with write permissions.

Severity

Critical

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
Low
User interaction
None
Scope
Changed
Confidentiality
Low
Integrity
High
Availability
High

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:H/A:H

CVE ID

No known CVE

Weaknesses

Credits