Description
Versions of pip since 24.1b1 allow someone to run arbitrary code after a specially crafted bdist whl file is installed.
When installing wheel files pip does not constrain the directories the wheel contents are written into, except for checks that ensure traversal is only within the destination directories (e.g, purelib, platlib, data, etc) (see #4625)
This means a wheel is able to place files into existing modules that belong to other packages, such as pip, setuptools, etc.
If the installer lazily imports a module after the wheel is installed it is possible for the wheel to overwrite the module with its own code, which is then imported unintentionally by the installer.
For pip, this has been true since 24.1b1 when a change was introduced that dynamically loads the pip._internal.self_outdated_check module after running a command to check if pip needs upgrading.
Because this module is loaded after a package has been installed, a wheel can overwrite {purelib}/pip/_internal/self_outdated_check.py and have the code within it automatically executed when pip install {wheel} is run.
Expected behavior
This behavior is surprising. My understanding is that most Python users expect wheels can't run code during installation.
For example, the recent blog post on command jacking demonstrates this expectation:
Python wheels (.whl files) have become increasingly prevalent due to their performance benefits in package installation. However, they present a unique challenge for attackers
While both .tar.gz and .whl files may contain a setup.py file, .whl files don’t execute setup.py during installation. This characteristic has traditionally made it more difficult for attackers to achieve arbitrary code execution during the installation process when using .whl files.
That said, the wheel spec says nothing about security, or avoiding on-install code execution.
pip version
24.1b1
Python version
v3.11 later
OS
any
How to Reproduce
- Download wheelofdespair-0.0.1-py3-none-any.zip
mv wheelofdespair-0.0.1-py3-none-any.zip wheelofdespair-0.0.1-py3-none-any.whl
python3 -m venv env
. env/bin/activate
pip install --upgrade pip
pip install wheelofdespair-0.0.1-py3-none-any.whl
Output
Collecting wheelofdespair
Downloading wheelofdespair-0.0.1-py3-none-any.whl.metadata (201 bytes)
Downloading wheelofdespair-0.0.1-py3-none-any.whl (1.5 kB)
Installing collected packages: wheelofdespair
Successfully installed wheelofdespair-0.0.1
PoC: Wheel-of-Despair code execution.
Code of Conduct
Description
Versions of pip since 24.1b1 allow someone to run arbitrary code after a specially crafted bdist whl file is installed.
When installing wheel files pip does not constrain the directories the wheel contents are written into, except for checks that ensure traversal is only within the destination directories (e.g, purelib, platlib, data, etc) (see #4625)
This means a wheel is able to place files into existing modules that belong to other packages, such as pip, setuptools, etc.
If the installer lazily imports a module after the wheel is installed it is possible for the wheel to overwrite the module with its own code, which is then imported unintentionally by the installer.
For pip, this has been true since 24.1b1 when a change was introduced that dynamically loads the
pip._internal.self_outdated_checkmodule after running a command to check if pip needs upgrading.Because this module is loaded after a package has been installed, a wheel can overwrite
{purelib}/pip/_internal/self_outdated_check.pyand have the code within it automatically executed whenpip install {wheel}is run.Expected behavior
This behavior is surprising. My understanding is that most Python users expect wheels can't run code during installation.
For example, the recent blog post on command jacking demonstrates this expectation:
That said, the wheel spec says nothing about security, or avoiding on-install code execution.
pip version
24.1b1
Python version
v3.11 later
OS
any
How to Reproduce
mv wheelofdespair-0.0.1-py3-none-any.zip wheelofdespair-0.0.1-py3-none-any.whlpython3 -m venv env. env/bin/activatepip install --upgrade pippip install wheelofdespair-0.0.1-py3-none-any.whlOutput
Code of Conduct