In an environment where an untrusted or less trusted entity can modify the environment variables, consider validating hash-based byte code [Python 2023 Command line and environment].
Python source code .py
files need to be converted into “byte code” .pyc
or .pyo
in memory or in a filesystem __pycache__
before running on the Python Virtual Machine (PVM) [Dec 2009 PEP 3147].
Python 3.8 [Dec 2009 PEP 3147] also has a backward compatibility mode supporting delivering only byte code.
Python 3.8 introduced the option to customize the __pycache__
folder via -X pycache_prefix=PATH
, or the PYTHONPYCACHEPREFIX
environment variable. An attacker may manipulate the PYTHONPYCACHEPREFIX
or PYTHONPATH
to inject their code that can go unnoticed without hash-based verification. Without --check-hash-based-pycs
Python only compares the byte code against the source code via a timestamp [Python 2023 The import system], potentially allowing an attack.
Python 2.6 also introduced the ability to stop Python from writing “byte code” files via the -B
flag or PYTHONDONTWRITEBYTECODE=1
environment variable. However, this does not guarantee full protection.
Byte code files contain a 32-bit ‘magic number’ to identify the byte code format to determine if the PVM matches. Byte code also uses a naming convention to match up the CPython interpreter down to its minor version, such as sessions.cpython-39.pyc
for the sessions module compiled with CPython 3.9.
Setting --check-hash-based-pycs
to default
or never
skips integrity verification of the byte code against its source code and only compares timestamp and size.
The following noncompliant01.bash
code uses the Python standard library http.server
as an example of a Python process started from a bash script without hash-based verification:
# Non-compliant Code Example
python3 -m http.server -b 127.0.0.42 8080
An attacker can exploit this by manipulating the PYTHONPATH
to inject their code that can go unnoticed without hash-based verification as shown in the following example:
cd
CWD=$(pwd)
mkdir -p temp/http
touch temp/http/__init__.py
echo "print('hello there')" > temp/http/server.py
export PYTHONPATH=$CWD/temp/
# and now launch again
python3 -m http.server -b 127.0.0.42 8080
The http.server
module is now launched from the PYTHONPATH
and only prints “hello there” instead of launching the web server.
In the following compliant solution, a user custom PYTHONPATH
is suppressed with the -I
isolation flag. This isolates the environment to avoid malicious code injection via PYTHONPATH
. Additionally, using --check-hash-based-pycs always
enforces hash-based integrity verification of byte code files against their source code files.
compliant01.bash:
# Compliant Code Example
python3 -I --check-hash-based-pycs always -m http.server -b 127.0.0.42 8080
ENV-4P-EX0: Untrusted entities are not able to change environmental variables or any Python files.
Currently None.
Component | CVE | Description | CVSS rating | Comment |
---|---|---|---|---|
python-dbusmock <=0.15.1 | CVE-2015-1326 | AddTemplate() D-Bus method call or DBusTestCase.spawn_server_template() method could be tricked into executing malicious code if an attacker supplies a .pyc file. | 3.x: 8.8 High | |
catfish <= 0.6 | CVE-2014-2095 | Fedora package such as 0.8.2-1 is not used, allowing local users to gain privileges via a Trojan horse bin/catfish.pyc under the current working directory. | 2.0: 4.6 Med | |
catfish <= 0.4.0.3 | CVE-2014-2094 | Local users can gain privileges via a Trojan horse catfish.pyc in the current working directory. | 2.0: 4.6 Med |