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
9.9
/ 10

CVSS base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
Low
User interaction
None
Scope
Changed
Confidentiality
Low
Integrity
High
Availability
High
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