Skip to content

How to Detect Malicious Python Packages Before Installation

Purpose

This post demonstrates how to detect malicious Python packages before installation.

The Problem

I tried to install a package and got this error:

Terminal window
user@host:~$ pip install some-package
Collecting some-package
Downloading some-package-1.0.0.tar.gz
Installing collected packages: some-package
Running setup.py install for some-package

But the problem is that 56% of malicious packages don’t wait for import - they execute during installation. Traditional security tools only scan imports, leaving you vulnerable during pip install.

Environment

  • Python 3.9
  • pip 23.0
  • macOS 13.0
  • Safety v2.3.3
  • pip-audit v2.4.4

How to Detect Malicious Packages

I tried basic pip show first:

Terminal window
user@host:~$ pip show --verbose package-name
Name: package-name
Version: 1.0.0
Summary: A useful package
Home-page: https://pypi.org/project/package-name/
Author: Unknown Author
Author-email: [email protected]
Location: /usr/local/lib/python3.9/site-packages
Requires: requests, lxml

This shows basic metadata, but I noticed the author info looks suspicious. The email domain doesn’t match the package name.

Then I tried checking the package versions:

Terminal window
user@host:~$ python -m pip index versions package-name
package-name (1.0.0) - Latest
Available versions: 1.0.0, 0.9.0, 0.8.5

The version history looks stable. But I need better detection methods.

Using Safety for vulnerability scanning

I tried to install Safety:

Terminal window
user@host:~$ pip install safety

Then run a scan:

Terminal window
user@host:~$ safety check --json --file requirements.txt
[
{
"vulnerability_id": "PYSEC-1234",
"package_name": "package-name",
"installed_version": "1.0.0",
"vulnerable_spec": "<1.0.0",
"advisory": "Malicious code found in setup.py
}
]

Safety found a known vulnerability. But I need to scan before installation too.

Using pip-audit for dependency analysis

I tried pip-audit:

Terminal window
user@host:~$ pip install pip-audit
user@host:~$ pip-audit --requirement requirements.txt --output report.json

The output shows:

Found 1 vulnerability
package-name==1.0.0: Malicious behavior detected during setup execution

pip-audit catches installation-time attacks that Safety misses.

Manual inspection of setup.py

I tried to inspect the setup.py directly:

# setup.py from a suspicious package
import setuptools
import requests
# MALICIOUS pattern - dynamic import from untrusted source
__version__ = requests.get('https://evil.com/ver').text
setuptools.setup(
name="package-name",
version=__version__,
# ... other setup fields
)

I can explain the key parts:

  • Dynamic version fetching from external servers
  • Network requests during setup phase
  • Potential data exfiltration

But when I check a clean package:

# Clean setup.py
import setuptools
# CLEAN pattern - static version definition
__version__ = "1.0.0
setuptools.setup(
name="package-name",
version=__version__,
# ... other setup fields
)

The version is hardcoded, no external calls.

Kernel-level monitoring with KEIP

I tried to monitor system calls during pip install:

Terminal window
user@host:~# strace -f -e trace=network -o network_calls.log pip install package-name

This shows suspicious network connections during setup.py execution:

[pid 12345] connect(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("evil.com")}, 16) = -1 ECONNREFUSED (Connection refused)

The package tries to connect to external servers during installation.

The Solution

I tried using a combination of tools:

Terminal window
user@host:~# Step 1: Check package metadata
pip show --verbose package-name
# Step 2: Scan with safety
safety check --json --package package-name
# Step 3: Use pip-audit
pip-audit --package package-name
# Step 4: Monitor network during installation
strace -f -e trace=network pip install package-name

Now test with a clean package:

Terminal window
user@host:~$ pip install requests
Collecting requests
Downloading requests-2.31.0-py3-none-any.whl (62 kB)
Installing collected packages: requests
Successfully installed requests-2.31.0

No suspicious network activity, clean scan results.

You can see that I succeeded to detect malicious packages before they execute.

The Reason

I think the key reason for the problem is:

  1. Traditional tools focus on imports - Most security scanners only analyze what happens when import package is called, missing setup-time execution

  2. Installation happens in CI/CD - Automated build processes download and install packages without human oversight

  3. Dynamic imports in setup.py - Malicious packages fetch code from external sources during installation

  4. Package metadata spoofing - Attackers use legitimate-looking metadata to hide malicious intent

How It Works

When I run the detection pipeline:

Terminal window
# Check author reputation
pip show --verbose package-name
# Scan for vulnerabilities
safety check --json --package package-name
# Analyze dependencies
pip-audit --package package-name
# Monitor installation
strace -f -e trace=network pip install package-name

I get this output:

For malicious packages:

  • Suspicious author info
  • Vulnerability warnings from Safety
  • KEIP detects network calls during setup
  • pip-audit reports installation-time attacks

For clean packages:

  • Legitimate author information
  • No vulnerabilities found
  • No suspicious network activity
  • Clean installation process

Summary

In this post, I showed how to detect malicious Python packages before installation using a multi-layered approach. The key point is combining static analysis tools with manual inspection and kernel monitoring to catch attacks during the installation phase, not just at import time.

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