Vulnerability in GitHub Actions workflow for PyPI

An exploitable vulnerability in a GitHub Actions workflow for PyPI’s source repository could allow an attacker to obtain write permissions against the pypa/warehouse repository.

  • Disclosure date: 2021-07-27 (Reported via security policy on pypi.org)
  • Disclosed by: RyotaK
  • Bounty awarded to discloser: $1,000 USD for multiple reports in 2021-07

Summary

The PyPI team uses Dependabot for automatic updates to the dependencies of the web application that powers PyPI. This tool generates a high volume of pull requests against the source repository, and lacks a feature to group these updates into a single pull request

To reduce the burden of merging multiple individual pull requests, the maintainers use an open-source GitHub Action workflow to group all Dependabot pull requests.

To quote the security researcher:

As combine-prs.yml will pick up pull requests that have dependabot as a prefix in the head.ref, it’s possible to force this workflow to pick up a malicious pull request.

(As head.ref will return branch name even if it’s in the forked repository, someone may create a branch name like dependabotasdf and it’ll be picked by this workflow.)

Since branch names can contain shell metacharacters, this line would be unsafe as the ${{ }} expression is used. Because the ${{ }} expression is evaluated before commands are passed to bash, it makes this workflow vulnerable to command injection.

By combining these vulnerabilities, it’s possible to obtain write permissions against the pypa/warehouse repository by the following ways:

  1. Fork pypa/workhouse.
  2. In forked repository, create a branch named dependabot;cat$IFS$(echo$IFS'LmdpdA=='|base64$IFS'-d')/config|base64;# (This command will execute cat .git/config | base64. As actions/checkout leaves GitHub token in the .git/config file by default, it’s possible to obtain it from there.)
  3. Add harmless modification to the created branch.
  4. Create a pull request with a harmless name (e.g. “WIP”)
  5. Wait for Combine PRs to be triggered.
  6. GitHub Token with write permissions against pypa/warehouse will be leaked.

Analysis

PyPI administrators analyzed the vulnerabilty and found it to be exploitable.

Audit

A successful exploitation of the vulnerability would be identifiable via an opened pull request against the pypa/warehouse repository, with the branch name prefixed with dependabot and created by a non-Dependabot user.

The PyPI administrators analyzed all pull requests created against pypa/warehouse and found 2,874 pull requests with branches starting with dependabot. All of these branches were created by the dependabot[bot] or dependabot-preview[bot] users, with the exception of two:

The PyPI administrators analyzed the PR from the unknown user and determined that it was not attempting to exploit the vulnerabiltiy as it lacked a malicious branch name. In addition, this PR was not picked up by a run of the workflow at any point.

Timeline

  • 2020-10-12: “Combine PRs” workflow added in (PR #8694)
  • 2021-07-27: Issue reported by RyotaK following guidelines in security policy on pypi.org)
  • 2021-07-27 (+0days): Fix is implemented and deployed in commit 33ad32