Summary
Workflow runs with full write permissions, check-types.yml unsafely checks out attacker's submitted code, and then runs it.
Details
PoC
- Create a private repository containing the contents of cal.com
- 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)
- Create a branch
- 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
- Push the branch to your private repository
- 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.
Summary
Workflow runs with full write permissions, check-types.yml unsafely checks out attacker's submitted code, and then runs it.
Details
PoC
cal.com/.github/workflows/check-types.yml
Line 8 in 9aa60fa
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)cal.com/package.json
Line 73 in 9aa60fa
touch a; git add a; git config user.name user; git config user.email hello@example.com; git push origin HEAD:main
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...
cal.com/.github/workflows/pr.yml
Lines 4 to 6 in 9aa60fa
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).
cal.com/.github/workflows/pr.yml
Lines 18 to 21 in 9aa60fa
cal.com/.github/workflows/check-types.yml
Line 10 in 9aa60fa
performs a safe checkout because checkout defaults to being cautious
cal.com/.github/workflows/check-types.yml
Line 11 in 9aa60fa
cal.com/.github/actions/dangerous-git-checkout/action.yml
Lines 6 to 10 in 9aa60fa
now you've checked out the attacker's code.
cal.com/.github/workflows/check-types.yml
Line 12 in 9aa60fa
cal.com/.github/actions/yarn-install/action.yml
Line 62 in 9aa60fa
This does a yarn install, which isn't terribly exciting.
cal.com/.github/workflows/check-types.yml
Line 19 in 9aa60fa
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.