=================================================
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.
Mitigation
==========
This vulnerability was fixed in https://github.com/pypa/warehouse/pull/9846 via
https://github.com/pypa/warehouse/pull/9846/commits/fb98c6bb4d68fb43944171214971f6c776f844ce
and
https://github.com/pypa/warehouse/pull/9846/commits/50bd16422889d653127d373c9615516bf883a394
by matching against the PR creator username and not using an unecessary
``echo``.
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:
* https://github.com/pypa/warehouse/pull/7275, created by a PyPI administrator
* https://github.com/pypa/warehouse/pull/6916, a drive-by PR from an unfamiliar
user
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
`_